StuQ Koa在线课程

庖丁解views

我们使用

rails g scaffold user name:string password:string

tree blog/app/views 
blog/app/views
├── layouts
│   └── application.html.erb
└── users
    ├── _form.html.erb
    ├── edit.html.erb
    ├── index.html.erb
    ├── index.json.jbuilder
    ├── new.html.erb
    ├── show.html.erb
    └── show.json.jbuilder

2 directories, 8 files

去掉jbuilder,api文件,和视图无关

tree blog/app/views 
blog/app/views
├── layouts
│   └── application.html.erb
└── users
    ├── _form.html.erb
    ├── edit.html.erb
    ├── index.html.erb
    ├── new.html.erb
    ├── show.html.erb

2 directories, 8 files

总结一下里面的特点

  • index是显示列表
  • new和edit都复用_form
  • show显示出所有即可

我们想一下jade是否也可以呢?

jade特性

  • block和extend是布局
  • include是包含,可以实现引用_form类似的
  • foreach类的也支持

所以jade实现erb的功能毫无问题

实现列表

index.html.erb

<h1>Listing users</h1>

<table>
  <thead>
    <tr>
      <th>Name</th>
      <th>Password</th>
      <th colspan="3"></th>
    </tr>
  </thead>

  <tbody>
    <% @users.each do |user| %>
      <tr>
        <td><%= user.name %></td>
        <td><%= user.password %></td>
        <td><%= link_to 'Show', user %></td>
        <td><%= link_to 'Edit', edit_user_path(user) %></td>
        <td><%= link_to 'Destroy', user, method: :delete, data: { confirm: 'Are you sure?' } %></td>
      </tr>
    <% end %>
  </tbody>
</table>

<br>

<%= link_to 'New User', new_user_path %>

转成jade就无比简单了

extends ../layouts/layout

block content
  h1 Listing orders

  table
    thead
      tr
        each n in ['name','password']
          th #{n}
        th(colspan="3")
    tbody
      each order in orders
        tr
          each n in ['order.name','order.password']
            td #{ eval(n) }
          td 
            a(href='/orders/#{ order._id}') Show
          td 
            a(href='/orders/#{ order._id}/edit') Edit
          td 
            a(onclick="click_del('/orders/#{ order._id}/')") Delete

  br

  p 
    a(href='/orders/new') New Order
  • erb里使用 @users
  • 而jade里使用['name','password']

效果是一样的。

实现创建和编辑

那么创建呢?

new.jade

extends ../layouts/layout

block content
  h1 New order

  include order

  a(href='/orders') Back

其核心在include order的order.jade里

这里的order用_form也不是不可以,不过没啥大劲

jade里还有一个特性,include文件,可以把上下文里的文件同名对象也传进去。也就是我include 的order,如果上下文里有的order,的order.jade里。

即减少代码转换,又精简代码,何乐而不为呢?

创建和更新都是类似,我们先看一下更新的edit.jade

extends ../layouts/layout

block content
  h1 Editing order

  include order

  a(href='/orders/#{ order._id}') Show
  span |
  a(href='/orders') Back

和创建基本无大差别,文字和url稍有变化而已。

看一下order.jade

- var _action = order._action == 'edit' ? '#' : '/orders/'
- var _method = order._action == 'edit' ? ""  : "post"
- var _type   = order._action == 'edit' ? "button"  : "submit"
- var onClick  = order._action == 'edit' ?  "click_edit('order-" + order._action + "-form','/orders/" + order._id + "/')" : ""
form(id='order-#{ order._action}-form',action="#{_action}", method="#{_method}",role='form')
  each n in ['order.name','order.password']
    - m = eval(n);
    div(class="field")
      label #{n.split('.')[1]} #{m}
      br
      input(type='text',name="#{n.split('.')[1]}" ,value="#{ m == undefined ? '' : m }")

  div(class="actions") 
    input(type='#{_type}',value='Submit',onClick='#{onClick}')

这个代码有点丑陋,为了兼容创建和更新,所以定义了几个变量的。

说明

  • form表单处理请求,form的action约定了
  • 提交分2种
    • 创建,就直接post提交
    • 更新交给了onClick变量对应的click_edit方法(位于public/javascripts/app.js)

看一下这个前端js吧

function click_edit(id, url){
  // $('#' + id).attr('action','#');
  console.log(url);

  if (!confirm("确认要更新?")) {
    return window.event.returnValue = false;
  }

  var form = document.querySelector('form')
  var data = form2obj(form);
  console.log(data);

  // return false;
  $.ajax({
    type: "PATCH",
    url: url,
    data : data
  })
  .done(function( res ) {
    if(res.status.code == 0){
      // alert( "Data delete: success " + res.status.msg );
      window.location.href= res.data.redirect;
    }else{
      alert( "Data delete fail: " + res.status.msg );
    }
  });

  return false;
}

简单的更新,使用PATCH方法而已。这里有一个知识点

var data = form2obj(form);

把表单里的内容转换成ajax里的传值对象,算是个tips吧(具体见public/javascrips/form2obj.js)。

实现删除

首先index.jade里有删除功能

  td 
    a(onclick="click_del('/orders/#{ order._id}/')") Delete

根据rest路由,我们一般的做法

 DELETE /users/:id(.:format)      users#destroy

也就是我delete请求到/users/:id即可

即,对应的是click_del方法(位于public/javascripts/app.js)


function click_del(url){
  if (!confirm("确认要删除?")) {
    return window.event.returnValue = false;
  }

  console.log(url);

  $.ajax({
    type: "DELETE",
    url: url
  })
  .done(function( res ) {
    if(res.status.code == 0){
      // alert( "Data delete: success " + res.status.msg );
      window.location.href= location.href;
    }else{
      alert( "Data delete fail: " + res.status.msg );
    } 
  });
}

jade版的crud

详情如下

➜  mvc git:(master) ✗ tree app/views/orders 
app/views/orders
├── edit.jade
├── index.jade
├── new.jade
├── order.jade
└── show.jade

0 directories, 5 files

jade版的依赖js

  • app.js
    • click_del删除方法
    • click_edit编辑方法
  • form2obj.js
➜  mvc git:(master) ✗ tree public           
public
├── 404.html
├── 422.html
├── 500.html
├── favicon.ico
├── images
├── javascripts
│   ├── app.js
│   └── form2obj.js
├── robots.txt
├── stylesheets
│   └── style.css
└── uploads

4 directories, 8 files

layout

最后再简单的介绍一下layout.jade

上面已知依赖

  • app.js
    • click_del删除方法
    • click_edit编辑方法
  • form2obj.js

如果我让生成的代码看不到app和form2obj的存在,生成的代码就可以复用了,是不是?

其实丢在布局文件里就可以

doctype html
html
  head
    title= title
    link(rel='stylesheet', href='/stylesheets/style.css')
    link(href="http://libs.baidu.com/bootstrap/3.0.3/css/bootstrap.min.css" rel="stylesheet")
  body
    block content
script(src="http://libs.baidu.com/jquery/1.9.0/jquery.js")
script(src="http://libs.baidu.com/bootstrap/3.0.3/js/bootstrap.min.js")
script(src='/javascripts/form2obj.js')
script(src='/javascripts/app.js')

说明

  • 引了jq和bootstrap
  • 引了app.js
  • 引了form2obj.js

简单吧?