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()
}
}
```