Git Product home page Git Product logo

antd-form-builder's Issues

css misbehavior

Screen Shot 2020-03-27 at 11 39 23 AM

Problems:

  1. components show the wrong style. When I put above submit button, all on this page show the correct style.

  2. Before click , all components works correct. After click , the red rectangle area won't response to any click event. It seems like something invisible musk that area.

Environment:

package.json

{
  "private": true,
  "scripts": {
    "start": "umi dev",
    "build": "umi build",
    "prettier": "prettier --write '**/*.{js,jsx,tsx,ts,less,md,json}'",
    "test": "umi-test",
    "test:coverage": "umi-test --coverage"
  },
  "gitHooks": {
    "pre-commit": "lint-staged"
  },
  "lint-staged": {
    "*.{js,jsx,less,md,json}": [
      "prettier --write"
    ],
    "*.ts?(x)": [
      "prettier --parser=typescript --write"
    ]
  },
  "dependencies": {
    "@types/classnames": "^2.2.10",
    "@umijs/preset-react": "1.x",
    "@umijs/test": "^3.0.13",
    "antd-form-builder": "file:./vendors/antd-form-builder",
    "classnames": "^2.2.6",
    "lint-staged": "^10.0.7",
    "prettier": "^1.19.1",
    "react": "^16.12.0",
    "react-dom": "^16.12.0",
    "umi": "^3.0.13",
    "yorkie": "^2.0.0"
  }
}

config/config.ts

import { defineConfig } from 'umi';
import {routes} from './router';

export default defineConfig({
  targets: {
    ie: 11,
  },
  locale: {
    default: 'zh-CN',
    antd: true,
    baseNavigator: true,
    baseSeparator: '-',
  },
  layout: {
    title: 'Da Vinci',
    locale: true,
  },
  antd: {},
  dva: {hmr: true},
  dynamicImport: {
    loading: '@/components/PageLoading',
  },
  routes,
});

running demo
your example-v4/basic.js

import React, { Component } from 'react'
import { Form, Button, Rate, Card, InputNumber, DatePicker } from 'antd'
// @ts-ignore
import FormBuilder from 'antd-form-builder'

export default class App extends Component {
  form = FormBuilder.createForm(this);
  handleFinish = () => {
    console.log('submit: ', this.form.getFieldsValue())
  };

  render() {
    const options = ['Apple', 'Orange', 'Banana']
    const meta = {
      columns: 1,
      dynamicFields: '*',
      fields: [
        {
          key: 'obj.input',
          label: 'Input',
          required: true,
          tooltip: 'This is the tooltip.',
        },
        { key: 'checkbox', label: 'Checkbox', widget: 'checkbox', initialValue: true },
        { key: 'rating', label: 'Rating', widget: Rate, initialValue: 2 },
        { key: 'switch', label: 'Switch', widget: 'switch', initialValue: true },
        { key: 'select', label: 'Select', widget: 'select', options },
        { key: 'checkbox-group', label: 'Checkbox Group', widget: 'checkbox-group', options },
        { key: 'radio-group', label: 'Radio Group', widget: 'radio-group', options },
        {
          key: 'radio-button-group',
          label: 'Radio Button Group',
          widget: 'radio-group',
          buttonGroup: true,
          options,
        },
        { key: 'password', label: 'Password', widget: 'password' },
        { key: 'textarea', label: 'Textarea', widget: 'textarea' },
        { key: 'number', label: 'Number', widget: 'number' },
        { key: 'date-picker', label: 'Date Picker', widget: 'date-picker' },
      ],
    }

    return (
      <Card>
      <Form
        form={this.form}
        layout="horizontal"
        onValuesChange={this.form.handleValuesChange}
        onFinish={this.handleFinish}
      >
        <FormBuilder meta={meta} form={this.form} />
        <Form.Item wrapperCol={{ span: 16, offset: 8 }} className="form-footer">
          <Button htmlType="submit" type="primary" onClick={() => this.forceUpdate()}>
            Submit
          </Button>
        </Form.Item>
      </Form>
      </Card>
    )
  }
}

