Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Form.List support noRecord #21725

Open
jjwong1991 opened this issue Mar 1, 2020 · 29 comments
Open

Form.List support noRecord #21725

jjwong1991 opened this issue Mar 1, 2020 · 29 comments
Assignees

Comments

@jjwong1991
Copy link

jjwong1991 commented Mar 1, 2020

  • [] I have searched the issues of this repository and believe that this is not a duplicate.

Reproduction link

Edit on CodeSandbox

Steps to reproduce

  1. 在列表xxx中输入非法值123
  2. 列表选择【隐藏】
  3. 点击【提交】
  4. 规则校验不会校验列表xxx字段的值,但是getFieldsValue()和onFinish的值仍旧包含
{
  list: [
    ...,
    xxx: "123",
  ],
}

What is expected?

Form.List内的Form.Item隐藏后在收集值时可以符合页面表现

<Form.List>中的<Form.Item name={[index, "xxx"]}>不渲染时
收集值应不包含list[index].xxx

<Form.List>中的<Form.Item name={[index, "xxx"]}>
应该等同于直接在
<Form>内书写<Form.Item name={["list", index, "xxx"]}>
去作为数据绑定
而不能因为是Form.List的子元素,表单项
<Form.Item name={[index, "xxx"]}>
的显隐变化在收集值时就被忽略掉。

<Form.Item name={[index, "xxx"]}>
不渲染时,收集的值应该为

{
  list: [
    type: "2",
  ],
}

而非

{
  list: [
    type: "2",
    xxx: "123",
  ],
}

What is actually happening?

<Form.List>中的<Form.Item name={[index, "xxx"]}>在未渲染时,值仍然被收集。

Environment Info
antd 4.0.0
React latest
System Windows10
Browser Chrome80

#21713 中并未解决我说的问题

@jjwong1991
Copy link
Author

Edit on CodeSandbox
正常的收集值应该如这个Demo的表现

@zombieJ
Copy link
Member

zombieJ commented Mar 2, 2020

hi @jjwong1991 ~

数据绑定是来自于 Form.Listname。子元素没有收集但是 list 本身是收集的。从 List 角度看,有注册子 path 就收集,没有就不收集这个行为本身就是很怪异的。导致收集出打洞的结果:

{
  list: [1, undefined, undefined, 5]
}

<Form.List>中的<Form.Item name={[index, "xxx"]}> 设计上就不等同于 <Form>内书写<Form.Item name={["list", index, "xxx"]}>

@zombieJ
Copy link
Member

zombieJ commented Mar 2, 2020

How do you think about this. @afc163 @shaodahong @mgcrea


@mgcrea Let me trans in English simplify. @jjwong1991 wish Form.List should not be collected value by onFinish. It should only collect when Form.List with sub Form.Item. But I think this makes value collection holy like:

{
  list: [1, undefined, undefined, 5]
}

@jjwong1991
Copy link
Author

@zombieJ 明白您所说的,如果整体隐藏一个list的直接子元素会产生这样的问题。
但是在实际应用中,list数组内的值通常会包含多个子组件产生一个对象数据作为每一个数组元素。现有的绑定方式<Form.List name="list"><Form>对这些list中的<Form.Item name={[index, "xxx"]}>的显隐变化所带来的对象内部的值的改变都是不可控的,当这些值产生时,它们被注册,放入了Form的store中,这没问题。但是当它们被从页面上移除,本应该忽略的值还会被从store中读取,甚至跳过表单校验被提交,这对于具有分支逻辑的表单显然是致命的。
如果可以,我期望可以提供类似getFieldsValue()getFieldsValue(true)这样的api,来让使用者决定是要获取store中的全部的值,还是只需要表单内显示的值

@zombieJ
Copy link
Member

zombieJ commented Mar 2, 2020

个人建议是对于打洞的 Form.List 可以自己封装一个 List 组件,跳过上层注册步骤。

@jx-zyf
Copy link

jx-zyf commented Mar 2, 2020

我认为表单校验和最后Finished获取的值应该用同样的规则,既然校验通过了,最后的值也应该是校验后的值。

@shaodahong
Copy link
Member

