JQuery Form Render === Manage form easily which: - Import custom form control easily as Component. - support complex data structure. - Directive way to build form. ## Index [TOC] ## Getting Started ```javascript import Form from '.../Common/Form/ateam.admin.form' // container: JQuery element let form = new Form(container) // formTemplate: a string to describe form html form.render(formTemplate).done(()=>{ //any initialization here... }) //to submit form form.submit().done(data => { ...... }).fail(error => { //validation fail }) ``` ## Form Component There are several basic components build-in: - TextInput - Checkbox - TextareaInput - SelectInput and you can also create your own component, then import it: ```javascript form.render(template,{ MyComponent:myComponent }) ``` and use it: ```xml <div> <MyComponent name="fieldName"/> </div> ``` ## Html Template `form.render` accept plain html string like: ```xml <div class="flex"> <TextInput name="someValue" defaultvalue="123" class="hide" readonly="readonly" type="text"/> <TextInput name="fillme" class="form-control input-medium" type="text"/> <Checkbox name="isTemplate" style="margin-left:40px;" /> </div> <OnError name="someValue" type="required"> <span class="color-red-2">'someValue' is required!</span> </OnError> ``` when submit, assume each fields filled, the data will be: ```javascript { someValue:'123', fillme:'fillme' isTemplate:true } ``` :::warning the property will not exist if the value input was empty ::: ## Access the data You can access data as **promise way** via `form.submit()`, or just access `form.data` which equivalent to `form.getData()`. Besides, it support **path** to describe complex data structure: ```xml <div class="flex"> <TextInput name="a.b.c" defaultvalue="123" class="hide" readonly="readonly" type="text"/> <TextInput name="a.b.d" class="form-control input-medium" type="text"/> <Checkbox name="isTemplate" style="margin-left:40px;"/> <TextInput name="displayName[0].value" class="form-control input-medium" type="text"/> </div> <OnError name="a.b.c" type="required"> <span class="color-red-2">'a.b.c' is required!</span> </OnError> ``` then the data will be like this: ```javascript { a:{ b:{ c:123, d:'someValue' } }, isTemplate:true, displayName:[ { value:'the name' } ] } ``` which means you can also fill the form with the raw data directly: ```javascript form.setData({ a:{ b:{ c:123, d:'someValue' } }, isTemplate:true, displayName:[ { value:'the name' } ] }) ``` ## Validation You can assign validator in `constructor`: ```javascript let form = new Form(container, validator) ``` or set them latter: ```javascript form.validator = validator ``` Trigger it via ```javascript form.validateAll() ``` or only one field ```javascript form.validate(fieldName) ``` ### Validator Validator is an object to describe how to validate, if we want to validate : **name**, **amount**, **notes**: ```javascript let validator = { name:{ length: v => v === undefined || v.length < 20 }, amount:{ positive: v => v === undefined || v >= 0 }, notes:{ required:true, length: v => v === undefined || v.length < 200 } } ``` The property value should be a **functio**n to return the input value **is valid(true) or not(false)**, except `required` validator; It's a **built-in validator** which accept `string`, `boolean`, `function` as: | Type | Description | |------|-------------| |boolean|the value cannot be empty if `true`| |field name (string)| the value cannot be empty if the value of field was `true`| |function|the value cannot be empty if the function return was `true`| ### Error result When submit reject, you will receive the error object which describe the status of validation, or access `form.error` after validation. The structure is similiar to ==validator== : ```javascript { name:{ length:true }, notes:{ required:true } } ``` which means the validation of ==length== was failed on field ==name==, and also ==required== failed on field ==notes== too. In the meantime, if you have declare matched **OnError** in your template ```xml <OnError name="notes" type="required"> <span class="color-red-2">'notes' is required!</span> </OnError> ``` It will be display too. ## Methods >#### `show([names,], show = true)` >display the component or not, you can manipulate a field: `form.show('name')` >or mutiple fields: `form.show(['name','phone','address'])` >[color=#3598dc] >#### `enable([names,], enable = true)` >enable the control or not >[color=#3598dc] >#### `obsolete([names,], obsolete = true)` >deactivate the field, the input will be hide, and it's value will be set to undefined, which means you will not get it when fetching data. >[color=#3598dc] >#### `getValue(name) : any` >return the field value which deponds on input control implementation. >[color=#3598dc] >#### `setValue(name, value)` >[color=#3598dc] >#### `setData(data)` >[color=#3598dc] >#### `getData(mapper = {}) : object` >you can use `mapper` to customsize the data structure like: >[color=#3598dc] ```javascript let mapper = { name:'displayName[0].value' } ///the output data will be { displayName:[ { value:'the value from name' } ] } ``` >#### `submit(mapper = {}) : promise` >[color=#3598dc] >#### `validate(name) : boolean` >`true` : **valid** >`false` : **invalid** >[color=#3598dc] >#### `validateAll() : boolean` >`true` : **valid** >`false` : **invalid** >[color=#3598dc] >#### `event(event, name, handler)` >Bind event listener to the control instance. >[color=#3598dc] ```javascript form.event('onChange', 'mySelect', v => { console.log(v + ' selected!') }) ``` >#### `find(selector) : JQuery element` >As JQuery `find()` >[color=#3598dc] >#### `method([names,], methodName, ...parms)` >An interface to call custom method of component. >[color=#3598dc] >#### `options(name, [options])` >A syntax sugar for **select-like** component to setup it's options. >Effects nothing on other components. >[color=#3598dc] >#### `reset()` >Clear all form value. >[color=#3598dc] >#### `field(name) : object` >Get the component instance. >[color=#3598dc] ## L20n If you want to pass l20n value into props, please use tag `L20N` to wrap in template to describe: ```xml= <L20N id="your-l20n-id" target="label"> <MyComponent name="checkMe" label="your-l20n-args"/> </L20N> ``` use `target` to indicate which props you want to translate. The default l20-args will be `{"id":"somevalue"}`, for more customize you can add `args` in `L20N`: ```xml= <L20N id="your-l20n-id" target="label"> <args id="argName">value</args> <MyComponent name="checkMe" /> </L20N> ``` ## Create Component ```javascript import BaseInput from 'form-render' export default class MyComponent extends BaseInput{ constructor(props){ super(props) this.$e = $(`create component DOM here, and render attributes use ${this.renderAttr()}`) } //overwrite the method you need. } ``` You can overwrite the following methods: >#### `getValue()` >[color=#3598dc] >#### `setValue(value)` >[color=#3598dc] >#### `enable(enable)` >[color=#3598dc] >#### `show(show)` >[color=#3598dc] >#### `clear()` >[color=#3598dc] and if you want to do somthig after render, please use >#### `componentDidMount(dfd)` >[color=#3598dc] to do something after rendering. :::warning you must call `dfd.resolve()` to end the rendering stage if `componentDidMount` declared. ::: ```javascript export default class MyComponent extends BaseInput{ constructor(props){ super(props) this.$e = $(`create component DOM here, and render attributes with ${this.renderAttr()}`) } componentDidMount(dfd){ //do stuff here... dfd.resolve() } } ```