zoukankan      html  css  js  c++  java
  • Rails-Treasure chest2 嵌套表单;

    • 嵌套表单1-1
    • 嵌套表单1-多
    • 选日期时间的UI (一个jquery Plugin)
    • 拆除前后台css和js
    • Rich Editor, 显示输入的HTML tag
    • 批次编辑/删除 

    嵌套表单1-1

    核心知识点:ActiveRecord Nested Attributes

    嵌套属性的功能可以让你在关联的记录上保存属性,通过父记录。默认这个功能是关闭的。

    使用类方法 #accepts_nested_attributes_for( :小写类名 ), 开启这个功能后,会增加一个属性存入方法,

    如  profiles_attributes=(attributes)

    ⚠️::autosave被添加到关联上,当user更新, profile也自动更新。

    实例方法:

    :allow_destroy, 如果是true,任何删除record的行为,attributes_hash中会带一个_destroy key和a value("1"或true),对应的id的记录会被删除。

    情景:

    给User一个Profile,用于储存用户个人资料,1对1的关系。

    model这样设计的原因是为了节省数据库的查询量。

    单独的resource编辑UI

    1. 编辑一个嵌套的路由
      •    resources :users do
             resource :profile, :controller => "user_profiles"
           end
    2. 单独的连接,单独的edit表单, 单独的controller

    使用accepts_nested_attributes_for方法编辑嵌套表单

    1. 在User.rb上加上accepts_nested_attributes_for :profile
    2. 在users_controller.rb上,把profile_attributes加入参数白名单。
      • :profile_attributes => [:id, ...]
    3. 在users_controller.rb上,edit方法上如果@user的profile不存在则创建一个(用关联方法)
      • @profile = @user.profile || @user.create_profile
    4. 在views/users/edit.html.erb上,塞入更新profile的表格。
      • f.field_for :profile do |ff|

    方法:field_for(record_name, record_object=nil, field_options = {} , &block)

    创建一个类似form_for的和指定model对象相关的scope,但不会创建form标签。

    用于在一个form表格内添加额外的model对象。

    和form_for类似,会生成FormBuilder对象(关联着model对象)

    但参数有2个record_name和record_object用于不同的功能:

    1. record_name:  用于fields。提交的values出现在params hash中的样子。最终提交给controller。
    2. record_object(可选):what default values are shown when the form the fields appear in is first displayed。(没弄明白,但不重要,忽略)

    嵌套表单(1-to-many)

    def new
      @event = Event.new
      @event.tickets.build

    end

    在new方法中,每多加一行@event.tickets.build。在生成的表格中就多一个ticket输入框。

    admin/events/_form.html.erb

    <%= f.fields_for :tickets do |ff| %>
      <div class="form-group">
        <%= ff.label :name %>
        <%= ff.text_field :name, :class => "form-control"%>
      </div>
    <% end %>

    def edit
      if @event.tickets.empty?
        @event.tickets.build
      end
    end

    如果没有tickes则编辑时,增加一个ticket的输入框。

    如果想要在编辑页面,新增功能:

    1. 增加一个“新增票”的按钮。点击后新增ticket输入框,新增一个ticket
    2. 在每个ticket输入框下面,增加一个“删除这个票据”的按钮。点击后删除这个ticket

    需要使用javascript来实现相关功能。使用jQuery插件nested_form_fields gem

    单独的rescources UI编辑

    如果 has_many 的资料非常多、字段又多的话,就不适合用嵌套(Nested)表单 UI。


    选日期时间的 UI

    <%= f.date_select :birthday, :class => "form-control" %>

    这是Rails内建的,有点丑,但所有浏览器都能用。

    有:start_year, :end_year,等N多个选项



    拆除前后台css和js

    默认的 layout 是 app/views/layouts/application.html.erb

    而后台的 controller 透过 layout "admin" 可以改用 :

    app/views/layouts/admin.html.erb

    但是有个部分我们没有拆开,那就是 css 和 javascript, 到目前还是共享:

    app/assets/stylesheets/application.scss 和 app/assets/javascript/application.js

    随着 Plugins 功能越装越多,有很多 css/js 前后台是不一样的,前台目前没有用到,但是因为前后台共享的关系,还是被用户下载了。

    这部分的 css/js 也可以前后台拆开,除了可以让前台下载更有效率,也可以让代码分开管理。

    方法:

    1. 

    新增 app/assets/stylesheets/admin.scss

    新增 app/assets/javascripts/admin.js

    2. 修改 config/initializers/assets.rb

    config/initializers/assets.rb

    - # Rails.application.config.assets.precompile += %w( search.js )
    
    + Rails.application.config.assets.precompile += %w( admin.css admin.js )

    3. 重启服务器后,更改admin.html.erb文件的<head> 为:

     <%= stylesheet_link_tag    'admin', media: 'all', 'data-turbolinks-track': 'reload' %>
     <%= javascript_include_tag 'admin', 'data-turbolinks-track': 'reload' %>
    

    4. 挪动插件即可。



    Rich Editor, 显示输入的HTML tag

    simple_format方法的作用是换行,包裹一个<p>

    去掉simple_format,  HTML 会完全被脱逸(escaped)了,例如 < 会变成 &lt;。这是 Rails 默认的安全机制。

    如果使用 raw 或者 html_safe的话,如果是管理员后台操作倒没有问题,但前台用户使用的话容易被xss跨脚本攻击。

    最好使用

    sanitize 方法,白名单过滤sanitize(html, options = {})

    可以设置白名单,单一定要全面:

    tags: ["table", "tr", "td"]

    attributes: ["border"]

    或者在config/application.rb中设置

    config.action_view.sanitized_allowed_tags = Rails::Html::WhiteListSanitizer.allowed_tags + %w(table tr td) 
    config.action_view.sanitized_allowed_attributes = Rails::Html::WhiteListSanitizer.allowed_attributes + %w(style border)

    Rich Edit  ckeditor

     编辑器:https://github.com/galetahub/ckeditor (Rails 5.1x, integration)

    不是很流行的编辑器。

     安装:

    gem 'ckeditor', github: 'galetahub/ckeditor'

     初始化:config/initializers/assets.rb

    Rails.application.config.assets.precompile += %w( ckeditor/*)

     使用:

    从gem上加载editor: app/assets/javascripts/application.js:

    //= require ckeditor/init

    ⚠️在//= require_tree.前面。

    FormHelpers:

    = form_for @page do |form|
      = form.cktext_area :notes, ckeditor: { language: 'uk'}
      = form.cktext_area :content, value: 'Default value', id: 'sometext'
      = cktext_area :page, :info, cols: 40, ckeditor: { uiColor: '#AADC6E', toolbar: 'mini' }

    toolbar可以客制化。具体其他的设置见git。



    批量删除

    1.  设置routes。使用collection { post :bulk_update} 
      • 如果routes使用delete。则对应的form_tag必须明确指定method: :delete。
      • 如果要用同一个
    2. 在index.html.erb上, 给table增加form_tag(),  check_box_tag(),和subimt_tag()。
    3. 增加<script> javascript。
    4. 修改controller, 增加bulk_update方法,使用Array[params[:ids]]循环遍历event,并删除。

    重点:

    第2步:请求参数的设置

    <th><%= check_box_tag("全选", "1", false, id:"toggle_all" )%></th>

    <td><%= check_box_tag("ids[]", event.id, false)%></td>

    ⚠️:

    必须使用"ids[]",这样可以得到属性name="ids[]"

    提交的时候,生成参数: "全选"=>"1", "ids"=>["22", "24",...] ,参数是由name/value对儿组成的。

    因为如果name是'ids' ,这是一个string,提交生成的参数只会取完全相同的名字的第一个value,生成 "ids"=>"26",  造成批量删除功能失败。

    第三步:用到增加和移除属性的method: setAttribute(), removeAttribute(),

    也使用了querySelectorAll()获得一个数组的checkbox tag,然后循环forEach()

    document.getElementById("toggle_all").addEventListener("click", function(){
     if (this.hasAttribute("checked") == false ) {
      this.setAttribute("checked", "checked");
        document.querySelectorAll("input[name='ids[]']").forEach(function(e){
        e.setAttribute("checked", "checked");
      })
     } else {
      this.removeAttribute("checked");
        document.querySelectorAll("input[name='ids[]']").forEach(function(e){
       e.removeAttribute("checked");
     })

    })})


    批次修改:

    一个form的url是固定的,也只能绑定一个action, ,同时form不能嵌套,如果想要多个批量功能,怎么办?

    上节使用form_tag(bulk_update_admin_events_path),因此地址指向确定。HTML方法是post。

    办法:

    提交按钮<input type="submit" name="commit", value="XXX">name/value属性对儿,在request params中会出现,如"commit"=>"批次删除"。

    因此不同按钮使用不同的name,即可区别不同的功能要求,然后在controller的action中使用if进行判断。

    def bulk_update
     total = 0
     if params[:commit] == I18n.t(:bulk_update) && params[:ids]
      Array(params[:ids]).each do |event_id|
       event = Event.find(event_id)
       event.status = params[:status]
       event.save
       total += 1
      end
      flash[:notice] = "批量变更状态为#{params[:status]}"
     elsif params[:commit] == I18n.t(:bulk_delete) && params[:ids]
      Array(params[:ids]).each do |event_id|
       event = Event.find(event_id)
       event.destroy
       total += 1
      end
      flash[:alert] = "成功删除#{total}笔"
     end

       redirect_to admin_events_path
    end

    ⚠️: I18n.t(:bulk_delete)加上的话,需要在en.yml和zn-CH.yml上加上对应的翻译。

  • 相关阅读:
    Swizzle在OC问题排查中的应用
    MacOS中系统提供的音频单元
    Mac catalyst 使用iOS-AudioUnit的音频采集、播放
    删除单向链表中的某一个节点
    C语言的的free和c++的delete的区别
    Mac下使用源码编译安装TensorFlow CPU版本
    ROC曲线与AUC值
    Linux中如何产生core文件?
    更改Linux默认栈空间的大小
    互信息(Mutual Information)
  • 原文地址:https://www.cnblogs.com/chentianwei/p/9424995.html
Copyright © 2011-2022 走看看