我的git代码:https://github.com/chentianwei411/nested_form-Stimulus-

Stimulus:     https://www.cnblogs.com/chentianwei/p/9806875.html

开始:

rails new -m ../jumpstart,\ Gorails视频\(创建一个rails模版\)/template.rb -d postgresql nested_forms

rails webpacker:install:stimulus

在_header.html.erb内添加:

使用javascript_pack_tag方法添加JS pack到Rails views

<%= javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' %>

然后修改文件名:

mv app/javascript/controllers/{hello.nested_form}_controller.js

创建手脚架和模型:

rails g scaffold Project name description

rails g model Task description project:belongs_to

rails db:migrate

产生:

  create_table "projects", force: :cascade do |t|
t.string "name"
t.string "description"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end create_table "tasks", force: :cascade do |t|
t.string "description"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.bigint "project_id"
t.index ["project_id"], name: "index_tasks_on_project_id"
end

嵌套结构的类方法使用:

class Project < ApplicationRecord
has_many :tasks, inverse_of: :project
accepts_nested_attributes_for :tasks, reject_if: :all_blank, allow_destroy: true
end

具体解释见博客:

ActiveRecord Nested Atrributes 关联记录,对嵌套属性进行CURD
Rails-Treasure chest2 嵌套表单;

controller内添加属性白名单:

    def project_params
params.require(:project).permit(:name, :description, tasks_attributes: [:id, :description, :_destroy])
end

然后在view视图_form.html.erb上添加一个嵌套的form builder:

  <h4>Tasks</h4>

  <%= form.fields_for :tasks do |task| %>
<div class="form-group">
<%= task.label :description %>
<%= task.text_field :description, class: 'form-control'%>
</div>
<% end %>

使用#fields_for(record_name, record_object = nil, &block),创建一个scope在一个指定的model对象如form_for,但是不创建自身的form tags。它用于在一个form内创建额外的model对象。(具体见api文档)

修改project_controller,添加语句@project.tasks.new。或者设置视图中的#fields_for方法的第2个参数record_object为Task.new

  def new
@project = Project.new
@project.tasks.new
end

⚠️:重构视图,可以把fields_for方法的块block提取出来。_nest.html.erb

使用template标签,让JavaScript决定是否显示在页面。配合使用stimulus.js。

  <template>
<%= form.fields_for :tasks, Task.new, child_index: 'New_RECORD' do |task| %>
<%= render 'nest', form: task%>
<% end %>
</template>

child_index: 'New_RECORD' 即子索引的名字,会在label,input标签的for,name,  id属性上使用到:

<div class="form-group">
<label for="project_tasks_attributes_New_RECORD_description">Description</label>
<input class="form-control" type="text" name="project[tasks_attributes][New_RECORD][description]" id="project_tasks_attributes_New_RECORD_description">
</div>

视图的最终代码:

  • data-controller调用js文件中的类对象实例化的对象
  • data-target, 用于取对应元素。
  • data-action,用于绑定事件。这里使用了link_to视图方法。
  <div data-controller="nested-form">
<template data-target="nested-form.template">
<%= form.fields_for :tasks, Task.new, child_index: 'New_RECORD' do |task| %>
<%= render 'nest', form: task%>
<% end %>
</template> <%= form.fields_for :tasks do |task| %>
<%= render 'nest', form: task%>
<% end %> <div class="mb-3" data-target='nested-form.links'>
<%= link_to 'Add Task', '#', class: 'btn btn-outline-primary', data: {action: 'click->nested-form#add_association'} %>
</div>
</div>

nested_form_controller.rb

export default class extends Controller {
static targets = [ "links", "template" ] connect() {
}
add_association(event) {
event.preventDefault() var content = this.templateTarget.innerHTML.replace(/New_RECORD/g, new Date().getTime())
this.linksTargets.insertAdjacentHTML('beforebegin', content)
}
} ⚠️这里用到js库的2个方法replace和insertAdjacentHTML用于对元素内容操作和元素节点的插入。
视图上的最终代码:
<input class="form-control" type="text" name="project[tasks_attributes][1554196211709][description]" id="project_tasks_attributes_1554196211709_description">

功能:增加task,还可以删除task。

在_nest.html.erb表格内增加一个"Remove"连接按钮。

<%= content_tag :div, class:'nested-fields', data: {new_record: form.object.new_record?}  do %>
<div class="form-group">
<%= form.label :description %>
<%= form.text_field :description, class: 'form-control'%>
<small><%= link_to "REMOVE", "#", data: {action: "click->nested-form#remove_association"}%></small>
<!-- <%= form.hidden_field :_destroy%> -->
</div>
<% end %>

data-new-record用于判断是不是新建数据。

添加一个data-action绑定事件remove_association。

再看nested_form_controller.js,添加事件:

  remove_association(event) {
event.preventDefault() let wrapper = event.target.closest(".nested-fields") #有nest-fileds类的元素
if (wrapper.dataset.newRecord == "true") {
wrapper.remove()
} else {
console.log("不能删除")
}
}

注意:

dataset属性提供了读写所有客制化的data属性 data-*

考虑到edit界面,有已经添加的task和新增的task,所以要区分,新增的直接从nom树移除,已经添加的则要发出删除请求。

修改上面的代码:

_nest.html.erb内添加一个input标签,并隐藏。它的用途是标记作用!

在js中添加2行代码:

  1. 找到这个input标签,并设置value等于1或者true, 这样更新时,会自动判断是否删除。
  2. 从节点树上隐藏这个元素。CSS#display属性设置none。
  remove_association(event) {
event.preventDefault() let wrapper = event.target.closest(".nested-fields")
if (wrapper.dataset.newRecord == "true") {
wrapper.remove()
} else {
wrapper.querySelector("input[name*='_destroy']").value = true
wrapper.style.display = 'none'
}
}

最新文章

  1. selenium python 安装
  2. sql语句判断默认值为getdate()的约束是否存在
  3. Window 命令
  4. pro*c调用过程
  5. apache开源项目--HBase
  6. linux 中的vim的配置文件的位置
  7. libthrift0.9.0解析(三)之TProtocol&amp;TTransport
  8. GitHub托管BootStrap资源汇总
  9. javascript 获取滚动条高度+常用js页面宽度与高度(转)
  10. 第一章 andriod studio 安装与环境搭建
  11. sql 查询所有数据库、表名、表字段总结
  12. Hibernate_10_继承的例子_单表
  13. CodeForces 645B Mischievous Mess Makers
  14. .bat批处理命令的介绍
  15. selenium 执行js,实现滚动条
  16. 分析业务模型-类图(Class Diagram)
  17. form表单总结
  18. 自学Python5.3-类和对象的简单操作
  19. php框架:Flight 简介
  20. The Little Prince-12/03

热门文章

  1. 捕鱼达人Demo版下载
  2. 数据迁移时 提示 No changes detected
  3. 22 pycharm如何将一段代码同时向左缩进一个tab键
  4. 功能比较全的StackExchange.Redis封装帮助类(.Net/C#)
  5. 【编程基础】C语言常见宏定义
  6. 实验1 C语言开发环境使用和数据类型,运算符,表达式
  7. JavaScript之事件的绑定与移除
  8. 1.5:Unity Render Pipeline
  9. BZOJ5279: [Usaco2018 Open]Disruption
  10. MySQL 常用30种SQL查询语句优化方法