[翻译] 165: 批量修改记录(Edit Multiple)

本文介绍了一个Rails应用中实现批量修改数据库记录的方法。通过在视图中添加复选框选择要修改的记录,并创建专门的控制器操作来更新所选记录的多个属性。

165 : 批量修改记录(Edit Multiple)

查看原版Railscast

 

译者:darkbaby123

校对:本文现在暂无校对者,如果哪位兄弟热心帮忙,可以联系蜗牛同学,或者给我发消息

 

第52集 演示了如何编辑多条数据库记录的例子。那个示例程序允许你一次选中多个任务,然后每个被选中的任务(Task )都会被设置成“完成”。

 

 

这一集中我们将对这种想法进行扩展。但这次,我们可以让用户选择更新一批记录的多个属性,而不是上一集的单个属性。

 

网上商店应用程序

 

下面显示的就是我们要改造的应用程序。目前,如果我们要修改表格的最后两条记录,就要单独编辑它们。如果要修改的记录很少,这不成问题。但如果面对成堆的记录这就很乏味了。下面我们将修改这个程序,让它可以同时修改一批记录的多个属性。

 

 

修改index视图

 

我们将在每条产品之前加上一个checkbox,这样我们可以轻易地选择要修改的产品。但在此之前,我们先把显示所有产品的table标签用form包起来。目前我们在Controller层还没有一个用来编辑多个产品的action,所以先把url地址空着,以后再来填吧。

 

<% form_tag ... do %>

 

我们没有为form指定提交方式,所以它默认是POST。虽然这个form将带我们到一个显示所有选中产品的新页面,我们似乎应该用GET,但因为我们要传递多个产品的id过去,这是GET做不到的。

 

在table的每一行中,我们要加入一个checkbox,它的值是对应的产品的 id (还要在table的标题部分加上一个空的<th> ,保证表格是对齐的)。

 

<td><%= check_box_tag "product_ids[]", product.id %></td>

 

注意check_box_tag的name,它是以一个方括号结尾的,这是所有传上去的值将会被集合成一个数组。

 

最后我们在table下面加上提交按钮。

 

<%= submit_tag "Edit Checked" %>

 

现在我们的index视图应该是这个样子:

 

<h1>Products</h1>
<% form_tag ... do %>
<table>
  <thead>
    <tr>
      <th>Name</th>
      <th>Category</th>
      <th>Price</th>
    </tr>
  </thead>
  <tbody>
    <% for product in @products %>
      <tr>
        <td><%= check_box_tag "product_ids[]", product.id %></td>
        <td><%= product.name %></td>
        <td><%= product.category.name %></td>
        <td><%= number_to_currency product.price, :unit => "&pound;" %></td>
        <td><%= link_to "Edit", edit_product_path(product) %></td>
        <td><%= link_to "Destroy", product_path(product), :confirm => "Are you sure?", :method => :delete %></td>
      </tr>
    <% end %>
  </tbody>
</table>
<%= submit_tag "Edit Checked" %>
<% end %>
<%= link_to "New Product", new_product_path %>

 

修改products控制器

 

我们的products控制器包含了有用的RESTful action。现在我们要增加两个action来编辑和更新多条记录。

 

def edit_multiple
end

def update_multiple
end

 

这两个action的用处和标准的 editupdate 差不多,但它们用来处理多条记录的编辑和更新。其中 edit_multiple 方法需要对应的视图,我们过一会儿再写它。

 

在我们的程序中,Products 是一个RESTful资源,所以我们必须修改routes文件使这两个新action可以访问。因为我们要对products集合添加方法,所以我们用 :collection 参数添加这两个方法。

 

map.resources :products, :collection => { :edit_multiple => :post, :update_multiple => :put }

 

:collection 参数需要一个hash,其中hash的键代表action的名字,值代表这个action的html method。如同上文提到的,我们采用POST作为 edit_multiple 的method,虽然理想情况下我们应该用GET。

 

现在我们的action定义好了,我们可以回到index视图,给 form_tag 填上正确的url地址

 

<% form_tag edit_multiple_products_path do %>

 