onSeach in select widget

I'm trying to adapt this example such that select widget allows for REST API query but it doesn't seem to fire onSearch event. This is my attempt (posting just the necessary changes)

This part is from the antd's search box

import {useState, useEffect} from "react"
import axios from "axios"
import querystring from 'querystring';

let timeout;
let currentValue;

function QueryFetch(url, value, initialState, callback) {
    const [data, setData] = useState(initialState);
    const [loading, setLoading] = useState(false);
    const [error, setError] = useState(null);

    if (timeout) {
        clearTimeout(timeout);
        timeout = null;
    }
    currentValue = value;

    const str = querystring.encode({
        code: 'utf-8',
        q: value,
    });

    useEffect(() => {
        setLoading(true)

        async function fetchData() {
            try {
                const res = await axios.get(`url?${str}`);
                if (currentValue === value) {
                    console.log(res.data);
                    setData(res.data.results);
                    callback(data);
                }
            } catch (error) {
                setError(error.message);
            } finally {
                setLoading(false);
            }
        }

        timeout = setTimeout(fetchData, 300);
    }, [url, str, data, callback, value])
}

export {QueryFetch}

and this is what I'm trying to do

const [cities, setCities] = useState({})

{
      key: 'city',
      label: 'City',
      widget: 'select',
      options: country ? citites : [],
      placeholder: loading ? 'Loading...' : 'Select city...',
      widgetProps: { loading },
      disabled: loading || !country,
      widgetProps: {
                                options: cities.map(
                                    c => <Option key={f.id} value={f.name}>{f.name}</Option>
                                ),
                                showSearch: true,
                                onSearch: () => {
                                    console.log("Entered search")
                                    QueryFetch(
                                        "http://localhost:8000/cities/",
                                        form.getFieldsValue("country"),
                                        null,
                                        data => setCities(data)
                                    )
                                },
                            }
    },

Nest for values as array

Unable to use nested array values.
https://codesandbox.io/s/autumn-butterfly-8tubs?file=/src/DynamicFields.js

Example. If we are trying to nest as objs
if we take A.B and A.C as 2 keys in the form with both being inputs, and have submit the form then we get the result as

A: {
    B: 'value B',
    C: 'value C`
}

But unable to do the same for array,

A[0].B and A[1].C are the keys and while submitting.

{
    A[0]: {
        B: "value B"
    },
    A[1]: {
        C: 'value C',
    }
}

Instead of being this way

{
    A: [
        B: "value B",
        C: "value C"
    ]
}

Import Icon

HI, i just switch to [email protected] and i got this error

./node_modules/antd-form-builder/dist/index.es.js
Attempted import error: 'Icon' is not exported from 'antd'.

And i'm using [email protected]

How can i fix it ?

Thanks

useForceUpdate does not exists on type

I got this error below after updating to the latest version, may I know if there any fix for the TypeScript?

Error

Type error: Property 'useForceUpdate' does not exist on type 'FC<FormBuilderInterface> & { defineWidget: (key: string, component: any, metaConvertor?: (field: FieldType) => FieldType) => void; }'.

const [form] = Form.useForm();
const forceUpdate = FormBuilder.useForceUpdate();
                               ^

Usage

const [form] = Form.useForm();
const forceUpdate = FormBuilder.useForceUpdate();

Temporary solution (Quick Fix)
Anyone facing this issue can actually pin the version to 2.1.2 since version 2.1.3 only added the TypeScript feature.

Uncaught TypeError: form.getFieldsValue is not a function

