--- tags: zeta-dom-react --- # Data object and array ==Since `v0.4.0`== Nested data objects and arrays are supported since v0.4. Field components can be associated with nested data properties using `FormObject` and `FormArray` component. ```typescript interface FormData { name: string; phone: { countryCode: string; phoneNumber: string; }; } function Form() { const form = useFormContext<FormData>(); return ( <Form context={form}> <TextField name="name" /> <FormObject name="phone"> <TextField name="countryCode" /> <TextField name="phoneNumber" /> </FormObject> </Form> ); } ``` ## Field component for data objects ```typescript interface PhoneData { countryCode: string; phoneNumber: string; } function PhoneField(props: FormFieldProps<PhoneData>) { const { value } = useFormField(props, {}); return ( <div> <FormObject value={value}> <TextField name="countryCode" /> <TextField name="phoneNumber" /> </FormObject> </div> ); } function Form() { const form = useFormContext<FormData>(); return ( <Form context={form}> <TextField name="name" /> <PhoneField name="phone" /> </Form> ); } ``` ## Data array ```typescript interface Item { id: string; name: string; } interface FormData { items: Item[]; } function Form() { const form = useFormContext<FormData>(); return ( <Form context={form}> <FormArray name="items"> {items => item.map(v => ( // important to provide key for each mapped element <FormObject key={FormObject.keyFor(v)} value={v}> <div> <TextField name="id" /> <TextField name="name" /> </div> </FormObject> ))} </FormArray> </Form> ); } ``` Or by having a composite field component simply: ```typescript function Form() { const form = useFormContext<FormData>(); return ( <Form context={form}> <FormArray name="items"> {items => item.map((v, i) => ( <Field key={FormObject.keyFor(v)} name={i} /> ))} </FormArray> </Form> ); } ``` ## Data path Events and operation like validations on nested data properties can be achieved like normal data properties with dot-separated data paths. ```typescript function Form() { const form = useFormContext<FormData>(); useEffect(() => { return form.on('dataChange', function (e) { console.log(e.data); }); }, [form]); return ( <Form context={form}> <TextField name="name" /> <FormObject name="phone"> <TextField name="countryCode" /> <TextField name="phoneNumber" /> </FormObject> </Form> ); } ``` For instance, the above `dataChange` event handler will log `["phone.countryCode"]` when user fills in the country code. ### Validate nested properties To validate a particular data property, provide the data path to that property like: ```typescript form.validate('phone.countryCode') ``` You can also supply the root object data path to validate all child properties: ```typescript form.validate('phone') // same as form.validate('phone.countryCode', 'phone.phoneNumber') ``` ### Access data with data path Other than accessing like ordinary JavaScript objects, `FormContext` provides `getValue` and `setValue` method to access values providing a data path. ```typescript const countryCode = form.getValue('phone.countryCode'); form.setValue('phone.countryCode', '1'); ``` :::warning Note that for data objects and array, `getValue` will return a deeply-cloned object; while `setValue` will deeply clone the supplied object. :::