Merge pull request #80 from houdiniproject/reactify_inputs

Making inputs more natural in React
This commit is contained in:
Eric Schultz 2018-07-12 12:29:24 -05:00 committed by GitHub
commit 1a779f48d4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 384 additions and 87 deletions

View file

@ -5,15 +5,16 @@ import {Field} from "../../../../types/mobx-react-form";
import LabeledFieldComponent from "./LabeledFieldComponent"; import LabeledFieldComponent from "./LabeledFieldComponent";
import {injectIntl, InjectedIntl} from 'react-intl'; import {injectIntl, InjectedIntl} from 'react-intl';
import {HoudiniField} from "../../lib/houdini_form"; import {HoudiniField} from "../../lib/houdini_form";
import ReactInput from "./form/ReactInput";
export const BasicField = injectIntl(observer((props:{field:Field, intl?:InjectedIntl, wrapperClassName?:string}) =>{ export const BasicField = observer((props:{field:Field, placeholder?:string, label?:string, wrapperClassName?:string}) =>{
let field = props.field as HoudiniField let field = props.field as HoudiniField
return <LabeledFieldComponent return <LabeledFieldComponent
inputId={props.field.id} labelText={field.label} inError={field.hasError} error={field.error} inputId={props.field.id} labelText={field.label} inError={field.hasError} error={field.error}
inStickyError={field.hasServerError} stickyError={field.serverError} inStickyError={field.hasServerError} stickyError={field.serverError}
className={props.wrapperClassName} > className={props.wrapperClassName} >
<input {...props.field.bind()} placeholder={field.placeholder ? props.intl.formatMessage({id:field.placeholder}) : undefined} className="form-control"/> <ReactInput field={field} label={props.label} placeholder={props.placeholder} className="form-control"/>
</LabeledFieldComponent> </LabeledFieldComponent>
})) })

View file

@ -0,0 +1,49 @@
// License: LGPL-3.0-or-later
import * as React from 'react';
import { observer, Provider } from 'mobx-react';
import {Field, Form} from "mobx-react-form";
import {observable, action, toJS} from 'mobx';
export interface ReactFormProps
{
form:Form
}
///Mostly useless class but, at some point, will replace all our form elements
@observer
export class ReactForm extends React.Component<ReactFormProps, {}> {
@observable
form:Form
@action.bound
componentWillMount()
{
this.form = this.props.form
}
componentDidUpdate(){
}
componentWillUnmount(){
}
render() {
return <form >
{this.props.children}
</form>
}
}

View file

