联动

上一节我们介绍了表达式的概念,而表达式应用最多的场景,是实现页面的联动效果。

基本联动

元素的联动是页面开发中很常见的功能之一,类似于:

  • 某个条件下显示或隐藏某个组件
  • 某个条件下请求接口
  • 某个条件下轮询接口停止轮询
  • 等等…

联动配置项一般都是 表达式

组件配置联动

控制组件的显隐,表单项的禁用状态等,看下面这个例子:

```schema: scope=”body” { “type”: “form”, “body”: [ { “type”: “radios”, “name”: “foo”, “label”: false, “options”: [ { “label”: “类型1”, “value”: 1 }, { “label”: “类型2”, “value”: 2 } ] }, { “type”: “input-text”, “name”: “text1”, “label”: false, “placeholder”: “选中 类型1 时可见”, “visibleOn”: “${foo == 1}” }, { “type”: “input-text”, “name”: “text2”, “label”: false, “placeholder”: “选中 类型2 时不可点”, “disabledOn”: “${foo == 2}” } ] }

  1. 上面实例主要为一个表单,表单内有三个组件:一个`radio`, 两个`text`,通过配置联动配置项,实现下面联动效果:
  2. 1. 只要当`radio`选中`类型1`时,才会显示`text1`
  3. 2. `radio`选中`类型2`时,`text2`将会变为`禁用状态`
  4. > **注意:**
  5. >
  6. > 在表单项联动中,为了方便数据的读取,赋值后或者修改过的表单项,通过隐藏后,并不会在当前数据域中删除掉该字段值,因此默认提交的时候可能仍然会带上已隐藏表单项的值。
  7. >
  8. > 如果想要在提交时去掉某个隐藏的字段,可以通过添加 [clearValueOnHidden](../../components/form/formitem#隐藏时删除表单项值) 属性实现。
  9. ### 接口联动
  10. #### 基本使用
  11. 接口联动是另外一种很常见的场景,查看下面这个例子:
  12. ```schema: scope="body"
  13. {
  14. "title": "",
  15. "type": "form",
  16. "mode": "horizontal",
  17. "body": [
  18. {
  19. "label": "选项1",
  20. "type": "radios",
  21. "name": "a",
  22. "inline": true,
  23. "options": [
  24. {
  25. "label": "选项A",
  26. "value": 1
  27. },
  28. {
  29. "label": "选项B",
  30. "value": 2
  31. },
  32. {
  33. "label": "选项C",
  34. "value": 3
  35. }
  36. ]
  37. },
  38. {
  39. "label": "选项2",
  40. "type": "select",
  41. "size": "sm",
  42. "name": "b",
  43. "source": "/api/mock2/options/level2?a=${a}",
  44. "description": "切换<code>选项1</code>的值,会触发<code>选项2</code>的<code>source</code> 接口重新拉取"
  45. }
  46. ],
  47. "actions": []
  48. }

上面例子我们实现了这个逻辑:每次选择选项1的时候,会触发选项2source配置的接口重新请求,并返回不同的下拉选项。

是如何做到的?

实际上,所有初始化接口链接上使用数据映射获取参数的形式时,例如下面的query=${query},在当前数据域中,所引用的变量值(即 query)发生变化时,自动重新请求该接口。

  1. {
  2. "initApi": "/api/initData?query=${query}"
  3. }

tip:

触发所引用变量值发生变化的方式有以下几种:

  1. 通过对表单项的修改,可以更改表单项name属性值所配置变量的值;
  2. 通过组件间联动,将其他组件的值发送到目标组件,进行数据域的更新,从而触发联动效果

接口联动一般只适用于初始化接口,例如:

  • form组件中的initApi
  • select组件中的source选项源接口url, data只能用于主动联动;
  • service组件中的apischemaApi
  • crud组件中的api;(crud 默认是跟地址栏联动,如果要做请关闭同步地址栏 syncLocation: false)
  • 等等…

如果 api 地址中有变量,比如 /api/mock2/sample/${id},amis 就不会自动加上分页参数,需要自己加上,改成 /api/mock2/sample/${id}?page=${page}&perPage=${perPage}