现在我们刷新index页面,就可以看到每个产品前面的checkbox,下面的“Edit Checked”按钮会带我们到edit_multiple页面。

 

 

新的form

 

如果我们选中一些产品,然后点击下面的"Edit Checked"按钮,会看到一个missing template错误。这是因为我们还没有创建视图代码,所以下一步工作就是完成它。我们将把视图代码写在 /app/views/products/edit_multiple.html.erb 文件中。但在此之前,我们先看看点击按钮后,development日志中生成的信息。

 

Processing ProductsController#edit_multiple (for 127.0.0.1 at 2009-06-06 19:24:37) [POST]
  Parameters: {"product_ids"=>["3", "4", "5"], "commit"=>"Edit Checked", "authenticity_token"=>"s5z3KEJpBM7zC2JooC/relZ2oZtVpfxL/IMklpcBuYU="}

 

在Parameters部分,我们可以看到我们选中的产品的id以数组的形式组织着。在控制器中,我们将用这些参数找到之前选中的产品。

 

def edit_multiple
  @products = Product.find(params[:product_ids])
end

 

现在转到我们的edit_multiple视图。在我们之前的程序中,我们已经有了一个放在partial中的form,它用在新建和编辑产品的页面。看起来似乎我们可以直接把这个form拿过来用。但因为它做一些修改来适应批量记录,所以我们干脆建立一个新的form。

 

在视图的开头我们先定义一个没有关闭block的 form_for (译者注:原文写的form_tag,但实际代码是form_for,所以翻译时改了一下)。因为这个form是用来更新多个产品的,我们为它的第一个参数指定一个symbol而不是实际的Product对象。我们还要为它指定 :url:method (注意是PUT,所以要单独指定)。

 

<% form_for :product, :url => update_multiple_products_path, :html => { :method => :put } do |form| %>

 

在form中我们需要设定所有选中的产品的 id s,否则form提交时,我们不知道哪些产品会被更新。我们可以用一系列的hidden_field_tag来存放产品的 id s,同时我们做一个列表来显示这些待更新的产品。

 

<ul>
  <% for product in @products %>
    <li>
      <%= h product.name %>
      <%= hidden_field_tag "product_ids[]", product.id%>
    </li>
  <% end %>
</ul>

 

下一步,我们将为form添加表单元素来表示 Product 的属性。这些属性包括产品名称、分类名称、价格、和是否停售的信息。当我们提交这个form时,我们只希望更新那些填写了具体值的属性。所以对那些有一系列选择的下拉框,我们要加一个空选项,这样用户就可以选择“空”来跳过这些属性的修改。

 

<ol class="formList">
  <li>
    <%= form.label :category_id %>
    <%= form.collection_select :category_id, Category.all, :id, :name, :include_blank => true %>
  </li>
  <li>
    <%= form.label :name %>
    <%= form.text_field :name %>
  </li>
  <li>
    <%= form.label :price %>
    <%= form.text_field :price %>
  </li>
  <li>
    <%= form.label :discontinued %>
    <%= form.select :discontinued, [["Yes", true], ["No", false]], :include_blank => true %>
  </li>
  <li>
    <%= form.submit "Submit" %>
  </li>
</ol>

 

最后,我们关闭 form_for 的block,我们的form也完成了。

 

<% end %>

 

现在如果我们刷新页面,我们会看到我们选中的产品的列表,还有下面用来修改产品信息的form。注意所有的下拉框都已经被设置成了空值,这样我们就不会去修改它们的在数据库中的值。

 

 

编写用于更新的action

 

现在我们已经几近完成了,但我们还要为 update_multiple 方法编写代码,当上图的form提交时,更新所有被选中的产品。

 

def update_multiple
  @products = Product.find(params[:product_ids])
  @products.each do |product|
    product.update_attributes!(params[:product].reject { |k,v| v.blank? })
  end
  flash[:notice] = "Updated products!"
  redirect_to products_path
end

 