@ -0,0 +1,164 @@
// License: LGPL-3.0-or-later
import * as React from 'react';
import 'jest';
import ReactInput from './ReactInput'
import {Form} from "mobx-react-form";
import {mount} from 'enzyme';
import {toJS, observable, action, runInAction} from 'mobx';
import {observer} from 'mobx-react';
import {InputHTMLAttributes} from 'react';
import {ReactForm} from "./ReactForm";
@observer
class TestChange extends React.Component{
@observable
remove:boolean
@observable
form: Form
@action.bound
componentWillMount(){
this.form = new Form({fields:[{
name: 'name',
extra: null}
]})
}
@action.bound
onClick(){
this.remove = true
}
render() {
let reactInput = !this.remove ? <ReactInput field={this.form.$('name')} label={'label1'} placeholder={"holder"}>
{this.props.children}
</ReactInput> : undefined
return <ReactForm form={this.form}>
{reactInput}
<button onClick={() => this.onClick()}/>
</ReactForm>
}
}
class WrappedInput extends React.Component<InputHTMLAttributes<HTMLInputElement>>{
render(){
let notChildren = {...this.props}
delete notChildren.children
return <div>
<input {...notChildren} />
</div>
}
}
describe('ReactInput', () => {
let form: Form
beforeEach(() => {
form = new Form({
fields: [
{
name: 'name',
extra: null
}
]
})
})
describe('no children passed in', () => {
test('gets added properly', () => {
let res = mount(<ReactForm form={form}>
<ReactInput field={form.$('name')} label={"label"}
placeholder={"holder"} value={'snapshot'} aria-required={true}/>
</ReactForm>)
//Did the attributes settings work as expected back to the objects
expect(form.$('name').label).toEqual('label')
expect(form.$('name').placeholder).toEqual('holder')
expect(form.$('name').value).toEqual('')
//is the aria attribute passted through to the input
let input = res.find('input')
expect(input.prop('aria-required')).toEqual(true)
// is the input properly bound?
input.simulate('change', {target: { value: 'something' } })
expect(form.$('name').value).toEqual('something')
})
test('gets removed properly', () => {
let res = mount(<TestChange/>)
// The two casts are needed because Typescript was going blowing up without the 'any' first.
// Why was it? *shrugs*
let f = res.find('ReactForm').instance() as any as ReactForm
expect(f.form.size).toEqual(1)
res.find('input').simulate('change', {target: { value: 'something' } })
expect(f.form.$('name').value).toEqual('something')
res.find('button').simulate('click')
expect(f.form.size).toEqual(1)
expect(toJS(res.find('form'))).toMatchSnapshot()
expect(f.form.$('name').label).toEqual('label1')
expect(f.form.$('name').placeholder).toEqual('holder')
})
})
describe('children passed in', () => {
test('gets added properly', () => {
let res = mount(<ReactForm form={form}>
<ReactInput field={form.$('name')} label={"label"}
placeholder={"holder"} value={'snapshot'} aria-required={true}>
<WrappedInput/>
</ReactInput>
</ReactForm>)
//Did the attributes settings work as expected back to the objects
expect(form.$('name').label).toEqual('label')
expect(form.$('name').placeholder).toEqual('holder')
expect(form.$('name').value).toEqual('')
//is the aria attribute passted through to the input
let input = res.find('input')
expect(input.prop('aria-required')).toEqual(true)
// is the input properly bound?
input.simulate('change', {target: { value: 'something' } })
expect(form.$('name').value).toEqual('something')
})
test('gets removed properly', () => {
let res = mount(<TestChange>
<WrappedInput/>
</TestChange>)
let f = res.find('ReactForm').instance() as any as ReactForm
res.find('input').simulate('change', {target: { value: 'something' } })
expect(f.form.$('name').value).toEqual('something')
expect(f.form.size).toEqual(1)
res.find('button').simulate('click')
expect(f.form.size).toEqual(1)
expect(f.form.$('name').label).toEqual('label1')
expect(f.form.$('name').placeholder).toEqual('holder')
})
})
})

View file

@ -0,0 +1,90 @@
// License: LGPL-3.0-or-later
import * as React from 'react';
import { observer, inject, Provider } from 'mobx-react';
import {InjectedIntlProps, injectIntl} from 'react-intl';
import {Field} from "mobx-react-form";
import {observable, action, toJS, runInAction} from 'mobx';
import {InputHTMLAttributes} from 'react';
export interface ReactInputProps
{
field:Field
label?:string
placeholder?:string
}
function castToNullIfUndef(i:any){
return i === undefined ? null : i
}
class ReactInput extends React.Component<ReactInputProps & InputHTMLAttributes<HTMLInputElement>, {}> {
constructor(props:ReactInputProps){
super(props)
}
@observable
field:Field
@action.bound
componentWillMount(){
this.field = this.props.field
this.updateProps()
}
componentWillUnmount(){
}
componentDidUpdate(prevProps: Readonly<ReactInputProps>, prevState: Readonly<{}>): void {
this.updateProps()
}
@action.bound
updateProps() {
this.field.set('label', castToNullIfUndef(this.props.label))
this.field.set('placeholder', castToNullIfUndef(this.props.placeholder))
}
@action.bound
renderChildren(){
let ourProps = this.winnowProps()
let elem = React.cloneElement(this.props.children as React.ReactElement<any>,
{...ourProps, ...this.field.bind() })
return elem
}
///Removes the properties we don't want to put into the input element
@action.bound
winnowProps(): ReactInputProps & InputHTMLAttributes<HTMLInputElement> {
let ourProps = {...this.props}
delete ourProps.field
delete ourProps.value
return ourProps
}
render() {
if (this.props.children)
{
return this.renderChildren()
}
else {
return <input {...this.winnowProps()} {...this.field.bind()}/>
}
}
}
export default observer(ReactInput)