"antd": "^4.16.13"


    const personalInfo = {
      name: { first: 'Nate', last: 'Wang' },
      email: '[email protected]',
      gender: 'Male',
      dateOfBirth: moment('2100-01-01'),
      phone: '15988888888',
      city: 'Shanghai',
      address: 'No.1000 Some Road, Zhangjiang Park, Pudong New District',
    }

    const meta = {
      columns: 2,
      fields: [
        { key: 'name.first', label: 'First Name' },
        { key: 'name.last', label: 'Last Name' },
        { key: 'gender', label: 'Gender' },
        {
          key: 'dateOfBirth',
          label: 'Date of Birth',
        },
        { key: 'email', label: 'Email' },
        { key: 'phone', label: 'Phone' },
        { key: 'address', label: 'Address', colSpan: 2 },
        { key: 'city', label: 'City' },
        { key: 'zipCode', label: 'Zip Code' },
      ],
    }

render () {

return (
 <Drawer
        visible={true}
        footer={
          <div style={{ textAlign: 'left' }}>
            <Button onClick={onClose} style={{ marginRight: 8 }}>
              Close
            </Button>
          </div>
        }
      >
        <div>
          <FormBuilder meta={meta} form={this.formRef} initialValues={personalInfo} viewMode />

        </div>

      </Drawer>
    );

  }

getFieldValue not working with custom component

I'm writing a page for creating an opinion about a particular thing and I'd like it to be a multi-step form with coordinated Select components, each querying different REST API endpoint. I tried to combine several things: antd's Search Box as well as antd-form-builder's Wizard. However, from what I can understand from the source code of antd-form-builder, onSearch is not supported in the select components so I had to make my own custom one that would be able to query REST API. Currently, I have the following code (this is essentially a copy-paste of the Wizard example linked above with a custom search box component from antd website linked above):

import React, {useState, useCallback} from "react";
import FormBuilder from "antd-form-builder";
import {Form, Button, Steps, Select} from 'antd'
import querystring from "querystring";
import axios from "axios";
import {api} from "../api";

const {Option} = Select;
const {Step} = Steps

let timeout;
let currentValue;

function fetch(url, value, query, callback) {
    if (timeout) {
        clearTimeout(timeout);
        timeout = null;
    }
    currentValue = value;

    async function getData() {
        if (currentValue === value) {
            const str = querystring.encode({
                name: value,
                q: query,
            });
            const res = await axios.get(`${url}?${str}`);
            callback(res.data);
        }
    }

    timeout = setTimeout(getData, 500);
}

const SearchInput = ({url, query, ...props}) => {
    const [data, setData] = useState([]);
    const [value, setValue] = useState(props.initialValue);

    const handleSearch = value => {
        if (value) {
            console.log(query)
            fetch(url, value, query, result => {
                setData(result);
            });
        } else {
            setData([]);
        }
    };

    const handleChange = value => {
        setValue(value)
    };

    return (
        <Select
            showSearch
            value={value}
            defaultValue={props.defaultValue? props.defaultValue : undefined}
            disabled={props.disabled}
            showArrow={true}
            style={{fontSize: "large"}}
            filterOption={false}
            onSearch={handleSearch}
            onChange={handleChange}
        >
            {data.map(d => <Option style={{fontSize: "large"}} key={d.id} value={d.name}>{d.name}</Option>)}
        </Select>
    );
}

FormBuilder.defineWidget('search-input', SearchInput)