配置请求条件

默认在变量变化时,总是会去请求联动的接口,你也可以配置请求条件,当只有当前数据域中某个值符合特定条件才去请求该接口。

```schema: scope=”body” { “title”: “”, “type”: “form”, “mode”: “horizontal”, “body”: [ { “label”: “选项1”, “type”: “radios”, “name”: “a”, “inline”: true, “options”: [ { “label”: “选项A”, “value”: 1 }, { “label”: “选项B”, “value”: 2 }, { “label”: “选项C”, “value”: 3 } ] }, { “label”: “选项2”, “type”: “select”, “size”: “sm”, “name”: “b”, “source”: { “method”: “get”, “url”: “/api/mock2/options/level2?a=${a}”, “sendOn”: “this.a === 2” }, “description”: “只有选项1选择B的时候,才触发选项2source接口重新拉取” } ], “actions”: [] }

  1. 更多用法,见:[Api-配置请求条件](../types/api#%E9%85%8D%E7%BD%AE%E8%AF%B7%E6%B1%82%E6%9D%A1%E4%BB%B6)
  2. #### 主动触发
  3. 上面示例有个问题,就是数据一旦变化就会出发重新拉取,而输入框的频繁变化值会导致频繁的拉取?没关系,也可以配置主动拉取如:
  4. ```schema: scope="body"
  5. {
  6. "type": "form",
  7. "name": "my_form",
  8. "body": [
  9. {
  10. "type": "input-text",
  11. "name": "keyword",
  12. "addOn": {
  13. "label": "搜索",
  14. "type": "button",
  15. "actionType": "reload",
  16. "target": "my_form.select"
  17. }
  18. },
  19. {
  20. "type": "select",
  21. "name": "select",
  22. "label": "Select",
  23. "source": {
  24. "method": "get",
  25. "url": "/api/mock2/form/getOptions?waitSeconds=1",
  26. "data": {
  27. "a": "${keyword}"
  28. }
  29. }
  30. }
  31. ]
  32. }
  1. 通过api对象形式,将获取变量值配置到data请求体中。
  2. 配置搜索按钮,并配置该行为是刷新目标组件,并配置目标组件target
  3. 这样我们只有在点击搜索按钮的时候,才会将keyword值发送给select组件,重新拉取选项

表单提交返回数据

表单提交后会将返回结果合并到当前表单数据域,因此可以基于它实现提交按钮后显示结果,比如

```schema: scope=”body” { “type”: “form”, “api”: “/api/mock2/form/saveForm”, “title”: “查询用户 ID”, “body”: [ { “type”: “input-group”, “name”: “input-group”, “body”: [ { “type”: “input-text”, “name”: “name”, “label”: “姓名” }, { “type”: “submit”, “label”: “查询”, “level”: “primary” } ] }, { “type”: “static”, “name”: “id”, “visibleOn”: “typeof data.id !== ‘undefined’”, “label”: “返回 ID” } ], “actions”: [] }

  1. 上面的例子首先用 `"actions": []` 将表单默认的提交按钮去掉,然后在 `input-group` 里放一个 `submit` 类型的按钮来替代表单查询。
  2. 这个查询结果返回类似如下的数据
  3. ```json
  4. {
  5. "status": 0,
  6. "msg": "保存成功",
  7. "data": {
  8. "id": 1
  9. }
  10. }

amis 会将返回的 data 写入表单数据域,因此下面的 static 组件就能显示了。

其他联动

还有一些组件特有的联动效果,例如 form 的 disabledOn,crud 中的 itemDraggableOn 等等,可以参考相应的组件文档。

组件间联动

联动很可能会出现跨组件的形式,思考下面这种场景:

有一个表单form组件,还有一个列表组件crud,我们想要把form提交的数据,可以用作crud的查询条件,并请求crud的接口,由于formcrud位于同一层级,因此没法使用数据链的方式进行取值。