update_multiple 的开始,我们用产品的 id s 数组(从form的隐藏字段中传上来的)获取了被选中的产品,然后我们遍历每个产品并进行更新。因为我们只要更新不为空的属性,所以我们用 reject 遍历每一个参数,去掉了值为空的属性。注意我们用的是带感叹号(!)的 update_attributes! 方法,因为我们没有对模型做任何校验。如果这个程序是一个实际产品,我们会去做校验,但这超出了这一集讨论的范围。使用 update_attributes! 表示如果有些数据不正确,程序将会抛出异常。一旦所有的产品都更新完成,我们设置一个flash信息然后跳转回到产品列表页面。

 

让我们看看它是否能正常工作。我们有两个产品,Video Game Console和Video Game Disc,被放在Toys & Games分类下。现在我们要把分类改成Electronics。如果我们选中这两个产品并点击“Edit Checked”按钮(译者注:原文是“Submit”按钮,但实际页面上是"Edit Checked")。我们将会看到它们被列在edit_multiple页面上。

 

 

如果我们选择从category下拉框中选择“Electronics”然后点击“Submit”按钮,我们会回到产品列表页面。

 

 

可以看到,这两个产品的分类已经改成了“Electronics”,但其他的属性都没有变化。

 

更进一步

 

目前我们已经提供了一种很有效的方式去同时修改多个模型对象。但在这一集的结尾,我们将更进一步,让价格可以按相对的数值来改变。比如,这样我们就可以让所有家具的价格降低20%。我们现在可以选中多个产品,但如果我们改变form中的价格,那么所有的产品都会变成一样的价格。如果我们利用虚拟属性,更改相对价格的逻辑就变得非常直观。虚拟属性在第16集中有介绍,如果想了解它们,你可以去 看看视频读读文章

 

我们将在 Product 模型中创建一个虚拟属性price_modification 。我们会修改 edit_multiple 视图中的form这样它会修改我们的新属性而不是直接修改 price 属性。

 

<li>
  <%= form.label :price_modification %>
  <%= form.text_field :price_modification %>
</li>

 

现在我们要在 Product 模型中设定新属性的getter和setter。

 

class Product < ActiveRecord::Base
  belongs_to :category

  def price_modification
    price
  end

  def price_modification=(new_price)
    if new_price.ends_with? "%"
      self.price += (price * (new_price.to_f / 100)).round(2)
    else
      self.price = new_price
    end
  end
end

 

getter方法很直观:我们只需要返回price就行,但setter方法稍微复杂一点。如果输入的值(new_value)以百分号结束,我们将把值转换成浮点数(new_price.to_f ),除以100然后乘以原始的价格,来求出价格改变的具体数值。然后把它和原始价格相加。如果那个具体数值是负数,价格就会降低。最后,我们用ActiveSupport扩展的 round 方法让新的价格保留小数点后两位。

 

现在,让我们开始贱卖家具。我们选中两个家具产品然后修改他们。

 

 

然后在“Price modification”一栏中,填写“-20%”。

 

 

当我们提交form并返回产品列表,我们可以看到选中的那些产品都降价了20%。

 

 

这一集中讲的技术非常有用,并且可以应用在一系列的场景中。经过这次实验,你应该可以发现在你的Rails应用的很多地方,能相对地修改属性都是非常有用的。

 

 

 