const OpinionsCreate = () => {
    const [form] = Form.useForm()
    const [currentStep, setCurrentStep] = useState(0)
    const forceUpdate = FormBuilder.useForceUpdate()
    const handleFinish = useCallback(() => {
        console.log('Submit: ', form.getFieldsValue(true))
    }, [form])

    const wizardMeta = {
        steps: [
            {
                title: 'Strona wysyłająca',
                formMeta: {
                    columns: 1,
                    fields: [
                        {
                            key: 'sending_university',
                            required: true,
                            disabled: true,
                            widget: "search-input",
                            widgetProps: {
                                initialValue: 'Some university',
                                url: api.endpoint1,
                                defaultValue: "Some university",
                                onChange: () => {
                                    // Clear sending_faculty value when country is changed
                                    form.setFieldsValue({sending_faculty: undefined})
                                },
                            }
                        },
                        {
                            key: 'sending_faculty',
                            required: true,
                            widget: "search-input",
                            widgetProps: {
                                url: api.endpoint2,
                                query: form.getFieldValue("sending_university"),
                            }
                        },
                    ],
                },
            },
        ]
    }

    // Clone the meta for dynamic change
    const newWizardMeta = JSON.parse(JSON.stringify(wizardMeta))
    // In a wizard, every field should be preserved when swtich steps.
    newWizardMeta.steps.forEach(s => s.formMeta.fields.forEach(f => (f.preserve = true)))

    // Generate a general review step
    const reviewFields = []
    newWizardMeta.steps.forEach((s, i) => {
        reviewFields.push(
            {
                key: 'review' + i,
                colSpan: 2,
                render() {
                    return (
                        <fieldset>
                            <legend>{s.title}</legend>
                        </fieldset>
                    )
                },
            },
            ...s.formMeta.fields,
        )
    })

    newWizardMeta.steps.push({
        key: 'review',
        title: 'Podsumowanie',
        formMeta: {
            columns: 2,
            fields: reviewFields,
        },
    })

    const stepsLength = newWizardMeta.steps.length

    const handleNext = () => {
        form.validateFields().then(() => {
            setCurrentStep(currentStep + 1)
        })
    }
    const handleBack = () => {
        form.validateFields().then(() => {
            setCurrentStep(currentStep - 1)
        })
    }
    const isReview = currentStep === stepsLength - 1

    return (
        <Form
            layout="horizontal"
            form={form}
            onValuesChange={forceUpdate}
            style={{width: '100%'}}
            onFinish={handleFinish}
        >
            <Steps current={currentStep}>
                {newWizardMeta.steps.map(s => (
                    <Step key={s.title} title={s.title}/>
                ))}
            </Steps>
            <div style={{background: '#f7f7f7', padding: '20px', margin: '30px 0'}}>
                <FormBuilder
                    viewMode={currentStep === stepsLength - 1}
                    form={form}
                    meta={newWizardMeta.steps[currentStep].formMeta}
                />
            </div>
            <Form.Item className="form-footer" style={{textAlign: 'right'}}>
                {currentStep > 0 && (
                    <Button onClick={handleBack} style={{float: 'left', marginTop: '5px'}}>
                        Back
                    </Button>
                )}
                <Button>Cancel</Button>&nbsp; &nbsp;
                <Button type="primary" onClick={isReview ? () => form.submit() : handleNext}>
                    {isReview ? 'Submit' : 'Next'}
                </Button>
            </Form.Item>
        </Form>
    )
}

export default OpinionsCreate;