确实有问题,如果隐藏了按理来说确实是需要干掉的,但是实际业务上对于数组结构的数据更多的是手动增删改,如果 Form 底层帮忙处理了反而不太好

这个例子:
https://ant.design/components/form-cn/#components-form-demo-dynamic-form-item

@jjwong1991
Copy link
Author

<Form.Item>的数据绑定的表现我觉得还是统一比较好,不然list内外的<Form.Item>表现却不一致,很让人困扰

@shaodahong
Copy link
Member

这个就是问题所在,List 太灵活了,Item 是对象关系的渲染,List 是数组关系的渲染,数组结构在 Form 表单的表现应该是一致的,不应该需要用户自己再去写 Item,直接

<Form.List name="list" render={({add, remove}) => <Input />}>

就好了

@zombieJ
Copy link
Member

zombieJ commented Mar 3, 2020

数据绑定不是来自于 Form.Item,而是来自上层的 Form.List。就如同 @shaodahong 的例子,用户在 Form.List 里甚至可以不用写 Form.Item。如果用户直接调用了 add 方法而不渲染 Form.Item,那么预期肯定不是希望得到一个空的列表。

Data binding does not come from From.Item. It's parent Form.List instead. User can even use it without Form.Item like @shaodahong 's sample. If user call add directly but not render Form.Item, it's expect to get the full list instead of empty one.

@jjwong1991
Copy link
Author

@zombieJ Fom.List在页面上表现为数组没问题,问题的点是,当Form.List内有Form.Item去描述数组内的对象结构时,Form.Item的变化没有再和数据映射起来,但是在Form.List外却又是正常映射的,这很奇怪

@jjwong1991
Copy link
Author

<FormList name="list">
  {(fields, operations) => {
    return (
      <Fragment>
        {fields.map((field, index) => (
          <div
            key={field.key}
          >
            <FormItem
              noStyle
              shouldUpdate
            >
              {({ getFieldValue }) => {
                return isShow ? (
                  <FormItem
                    {...formItemLayout}
                    name={[index, "value1"]}
                    label="Value"
                    rules={[
                      {
                        required: true,
                        message: "Please input Value!"
                      }
                    ]}
                  >
                    <Input />
                  </FormItem>
                ) : null;
              }}
            </FormItem>
            <FormItem
              {...formItemLayout}
              name={[index, "value2"]}
              label="Value"
              rules={[
                {
                  required: true,
                  message: "Please input Value!"
                }
              ]}
            >
              <Input />
            </FormItem>
          </div>
        ))}
      </Fragment>
    );
  }}
</FormList>

类似这样的结构中,在isShow=true时,应该期望得到

{
  list: [
    {
      value1: "",
      value2: "",
    },
  ],
}

isShow=false时,应该期望得到

{
  list: [
    {
      value2: "",
    },
  ],
}

但是现在在isShow先为true,后变为false的时候,得到的是

{
  list: [
    {
      value1: "",
      value2: "",
    },
  ],
}

value1在页面上并没有对应的组件,但却产生了值
而直接在Form下书写就不会这样

@jjwong1991
Copy link
Author

Edit on CodeSandbox
可以看下这个Demo,左侧是使用<FormList>包裹的两个<Input>,右侧是<Form>直接包裹的两个<Input>
isShow打开的情况下,在<Input>中输入任意值,分别点击两边的提交,两侧表单中value1和value2都有值。
不做其他修改,关闭isShow,再分别点击两边的提交,左侧表单仍然包含value2,右侧表单不包含value2,

@shaodahong
Copy link
Member

@jjwong1991 你的这个是条件渲染,底层是没办法知道的,我上面也提到了实际业务增删基本上都是手动对对应的某一项删除或者整体新增

@zombieJ
Copy link
Member

zombieJ commented Mar 3, 2020

{
  list: [
    {
      value1: "", // Should hide
      value2: "",
    },
  ],
}

这个例子感觉也有些道理,不同于打洞,List 注册子元素又做了一层分叉路径的确是按照分叉的走更合理。或许 Form.List 的确需要有一个诸如 record 的属性来让用户决定要不要开启全部收集。但是这又比较把用法搞的越来越复杂了。