View file

@ -0,0 +1,9 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`ReactInput no children passed in gets removed properly 1`] = `
<form>
<button
onClick={[Function]}
/>
</form>
`;

View file

@ -17,47 +17,31 @@ export interface NonprofitInfoFormProps
export const FieldDefinitions : Array<FieldDefinition> = [ export const FieldDefinitions : Array<FieldDefinition> = [
{ {
name: 'organization_name', name: 'organization_name',
label: 'registration.wizard.nonprofit.name.label',
placeholder: 'registration.wizard.nonprofit.name.placeholder',
type: 'text',
validators: [Validations.isFilled] validators: [Validations.isFilled]
}, },
{ {
name: 'website', name: 'website',
label: 'registration.wizard.nonprofit.website.label',
placeholder: 'registration.wizard.nonprofit.website.placeholder',
validators: [Validations.optional(Validations.isUrl)] validators: [Validations.optional(Validations.isUrl)]
}, },
{ {
name: 'org_email', name: 'org_email',
label: 'registration.wizard.nonprofit.email.label',
placeholder: 'registration.wizard.nonprofit.email.placeholder',
validators: [Validations.optional(Validations.isEmail)] validators: [Validations.optional(Validations.isEmail)]
}, },
{ {
name: 'org_phone', name: 'org_phone',
label: 'registration.wizard.nonprofit.phone.label',
placeholder: 'registration.wizard.nonprofit.phone.placeholder',
type: "tel" type: "tel"
}, },
{ {
name: 'city', name: 'city',
label: 'registration.wizard.nonprofit.city.label',
placeholder: 'registration.wizard.nonprofit.city.placeholder',
validators: [Validations.isFilled] validators: [Validations.isFilled]
}, },
{ {
name: 'state', name: 'state',
label: 'registration.wizard.nonprofit.state.label',
placeholder: 'registration.wizard.nonprofit.state.placeholder',
type: 'text',
validators: [Validations.isFilled] validators: [Validations.isFilled]
}, },
{ {
name: 'zip', name: 'zip',
label: 'registration.wizard.nonprofit.zip.label',
placeholder: 'registration.wizard.nonprofit.zip.placeholder',
validators: [Validations.isFilled] validators: [Validations.isFilled]
} }
] ]
@ -67,17 +51,33 @@ class NonprofitInfoForm extends React.Component<NonprofitInfoFormProps & Injecte
render() { render() {
return <fieldset > return <fieldset >
<BasicField field={this.props.form.$("organization_name")}/> <BasicField field={this.props.form.$("organization_name")}
<BasicField field={this.props.form.$('website')}/> label={this.props.intl.formatMessage({id:'registration.wizard.nonprofit.name.label' })}
placeholder={this.props.intl.formatMessage({id: 'registration.wizard.nonprofit.name.placeholder'})}
/>
<BasicField field={this.props.form.$('website')}
label={this.props.intl.formatMessage({id: 'registration.wizard.nonprofit.website.label'})}
placeholder={this.props.intl.formatMessage({id: 'registration.wizard.nonprofit.website.placeholder'})}/>
<TwoColumnFields> <TwoColumnFields>
<BasicField field={this.props.form.$('org_email')}/> <BasicField field={this.props.form.$('org_email')}
<BasicField field={this.props.form.$('org_phone')}/> label={this.props.intl.formatMessage({id: 'registration.wizard.nonprofit.email.label'})}
placeholder={this.props.intl.formatMessage({id: 'registration.wizard.nonprofit.email.placeholder'})}/>
<BasicField field={this.props.form.$('org_phone')}
label={this.props.intl.formatMessage({id: 'registration.wizard.nonprofit.phone.label'})}
placeholder={this.props.intl.formatMessage({id: 'registration.wizard.nonprofit.phone.placeholder'})}/>
</TwoColumnFields> </TwoColumnFields>
<ThreeColumnFields> <ThreeColumnFields>
<BasicField field={this.props.form.$('city')}/> <BasicField field={this.props.form.$('city')}
<BasicField field={this.props.form.$('state')}/> label={this.props.intl.formatMessage({id: 'registration.wizard.nonprofit.city.label'})}
<BasicField field={this.props.form.$('zip')}/> placeholder={this.props.intl.formatMessage({id: 'registration.wizard.nonprofit.city.placeholder'})}/>
<BasicField field={this.props.form.$('state')}
label={this.props.intl.formatMessage({id: 'registration.wizard.nonprofit.state.label'})}
placeholder={this.props.intl.formatMessage({id: 'registration.wizard.nonprofit.state.placeholder'})}/>
<BasicField field={this.props.form.$('zip')}
label={this.props.intl.formatMessage({id: 'registration.wizard.nonprofit.zip.label' })}
placeholder={this.props.intl.formatMessage({id: 'registration.wizard.nonprofit.zip.placeholder'})}/>
</ThreeColumnFields> </ThreeColumnFields>
<ProgressableButton onClick={this.props.form.onSubmit} className="button" disabled={!this.props.form.isValid} buttonText={this.props.intl.formatMessage({id: this.props.buttonText})} <ProgressableButton onClick={this.props.form.onSubmit} className="button" disabled={!this.props.form.isValid} buttonText={this.props.intl.formatMessage({id: this.props.buttonText})}
inProgress={this.props.form.submitting || this.props.form.container().submitting} disableOnProgress={true}/> inProgress={this.props.form.submitting || this.props.form.container().submitting} disableOnProgress={true}/>

View file

@ -175,24 +175,6 @@ export class InnerRegistrationWizard extends React.Component<RegistrationWizardP
this.form.signinApi = this.props.ApiManager.get(WebUserSignInOut) this.form.signinApi = this.props.ApiManager.get(WebUserSignInOut)
} }
//set up labels
let label: {[props:string]: string} = {
'nonprofitTab[organization_name]': "registration.wizard.nonprofit.name.label",
"nonprofitTab[website]": 'registration.wizard.nonprofit.website.label',
"nonprofitTab[org_email]": 'registration.wizard.nonprofit.email.label',
'nonprofitTab[org_phone]': 'registration.wizard.nonprofit.phone.label',
'nonprofitTab[city]': 'registration.wizard.nonprofit.city.label',
'nonprofitTab[state]': 'registration.wizard.nonprofit.state.label',
'nonprofitTab[zip]': 'registration.wizard.nonprofit.zip.label',
'userTab[name]': 'registration.wizard.contact.name.label',
'userTab[email]': 'registration.wizard.contact.email.label',
'userTab[password]': 'registration.wizard.contact.password.label',
'userTab[password_confirmation]': 'registration.wizard.contact.password_confirmation.label'
}
for (let key in label){
this.form.$(key).set('label', this.props.intl.formatMessage({id: label[key]}))
}
return <Wizard wizardState={this.registrationWizardState} disableTabs={this.form.submitting}> return <Wizard wizardState={this.registrationWizardState} disableTabs={this.form.submitting}>
<NonprofitInfoPanel tab={this.registrationWizardState.tabsByName['nonprofitTab']} <NonprofitInfoPanel tab={this.registrationWizardState.tabsByName['nonprofitTab']}
buttonText="registration.wizard.next"/> buttonText="registration.wizard.next"/>