There are several things going on here

  1. the first select has a default value, and I'd like this select to be disabled such that this default value couldn't be changed (I don't exactly know how I can achieve this, but I tried)
  2. the second select (in theory) should access the value in the first select, and query the backend endpoint api.endpoint2, which is a URL, with an additional ?q=... parameter, such that only certain options would be returned (the second select is coordinated)
  3. In the SearchInput there is a handleSearch method that fires whenever the user types something in the text input. The backend is queried such that the value from the first select is taken into account as well as the text typed by the user is contained in the retrieved options.

The custom search component is working alright itself, however, I stumbled upon several issues:

  1. I can't get the value from the first select by using form.getFieldValue("sending_university") so the second select queries just entire table in the database which is not what I wanted. It somehow doesn't recognize that there's an initial value in the first component.
  2. Even when I add a third select component, dependent on the second one, it doesn't recognize the value selected in the second component either.
  3. When I try to continue to the next step, validation fails, claiming that all fields' values are empty, even the first one with the default initial value.

I tried many combinations all to no avail. I'm pretty sure, what I want is doable but I'm already short of ideas. Does anyone see what's the issue and can help me out?

InitialValues not working anymore

HI,

I just switch from Antd@3 to Antd@4 and i upgrade your module.

I change my code to fit new specs but when I provide initialValues, there are not more informations in my fields.

Input.Group compact support?

How to implement a case when one field consists of two fields?
In plain antd form I would do something like this:

    <Input.Group compact>
      <Input style={{ width: '20%' }} defaultValue="0571" />
      <Input.Search style={{ width: '30%' }} defaultValue="26888888" />
    </Input.Group>

But how to do it with antd-form-builder?

Antd <ConfigProvider /> Support?

Creating forms with FormBuilder Component is a breeze but it seems to not take advantage of ant's ConfigProvider component props/settings. I'm using locale prop with and it translates every other every component but FormBuilder generated forms.

There are any workaround for this?


That's my Wrapper:

import ptBR from "antd/lib/locale/pt_BR";

const AdminWrapper = (props) => {
  message.config({ maxCount: 1 });

  return (
    <ConfigProvider locale={ptBR}>
        <NewChildren {...props} />
    </ConfigProvider>
  );
};

FormBuilder example:

<Form
    form={form}
    layout="vertical"
    onFinish={handleFinish}
    validateMessages={validateMessages}
>
    <Tabs tabPosition="top">
       <Tabs.TabPane tab="Usuario" key="1">
          <FormBuilder form={form} meta={meta1} initialValues={...} />
        </Tabs.TabPane>

        <Tabs.TabPane tab="Filiacao" key="2">
           <FormBuilder form={form} meta={meta2} initialValues={...} />
        </Tabs.TabPane>
    </Tabs>
</Form>

Validation Message object used as

prop:

export default {
  required: "Campo obrigatorio",

  types: {
    email: "E-mail invalido",
    number: "Numero invalido"
  }
};

colSpan support responsive config

currently colSpan is only support fixed number config.
Is it possible to support responsive config ? like
colSpan = { sm: 1, md: 2, }

Set formItemLayout, but displaying form as vertical.

Dear supnate,

Thank you for sharing this antd-form-builder!

I'm trying to adjust the column width using formItemLayout.
As in the API Reference, the default value of formItemLayout is [8, 16]. I tried to set it as

const meta = {
      formItemLayout:[6, 18],
      fields: [{...}]
}

Or

const meta = {
     formItemLayout:{
        labelCol: { span: 8 },
        wrapperCol: { span: 16 },
      },
      fields: [{...}]
}

But the form layout will change to vertical, not displaying as horizontal form.

Could you give a working example of set the column width?

Thank you very much!

how do I get access to the data??

Hello, I am sooo confused by the example, I simply want to have access to the data and all I get is Object object...

What am I doing wrong?

Although it shows in the console.log, I tried so many ways and spent so many hours to get access to the data. How can I do it?

Yours,

here is my codesandbox : https://codesandbox.io/s/elastic-gates-ncib1?file=/src/App.js

in CodeSandbox, I also get this in the console:

Warning: findDOMNode is deprecated in StrictMode. findDOMNode was passed an instance of Wave which is inside StrictMode. Instead, add a ref directly to the element you want to reference. Learn more about using refs safely here: https://fb.me/react-strict-mode-find-node
in button (created by Button)
in Wave (created by Button)
in Button (at App.js:21)
in div (created by FormItemInput)
in div (created by FormItemInput)
in div (created by Context.Consumer)
in Col (created by FormItemInput)
in FormItemInput (created by FormItem)
in div (created by Context.Consumer)
in Row (created by FormItem)
in FormItem (at App.js:20)
in form (created by ForwardRef(Form))
in ForwardRef(Form) (created by ForwardRef(InternalForm))
in SizeContextProvider (created by ForwardRef(InternalForm))
in ForwardRef(InternalForm) (at App.js:18)
in Unknown (at src/index.js:9)
in StrictMode (at src/index.js:8)

Tabs support

Hi,

I just discover you package and it seems to be very helpful ! It's exactly what I'm looking for !

I saw we can set Wizard form and it's very fine, and I think i will be a great idea to support Tabs too !

Thanks for your work !

Default error message for required fields

antd-form-builder is providing a hardcoded error message which is not localized. It should leave that to Ant Design so the users can define their own default error messages using Ant Design's ConfigProvider.

TS Error : Property 'useForm' does not exist on type 'FC<FormBuilderInterface>

just installed and copied example code

npm install --save-dev antd-form-builder

import React from 'react'
import { Form, Button } from 'antd'
import FormBuilder from 'antd-form-builder'

export default () => {
  const [form] = FormBuilder.useForm()
  const meta = {
    fields: [
      { key: 'username', label: 'User Name' },
      { key: 'password', label: 'Password', widget: 'password' },
    ],
  }

  return (
    <Form form={form}>
      <FormBuilder meta={meta} form={form} />
      <Form.Item wrapperCol={{ span: 16, offset: 8 }}>
        <Button type="primary">Login</Button>
      </Form.Item>
    </Form>
  )
}

demo request : Add new field

Hello,

Could you show an example where we can push a new item to an array in the form:

ex:
"Add New Fruit:" this a a new fruit a i am adding to the list ||| "Press button to Add Another fruit "

so we could add new values to an array dynamically.

Yours,

Marc

Embed icon

Hi,

Is there any way to embed an icon , like this vanilla antd example?

<Input prefix={<Icon type="user" style={{ color: 'rgba(0,0,0,.25)' }} />}
             placeholder="Username"  />

from https://ant.design/components/form

Import methods from lodash for tree-shaking

I've noticed FormBuilder.js has import _ from 'lodash' - this will import all of lodash.
You might want to do things like this to enable tree-shaking.

import memoize from 'lodash/memoize;
import isArray from 'lodash/isArray;
import pick from 'lodash/pick;
import capitalize from 'lodash/capitalize;
import has from 'lodash/has;
import get from 'lodash/get;
import find from 'lodash/find;

then just replace _. with ''

https://www.azavea.com/blog/2019/03/07/lessons-on-tree-shaking-lodash/
https://stackoverflow.com/questions/35250500/correct-way-to-import-lodash

In wizard, how do you add label and dynamic fields?

From your Wizard example, I am trying to add a dynamic form.
In my form when i click a checkbox, it successfully creates the label and field but I get the error:
index.js:1437 Warning: Each child in a list should have a unique "key" prop. See https://fb.me/react-warning-keys for more information

My code:

if (form.getFieldValue('random') === true) {

var keys = [
  {
    key: 'random-label',
    colSpan: 2,
    render() {
      return (
        <fieldset>
          <legend>Random Header</legend>
        </fieldset>
      )
    },
  },
  {
    key: 'random-field', 
    label: 'Random', 
    //required: true 
  },
];

keys.forEach(key => {
  newWizardMeta.steps[1].formMeta.fields.push(key);
});
}

Adding new components like antd 4 Upload

Antd version 4 components are basically added on Antd-Form-Builder Rekit. But, other components like photo upload added as a custom component. Form Builder deletes all duplicated codes on so many forms. For the eliminating custom components, this issue opened for adding antd 4 Upload component like as select, input ext..
https://ant.design/components/upload/

表单数据如何反显

调用, form.setFieldsValue 报错:
Warning: You cannot set a form field before rendering a field associated with the value.
请问有没有相关api, 可以反显表单数据

can support upload?

i use antd upload in formbuilder, it is ok when upload's props fileList is undefined, but if give an empty array, the upload works bad, uploaded image cann't display. thanks

Cannot pass formatter to Input

I would like to use formatter and parser function to <Input> or <InputNumber /> components, but can't figure out how to pass those functions.
I've tried adding them to widgetProps, or getValueProps but that didn't seem to work.
Thanks!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.