Rather than holy list, this example make sense for sub field register additional nest fields. Maybe we can ddd record prop on Form.List to decide whether need collect all values. But I'm afraid this make user harder to understand.

@jjwong1991
Copy link
Author

@shaodahong 是条件渲染没错,但是Form知道哪些渲染哪些没渲染,在执行表单校验和获取值时都会跳过没渲染的组件。而FormList内的组件则会在验证时被跳过,在取值时被取到。底层可以获取到何时触发校验,却不能获取到合适应该收集值,这说不通

@jjwong1991
Copy link
Author

jjwong1991 commented Mar 3, 2020

@zombieJ 所以我在想是否可以这样,当FormList内没有FormItem时,可以像现在的绑定逻辑来处理,保证不会出现打洞的情况;当FormList内有FormItem时,FormList只作为一个提供name前缀的组件,内部的FormItem的name被拼装成[FormList的name, index, name]这样的形式去被Form执行数据绑定,这样所有FormItem的表现都是一致的

@zombieJ
Copy link
Member

zombieJ commented Mar 3, 2020

自动收集太黑盒了,还是应该交由用户来选择。否则也会遇到其他类似这个 issue 的预期问题。

Automatic collection is in black box. We should let user to handle this.

@shaodahong
Copy link
Member

shaodahong commented Mar 3, 2020

条件渲染底层是不知道对应的那个的,没有被验证是因为这个 item 已经不存在了,在显示的时候输入触发收集,值已经缓存住了,切换到隐藏状态虽然条件渲染干掉了这个 item,但是已存在的 key 并没有干掉,validateFields 校验是根据对应 item 的 rule 来的 (然而已经被干掉了),想要解决这种情况我觉得就是要限制用户对 List 的渲染控制,不需要放开 Children 给他们,只需要提供类似 render 的就行

@shaodahong
Copy link
Member

而且从我的实际操作来看,我更希望 Form 提供配置化,因为手写 Item 这些重复性太多了

// render edit 模式下 默认渲染 <Input /> view模式下 渲染 value
const cloumns = [
  {name: 'name', rules: []},
  {name: 'age', rules: [], render: () => <Select />}
]

<Form mode={edit | view} cloumns={columns} />

@jjwong1991
Copy link
Author

我觉得加一个配置项是可取的,就像getFieldsValue(ture)这样来控制是否全量取值,当不传参数的时候就取页面渲染组件的值
既然可以在validateFields的时候校验rule是否已经被干掉,也可以在onFinish的时候校验name是否已经被干掉

@zombieJ
Copy link
Member

zombieJ commented Mar 3, 2020

嗯,我指的就是在 Form.List 加一个属性来支持。

Yes. I mean to add a prop on the Form.List.

@jjwong1991
Copy link
Author

辛苦了 近期是否有升级计划呢

@zombieJ
Copy link
Member

zombieJ commented Mar 4, 2020

可以在 feature 里弄一个。我打个标。

@zombieJ zombieJ self-assigned this Mar 4, 2020
@zombieJ zombieJ added 4.x In Ant Design 4.0 💡 Feature Request labels Mar 4, 2020
@zombieJ zombieJ changed the title Form.List的表现不符合预期。Form. List did not perform as expected. Form.List support noRecord Mar 4, 2020
@jjwong1991
Copy link
Author

请问一下这个目前有更新计划了吗~

@zombieJ
Copy link
Member

zombieJ commented Apr 22, 2020

暂时没有,收集一波 Form feedback 后再一起搞一搞。

@AhmedCommando
Copy link

is there a way to provide an initial state to the form.list. I mean a prefilled form items inside the form.list?

@shanzhaozhen
Copy link

Edit on CodeSandbox
可以看下这个Demo,左侧是使用<FormList>包裹的两个<Input>,右侧是<Form>直接包裹的两个<Input>
isShow打开的情况下,在<Input>中输入任意值,分别点击两边的提交,两侧表单中value1和value2都有值。
不做其他修改,关闭isShow,再分别点击两边的提交,左侧表单仍然包含value2,右侧表单不包含value2,

假如我使用的是第三方的Input,不是antd里面的,在Form.Item的包裹下,如何将error的信息提取出来啊

@jamesGao123
Copy link

这个问题有结论了吗 能改么

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

8 participants