<ti-dynamic-form #tiDynamicFormComponent [model]="model" (customEvent)="customEvent($event)" > <ng-template modelId="grid-with-batch-edit-global" as="dynamicGridHeaderTemplate" > <ti-header-operators [items]="[ { label: '迁移' }, { label: '修改策略' }, { label: '保存修改' }, { label: '取消修改' }, { label: '添加' } ]" [selectedRows]="[]" ></ti-header-operators> </ng-template> </ti-dynamic-form> 和 import { Component, ViewChild } from "@angular/core"; import { BehaviorSubject, Subject, delay, map, of } from "rxjs"; import { MATCH_DISABLED, MATCH_HIDDEN, MATCH_VALIDATOR_ADD, TI_GRID_KIT_ROW_ACTION_TYPE, TiDynamicFormComponent, TiDynamicFormGroupModel, TiDynamicGridModel, TiDynamicInputModel, TiDynamicSelectModel, TiGridKitEditEvents, } from "@cloud/ti-dynamic-form"; import { TiValidators } from "@cloud/tiny3"; import { TiRenderType, gridConstants } from "@cloud/tinycloud"; @Component({ selector: "app-dynamic-form-demo", templateUrl: "./dynamic-form-demo.component.html", }) export class DynamicFormDemoComponent { @ViewChild("tiDynamicFormComponent") tiDynamicFormComponent: TiDynamicFormComponent; model = [ new TiDynamicGridModel({ ...((isSingle = false) => { const id = isSingle ? "grid-with-batch-edit-single" : "grid-with-batch-edit-global"; return { id, label: "批量编辑表格", // 动态表单开启编辑能力 supportEdit: true, gridKitOption: { enableColsResponsive: true, rowSelection: "multiple", colDefs: ((singleEdit = true) => [ { headerName: "名称", field: "name", // 可通过该字段关闭该列修改能力。 supportEdit: false, }, { headerName: "策略", field: "protocol", cellRenderer: TiRenderType.TextWithTipRenderer, }, { headerName: "类型", field: "type", }, { headerName: "后端协议", field: "protocolPort", valueGetter: (params) => params.data.protocolPort.protocol + ": " + (params.data.protocolPort.sessionTime || "80"), }, { headerName: "操作", field: gridConstants.OPERATION_FIELD, cellRenderer: TiRenderType.OperationRenderer, cellRendererParams: (params) => { const gridDataLimit = new BehaviorSubject({ min: 1, }).getValue(); const deleteBtn = { label: "删除", key: "op_delete", actionCallback: (obj) => { new Subject().next({ action: TI_GRID_KIT_ROW_ACTION_TYPE.REMOVE, // 推荐使用data传递删除数据,兼容之前的rowIndex逻辑 // rowIndex: obj.rowIndex data: obj.data, }); }, }; // 如果是单行修改,其他行的修改按钮就需要禁用 const hasEditRows = !!new BehaviorSubject([]).getValue() .length; const editDisabledTip = hasEditRows ? "请先保存或者取消处于修改状态的行" : ""; if (singleEdit) { ({ label: "修改策略", key: "op-edit", actionCallback: (obj) => { new Subject().next({ // 当前修改的行数据 rowDatas: [obj.data], // 启动修改态 actionType: TiGridKitEditEvents.EDITING, callback: ({ editRows }) => { new BehaviorSubject([]).next(editRows); ((editRows) => {})(editRows); }, }); }, }).disabled = hasEditRows; ({ label: "修改策略", key: "op-edit", actionCallback: (obj) => { new Subject().next({ // 当前修改的行数据 rowDatas: [obj.data], // 启动修改态 actionType: TiGridKitEditEvents.EDITING, callback: ({ editRows }) => { new BehaviorSubject([]).next(editRows); ((editRows) => {})(editRows); }, }); }, }).tipStr = editDisabledTip; } deleteBtn.disabled = hasEditRows; deleteBtn.tipStr = editDisabledTip; // 如果当前只有一行,删除按钮设置成禁用; if ( params.context.parentArray.length <= (gridDataLimit?.min || 0) ) { deleteBtn.disabled = true; deleteBtn.tipStr = "只有一条记录,不能删除。"; } return { ...params, operationList: params.data.isCurrentEdit ? [ { label: "保存", key: "op-edit-cancel", actionCallback: (obj) => { new Subject().next({ // 当前修改的行数据 rowDatas: [obj.data], // 确定修改 actionType: TiGridKitEditEvents.CONFIRM_EDIT, // 如果在保存时需要在校验通过后先向后台请求保存,可以使用该接口。根据其返回的布尔值决定后续是否进行前台保存且 // 变成非编辑态(true: 进行后续;false或报错:不进行后续,停留在编辑态)。 // 13.0.120 版本新增 serverSideSaveFn: ({ editedRowDatas }) => ((editedRowDatas) => { console.log( "编辑后的行数据集合:", editedRowDatas ); // return throwError('This is an error!'); // 测试报错的场景 return of(editedRowDatas).pipe( delay(800), map(() => true) ); })(editedRowDatas), callback: ({ editRows }) => { new BehaviorSubject([]).next(editRows); ((editRows) => {})(editRows); }, }); }, }, { label: "取消", key: "ti-grid-kit-op-edit-cancel", actionCallback: (obj) => { new Subject().next({ // 当前修改的行数据 rowDatas: [obj.data], // 取消修改 actionType: TiGridKitEditEvents.CANCEL_EDIT, callback: ({ editRows }) => { new BehaviorSubject([]).next(editRows); ((editRows) => {})(editRows); }, }); }, }, ] : [ { label: "修改策略", key: "op-edit", actionCallback: (obj) => { new Subject().next({ // 当前修改的行数据 rowDatas: [obj.data], // 启动修改态 actionType: TiGridKitEditEvents.EDITING, callback: ({ editRows }) => { new BehaviorSubject([]).next(editRows); ((editRows) => {})(editRows); }, }); }, }, deleteBtn, ], }; }, flex: 1, }, ])(false), srcData: ((length = 2) => { const srcData = []; for (let i = 0; i < length; i++) { srcData.push({ name: `名称-${i}`, protocol: `容许-${i}`, type: `${i % 2 ? "IPV4" : "IPV6"}`, protocolPort: { sessionTime: "80", protocol: "HTTP", }, }); } return srcData; })(1), // displayMode: 'ti-grid' }, // 编辑态使用 groupFactory: () => [ new TiDynamicInputModel({ id: "protocol", value: "容许", validators: { required: null, }, }), new TiDynamicSelectModel({ id: "type", value: "IPV4", validators: { required: null, }, options: [ { label: "IPV4", value: "IPV4", }, { label: "IPV6", value: "IPV6", }, ], relations: [ { match: MATCH_DISABLED, when: [ { id: "protocol", value: "", }, ], }, ], }), // 协议端口 new TiDynamicFormGroupModel({ id: "protocolPort", group: [ new TiDynamicSelectModel({ id: "protocol", options: [ { value: "HTTP", label: "HTTP", }, { value: "TCP", label: "TCP", }, ], defaultValueIndex: 0, }), new TiDynamicInputModel({ id: "sessionTime", placeholder: "请输入保持会话时间", descriptions: "HTTP保持会话时间区域是1-1440,TCP保持会话的时间是1-60", // 根据表单值动态添加的校验器 dynamicValidators: { // 如果relation匹配成功,使用该校验规则 match: { rangeValue: { validator: TiValidators.rangeValue(1, 1440), }, }, // 如果relation匹配不成功,使用该校验规则 notMatch: { rangeValue: { validator: TiValidators.rangeValue(1, 60), }, }, }, // 校验规则,始终需要的校验规则 validators: { // 设置为null,会调用TiValidators.required进行校验; required: null, }, // 报错信息配置,可选项 errorMessages: { rangeValue: "当前协议的会话时间范围是:{0} - {1}", }, relations: [ { // 如果匹配成功,动态添加相关校验规则 match: MATCH_VALIDATOR_ADD, // 当是layer是L7的后端协议,启用相关校验规则 when: [ { id: "protocol", value: "HTTP", }, ], }, { match: MATCH_HIDDEN, when: [ { value: "IPV4", /** * 13.0.180 版本新增,用在formarray和dynamic-grid组件中和本行的formGroup其他control建立关联关系 * 解决之前通过rootPath(id.index.id)中行新增删除时index未同步变化时(groupFactory只能在初始化时调用一次)引起的关联关系不正确的问题 * * 注意:如果存在formarray里面嵌套formarry类,可能会存在查找到的行group不准确的场景 * 查找逻辑:当前control往上递归,如果其父元素是个formArray,则认为该group为要找的行group */ currentRowPath: "type", }, ], }, ], }), ], }), ], // 行数据限制 limit: new BehaviorSubject({ min: 1, }), initialCount: 5, // 配置触发表格刷新和添加删除事件的流: 组件内部会监听这个流去触发对应的行为。 actionEvents: { // 行操作流 rowAction: new Subject(), // 编辑态流 editAction: new Subject(), // 刷新单元格流 refreshCells: new Subject(), }, }; })(), }), ]; customEvent = (customEventObj) => { if (customEventObj?.type === "gridRowSelected") { } }; } 为什么点击按钮没反应
07-12
new TiDynamicGridModel({ ...((isSingle = false) => { const id = isSingle ? 'grid-with-batch-edit-single' : 'grid-with-batch-edit-global' return { id, label: '批量编辑表格', // 动态表单开启编辑能力 supportEdit: true, gridKitOption: { enableColsResponsive: true, rowSelection: 'multiple', colDefs: (( singleEdit = true ) => [ { headerName: '名称', field: 'name', // 可通过该字段关闭该列修改能力。 supportEdit: false, }, { headerName: '策略', field: 'protocol', cellRenderer: TiRenderType.TextWithTipRenderer, }, { headerName: '类型', field: 'type', }, { headerName: '后端协议', field: 'protocolPort', valueGetter: (params) => params.data.protocolPort .protocol + ': ' + (params.data.protocolPort .sessionTime || '80'), }, { headerName: '操作', field: gridConstants.OPERATION_FIELD, cellRenderer: TiRenderType.OperationRenderer, cellRendererParams: ( params ) => { const gridDataLimit = new BehaviorSubject({ min: 1, }).getValue() const deleteBtn = { label: '删除', key: 'op_delete', actionCallback: ( obj ) => { new Subject().next({ action: TI_GRID_KIT_ROW_ACTION_TYPE.REMOVE, // 推荐使用data传递删除数据,兼容之前的rowIndex逻辑 // rowIndex: obj.rowIndex data: obj.data, }) }, } // 如果是单行修改,其他行的修改按钮就需要禁用 const hasEditRows = !!new BehaviorSubject( [] ).getValue().length const editDisabledTip = hasEditRows ? '请先保存或者取消处于修改状态的行' : '' if (singleEdit) { ;({ label: '修改策略', key: 'op-edit', actionCallback: ( obj ) => { new Subject().next({ // 当前修改的行数据 rowDatas: [ obj.data, ], // 启动修改态 actionType: TiGridKitEditEvents.EDITING, callback: ({ editRows, }) => { new BehaviorSubject( [] ).next(editRows) ;(( editRows ) => {})( editRows ) }, }) }, }).disabled = hasEditRows ;({ label: '修改策略', key: 'op-edit', actionCallback: ( obj ) => { new Subject().next({ // 当前修改的行数据 rowDatas: [ obj.data, ], // 启动修改态 actionType: TiGridKitEditEvents.EDITING, callback: ({ editRows, }) => { new BehaviorSubject( [] ).next(editRows) ;(( editRows ) => {})( editRows ) }, }) }, }).tipStr = editDisabledTip } deleteBtn.disabled = hasEditRows deleteBtn.tipStr = editDisabledTip // 如果当前只有一行,删除按钮设置成禁用; if ( params.context .parentArray.length <= (gridDataLimit?.min || 0) ) { deleteBtn.disabled = true deleteBtn.tipStr = '只有一条记录,不能删除。' } return { ...params, operationList: params .data.isCurrentEdit ? [ { label: '保存', key: 'op-edit-cancel', actionCallback: (obj) => { new Subject().next( { // 当前修改的行数据 rowDatas: [ obj.data, ], // 确定修改 actionType: TiGridKitEditEvents.CONFIRM_EDIT, // 如果在保存时需要在校验通过后先向后台请求保存,可以使用该接口。根据其返回的布尔值决定后续是否进行前台保存且 // 变成非编辑态(true: 进行后续;false或报错:不进行后续,停留在编辑态)。 // 13.0.120 版本新增 serverSideSaveFn: ({ editedRowDatas, }) => (( editedRowDatas ) => { console.log( '编辑后的行数据集合:', editedRowDatas ) // return throwError('This is an error!'); // 测试报错的场景 return of( editedRowDatas ).pipe( delay( 800 ), map( () => true ) ) })( editedRowDatas ), callback: ({ editRows, }) => { new BehaviorSubject( [] ).next( editRows ) ;(( editRows ) => {})( editRows ) }, } ) }, }, { label: '取消', key: 'ti-grid-kit-op-edit-cancel', actionCallback: (obj) => { new Subject().next( { // 当前修改的行数据 rowDatas: [ obj.data, ], // 取消修改 actionType: TiGridKitEditEvents.CANCEL_EDIT, callback: ({ editRows, }) => { new BehaviorSubject( [] ).next( editRows ) ;(( editRows ) => {})( editRows ) }, } ) }, }, ] : [ { label: '修改策略', key: 'op-edit', actionCallback: (obj) => { new Subject().next( { // 当前修改的行数据 rowDatas: [ obj.data, ], // 启动修改态 actionType: TiGridKitEditEvents.EDITING, callback: ({ editRows, }) => { new BehaviorSubject( [] ).next( editRows ) ;(( editRows ) => {})( editRows ) }, } ) }, }, deleteBtn, ], } }, flex: 1, }, ])(false), srcData: ((length = 2) => { const srcData = [] for ( let i = 0; i < length; i++ ) { srcData.push({ name: `名称-${i}`, protocol: `容许-${i}`, type: `${ i % 2 ? 'IPV4' : 'IPV6' }`, protocolPort: { sessionTime: '80', protocol: 'HTTP', }, }) } return srcData })(1), // displayMode: 'ti-grid' }, // 编辑态使用 groupFactory: () => [ new TiDynamicInputModel({ id: 'protocol', value: '容许', validators: { required: null, }, }), new TiDynamicSelectModel({ id: 'type', value: 'IPV4', validators: { required: null, }, options: [ { label: 'IPV4', value: 'IPV4', }, { label: 'IPV6', value: 'IPV6', }, ], relations: [ { match: MATCH_DISABLED, when: [ { id: 'protocol', value: '', }, ], }, ], }), // 协议端口 new TiDynamicFormGroupModel({ id: 'protocolPort', group: [ new TiDynamicSelectModel({ id: 'protocol', options: [ { value: 'HTTP', label: 'HTTP', }, { value: 'TCP', label: 'TCP', }, ], defaultValueIndex: 0, }), new TiDynamicInputModel({ id: 'sessionTime', placeholder: '请输入保持会话时间', descriptions: 'HTTP保持会话时间区域是1-1440,TCP保持会话的时间是1-60', // 根据表单值动态添加的校验器 dynamicValidators: { // 如果relation匹配成功,使用该校验规则 match: { rangeValue: { validator: TiValidators.rangeValue( 1, 1440 ), }, }, // 如果relation匹配不成功,使用该校验规则 notMatch: { rangeValue: { validator: TiValidators.rangeValue( 1, 60 ), }, }, }, // 校验规则,始终需要的校验规则 validators: { // 设置为null,会调用TiValidators.required进行校验; required: null, }, // 报错信息配置,可选项 errorMessages: { rangeValue: '当前协议的会话时间范围是:{0} - {1}', }, relations: [ { // 如果匹配成功,动态添加相关校验规则 match: MATCH_VALIDATOR_ADD, // 当是layer是L7的后端协议,启用相关校验规则 when: [ { id: 'protocol', value: 'HTTP', }, ], }, { match: MATCH_HIDDEN, when: [ { value: 'IPV4', /** * 13.0.180 版本新增,用在formarray和dynamic-grid组件中和本行的formGroup其他control建立关联关系 * 解决之前通过rootPath(id.index.id)中行新增删除时index未同步变化时(groupFactory只能在初始化时调用一次)引起的关联关系不正确的问题 * * 注意:如果存在formarray里面嵌套formarry类,可能会存在查找到的行group不准确的场景 * 查找逻辑:当前control往上递归,如果其父元素是个formArray,则认为该group为要找的行group */ currentRowPath: 'type', }, ], }, ], }), ], }), ], // 行数据限制 limit: new BehaviorSubject({ min: 1, }), initialCount: 5, // 配置触发表格刷新和添加删除事件的流: 组件内部会监听这个流去触发对应的行为。 actionEvents: { // 行操作流 rowAction: new Subject(), // 编辑态流 editAction: new Subject(), // 刷新单元格流 refreshCells: new Subject(), }, } })(), })
07-12
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值