View file

@ -12,8 +12,6 @@ import {areWeOrAnyParentSubmitting} from "../../lib/houdini_form";
export const FieldDefinitions : Array<FieldDefinition> = [ export const FieldDefinitions : Array<FieldDefinition> = [
{ {
name: 'name', name: 'name',
label: 'registration.wizard.contact.name.label',
placeholder: 'registration.wizard.contact.name.placeholder',
validators: [Validations.isFilled] validators: [Validations.isFilled]
}, },
{ {
@ -24,14 +22,12 @@ export const FieldDefinitions : Array<FieldDefinition> = [
}, },
{ {
name: 'password', name: 'password',
label: 'registration.wizard.contact.password.label',
type: 'password', type: 'password',
validators: [Validations.isFilled], validators: [Validations.isFilled],
related: ['userTab.password_confirmation'] related: ['userTab.password_confirmation']
}, },
{ {
name: 'password_confirmation', name: 'password_confirmation',
label: 'registration.wizard.contact.password_confirmation.label',
type: 'password', type: 'password',
validators: [Validations.shouldBeEqualTo("userTab.password")] validators: [Validations.shouldBeEqualTo("userTab.password")]
} }
@ -50,12 +46,22 @@ class UserInfoForm extends React.Component<UserInfoFormProps & InjectedIntlProps
render() { render() {
return <fieldset> return <fieldset>
<TwoColumnFields> <TwoColumnFields>
<BasicField field={this.props.form.$("name")}/> <BasicField field={this.props.form.$("name")}
<BasicField field={this.props.form.$('email')}/> label={
this.props.intl.formatMessage({id: "registration.wizard.contact.name.label"})}
placeholder={this.props.intl.formatMessage({id: "registration.wizard.contact.name.placeholder"})}/>
<BasicField field={this.props.form.$('email')}
label={this.props.intl.formatMessage({id: "registration.wizard.contact.email.label"})}
placeholder={this.props.intl.formatMessage({id: "registration.wizard.contact.email.placeholder"})}
/>
</TwoColumnFields> </TwoColumnFields>
<BasicField field={this.props.form.$('password')}/> <BasicField field={this.props.form.$('password')}
<BasicField field={this.props.form.$('password_confirmation')}/> label={this.props.intl.formatMessage({id:'registration.wizard.contact.password.label'})}
/>
<BasicField field={this.props.form.$('password_confirmation')}
label={this.props.intl.formatMessage({id:'registration.wizard.contact.password_confirmation.label'})}
/>
<ProgressableButton onClick={this.props.form.onSubmit} <ProgressableButton onClick={this.props.form.onSubmit}

View file

@ -23,13 +23,11 @@ export interface SessionLoginFormProps
export const FieldDefinitions : Array<FieldDefinition> = [ export const FieldDefinitions : Array<FieldDefinition> = [
{ {
name: 'email', name: 'email',
label: 'email',
type: 'text', type: 'text',
validators: [Validations.isFilled] validators: [Validations.isFilled]
}, },
{ {
name: 'password', name: 'password',
label: 'password',
type: 'password', type: 'password',
validators: [Validations.isFilled] validators: [Validations.isFilled]
} }
@ -100,20 +98,14 @@ class InnerSessionLoginForm extends React.Component<SessionLoginFormProps & Inje
if(!this.form.signinApi){ if(!this.form.signinApi){
this.form.signinApi = this.props.ApiManager.get(WebUserSignInOut) this.form.signinApi = this.props.ApiManager.get(WebUserSignInOut)
} }
let label: {[props:string]: string} = {
'email': "login.email",
"password": 'login.password',
}
for (let key in label){
this.form.$(key).set('label', this.props.intl.formatMessage({id: label[key]}))
}
let errorDiv = !this.form.isValid ? <div className="form-group has-error"><div className="help-block" role="alert">{(this.form as any).error}</div></div> : '' let errorDiv = !this.form.isValid ? <div className="form-group has-error"><div className="help-block" role="alert">{(this.form as any).error}</div></div> : ''
return <form onSubmit={this.form.onSubmit}> return <form onSubmit={this.form.onSubmit}>
<BasicField field={this.form.$('email')}/> <BasicField field={this.form.$('email')}
<BasicField field={this.form.$('password')}/> label={this.props.intl.formatMessage({id: 'login.email'})}/>
<BasicField field={this.form.$('password')}
label={this.props.intl.formatMessage({id: 'login.password'})}/>
{errorDiv} {errorDiv}
<div className={'form-group'}> <div className={'form-group'}>
<ProgressableButton onClick={this.form.onSubmit} className="button" disabled={!this.form.isValid || this.form.submitting} inProgress={this.form.submitting} <ProgressableButton onClick={this.form.onSubmit} className="button" disabled={!this.form.isValid || this.form.submitting} inProgress={this.form.submitting}

30
package-lock.json generated
View file

@ -5697,9 +5697,9 @@
"integrity": "sha1-ILt0A9POo5jpHcRxCo/xuCdKJe0=" "integrity": "sha1-ILt0A9POo5jpHcRxCo/xuCdKJe0="
}, },
"hoist-non-react-statics": { "hoist-non-react-statics": {
"version": "2.5.0", "version": "2.5.5",
"resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-2.5.0.tgz", "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-2.5.5.tgz",
"integrity": "sha512-6Bl6XsDT1ntE0lHbIhr4Kp2PGcleGZ66qu5Jqk8lc0Xc/IeG6gVLmwUGs/K0Us+L8VWoKgj0uWdPMataOsm31w==", "integrity": "sha512-rqcy4pJo55FTTLWt+bU8ukscqHeE/e9KWvsOW2b/a3afxQZhwkQdT1rPPCJ0rYXdj4vNcasY8zHTH+jF/qStxw==",
"dev": true "dev": true
}, },
"home-or-tmp": { "home-or-tmp": {
@ -8900,19 +8900,19 @@
} }
}, },
"mobx": { "mobx": {
"version": "4.2.1", "version": "4.3.1",
"resolved": "https://registry.npmjs.org/mobx/-/mobx-4.2.1.tgz", "resolved": "https://registry.npmjs.org/mobx/-/mobx-4.3.1.tgz",
"integrity": "sha1-3UGQ2vG0PUGjoihYUlP5lwsKJ90=", "integrity": "sha1-M05aq0kWsdQ/D682BaZLG0s8y40=",
"dev": true "dev": true
}, },
"mobx-react": { "mobx-react": {
"version": "5.1.2", "version": "5.2.3",
"resolved": "https://registry.npmjs.org/mobx-react/-/mobx-react-5.1.2.tgz", "resolved": "https://registry.npmjs.org/mobx-react/-/mobx-react-5.2.3.tgz",
"integrity": "sha1-7FwtKaHfgj29GzfiFPo2oJBwVOI=", "integrity": "sha512-OuSlF2nJEa1PGookZcZnINbvEK4iWNNYiqUh6aebk2AkWxj3sG8OafDOQMcMYApQALTHRsrBIjOx/K8TFxcz7w==",
"dev": true, "dev": true,
"requires": { "requires": {
"hoist-non-react-statics": "2.5.0", "hoist-non-react-statics": "2.5.5",
"react-lifecycles-compat": "3.0.3" "react-lifecycles-compat": "3.0.4"
} }
}, },
"mobx-react-devtools": { "mobx-react-devtools": {
@ -8922,7 +8922,7 @@
"dev": true "dev": true
}, },
"mobx-react-form": { "mobx-react-form": {
"version": "github:houdiniproject/mobx-react-form#63a163397102d86745b7ac381d42b794ec172231", "version": "github:houdiniproject/mobx-react-form#8421c456a88cd3d7f887168a8228bb1a04911e7d",
"dev": true, "dev": true,
"requires": { "requires": {
"lodash": "4.17.10" "lodash": "4.17.10"
@ -11584,9 +11584,9 @@
"dev": true "dev": true
}, },
"react-lifecycles-compat": { "react-lifecycles-compat": {
"version": "3.0.3", "version": "3.0.4",
"resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.3.tgz", "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz",
"integrity": "sha512-bOr65SSYgxDgDNqLnDqt+gropXGPNB1Wbyys4tOYiNuP/qYWC4qFM9XH1ruzq+tT6EjE29pJsCr19rclKtpUEg==", "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==",
"dev": true "dev": true
}, },
"react-reconciler": { "react-reconciler": {

View file

@ -54,8 +54,8 @@
"less": "^3.0.4", "less": "^3.0.4",
"less-loader": "^4.1.0", "less-loader": "^4.1.0",
"lodash": "^4.17.5", "lodash": "^4.17.5",
"mobx": "^4.2.0", "mobx": "^4.3.1",
"mobx-react": "^5.0.0", "mobx-react": "^5.2.3",
"mobx-react-devtools": "^5.0.1", "mobx-react-devtools": "^5.0.1",
"mobx-react-form": "github:houdiniproject/mobx-react-form#our_fix", "mobx-react-form": "github:houdiniproject/mobx-react-form#our_fix",
"no-scroll": "^2.1.0", "no-scroll": "^2.1.0",

View file

@ -72,7 +72,7 @@ export declare class Field implements Base, FieldProperties, FieldMethods, Field
$(fieldName: string): Field; $(fieldName: string): Field;
add(obj: any): any; add(obj:{FieldDefinition}): any;
bind(): object; bind(): object;
@ -82,7 +82,7 @@ export declare class Field implements Base, FieldProperties, FieldMethods, Field
container(): Form |Field container(): Form |Field
del(key: any); del(path?: string);
each(callback: (i: Field) => void); each(callback: (i: Field) => void);
@ -173,6 +173,7 @@ interface FieldHandlers {
interface FieldDefinition { interface FieldDefinition {
name: string name: string
key?: string
label?: string label?: string
value?: any value?: any
default?: any default?: any
@ -253,12 +254,13 @@ interface FormInitializer{
interface initializationDefinition { interface initializationDefinition {
fields?:FieldDefinitions[] fields?:FieldDefinitions[]
hooks?: FormHooks
} }
export class Form implements Base { export class Form implements Base {
constructor(definition:initializationDefinition, options?:any) constructor(definition?:initializationDefinition, options?:any)
plugins(): void plugins(): void
setup(): any setup(): any
onInit(): void onInit(): void
@ -269,7 +271,7 @@ export class Form implements Base {
$(fieldName: string): Field; $(fieldName: string): Field;
add(obj: any): any; add(obj:FieldDefinition): any;
check(computed: string, deep?: boolean): boolean; check(computed: string, deep?: boolean): boolean;
@ -292,7 +294,7 @@ export class Form implements Base {
map(callback: (i: Field) => void); map(callback: (i: Field) => void);
observe(obj: any); observe(...obj: any)
select(path: string): Field; select(path: string): Field;
@ -304,10 +306,12 @@ export class Form implements Base {
update(obj: any): void; update(obj: any): void;
readonly submitting: boolean; readonly submitting: boolean;
readonly isValid:boolean;
protected validator :any protected validator :any
readonly isValid :boolean; readonly isValid :boolean;
readonly size:number
} }
@ -327,7 +331,7 @@ interface SharedFieldFormMethods {
has(key:string):boolean has(key:string):boolean
map(callback:(i:Field) => void) map(callback:(i:Field) => void)
each(callback:(i:Field) => void) each(callback:(i:Field) => void)
add(obj:any):any add(obj:FieldDefinition): any;
del(key:any) del(key:any)
observe(obj:any) observe(obj:any)
intercept(obj:any) intercept(obj:any)