```schema: scope=”body” [ { “title”: “查询条件”, “type”: “form”, “body”: [ { “type”: “input-text”, “name”: “keywords”, “label”: “关键字:” } ], “submitText”: “搜索” }, { “type”: “crud”, “api”: “/api/mock2/sample”, “columns”: [ { “name”: “id”, “label”: “ID” }, { “name”: “engine”, “label”: “Rendering engine” }, { “name”: “browser”, “label”: “Browser” }, { “name”: “platform”, “label”: “Platform(s)” }, { “name”: “version”, “label”: “Engine version” } ] } ]

  1. 现在更改配置如下:
  2. ```schema: scope="body"
  3. [
  4. {
  5. "title": "查询条件",
  6. "type": "form",
  7. "target": "my_crud",
  8. "body": [
  9. {
  10. "type": "input-text",
  11. "name": "keywords",
  12. "label": "关键字:"
  13. }
  14. ],
  15. "submitText": "搜索"
  16. },
  17. {
  18. "type": "crud",
  19. "name": "my_crud",
  20. "api": "/api/mock2/sample",
  21. "columns": [
  22. {
  23. "name": "id",
  24. "label": "ID"
  25. },
  26. {
  27. "name": "engine",
  28. "label": "Rendering engine"
  29. },
  30. {
  31. "name": "browser",
  32. "label": "Browser"
  33. },
  34. {
  35. "name": "platform",
  36. "label": "Platform(s)"
  37. },
  38. {
  39. "name": "version",
  40. "label": "Engine version"
  41. }
  42. ]
  43. }
  44. ]

我们进行两个调整:

  1. crud组件设置了name属性为my_crud
  2. form组件配置了target属性为crudnamemy_crud

更改配置后,提交表单时,如果有配置提交接口,会先请求提交,之后 amis 会寻找target所配置的目标组件,把form中所提交的数据,发送给该目标组件中,并将该数据合并到目标组件的数据域中,并触发目标组件的刷新操作,对于 CRUD 组件来说,刷新即重新拉取数据接口。

当然,crud组件内置已经支持此功能,你只需要配置crud中的filter属性,就可以实现上面的效果,更多内容查看 crud -> filter 文档。

我们再来一个例子,这次我们实现 两个 form 之间的联动

发送指定数据

target属性支持通过配置参数来发送指定数据,例如:"target" :"xxx?a=${a}&b=${b}",这样就会把当前数据域中的a变量和b变量发送给目标组件

```schema: scope=”body” [ { “type”: “form”, “title”: “form1”, “mode”: “horizontal”, “api”: “/api/mock2/form/saveForm”, “body”: [ { “label”: “Name”, “type”: “input-text”, “name”: “name” },

  1. {
  2. "label": "Email",
  3. "type": "input-text",
  4. "name": "email"
  5. },
  6. {
  7. "label": "Company",
  8. "type": "input-text",
  9. "name": "company"
  10. }
  11. ],
  12. "actions": [
  13. {
  14. "type": "action",
  15. "actionType": "reload",
  16. "label": "发送到 form2",
  17. "target": "form2?name=${name}&email=${email}"
  18. }
  19. ]

}, { “type”: “form”, “title”: “form2”, “name”: “form2”, “mode”: “horizontal”, “api”: “/api/mock2/form/saveForm”, “body”: [ { “label”: “MyName”, “type”: “input-text”, “name”: “name” },

  1. {
  2. "label": "MyEmail",
  3. "type": "input-text",
  4. "name": "email"
  5. },
  6. {
  7. "label": "Company",
  8. "type": "input-text",
  9. "name": "company"
  10. }
  11. ]

} ]

  1. 上例中我们给按钮上配置了`"target": "form2?name=${name}&email=${email}"`,可以把当前数据链中的`name`变量和`email`变量发送给`form2`
  2. ### 配置多个目标
  3. `target`支持配置多个目标组件 name,用逗号隔开,例如:
  4. ```json
  5. {
  6. "type": "action",
  7. "actionType": "reload",
  8. "label": "刷新目标组件",
  9. "target": "target1,target2"
  10. }

上例中点击按钮会刷新target1target2组件。

事实上,组件间联动也可以实现上述任意的 基本联动效果(显隐联动、接口联动等其他联动)。