Skip to content

Commit a1cf223

Browse files
Prepared new release
1 parent dbb0653 commit a1cf223

File tree

7 files changed

+175
-25
lines changed

7 files changed

+175
-25
lines changed

API.md

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
- [onInvalidSubmit()](#oninvalidsubmit)
1313
- [onChange()](#onchange)
1414
- [reset()](#resetform)
15+
[preventExternalInvalidation](#preventexternalinvalidation)
1516
- [Formsy.Mixin](#formsymixin)
1617
- [name](#name)
1718
- [value](#value)
@@ -136,13 +137,13 @@ Triggers when form is submitted with a valid state. The arguments are the same a
136137
```
137138
Triggers when form is submitted with an invalid state. The arguments are the same as on `onSubmit`.
138139

139-
#### <a name="onchange">onChange(currentValues)</a>
140+
#### <a name="onchange">onChange(currentValues, isChanged)</a>
140141
```html
141142
<Formsy.Form onChange={this.saveCurrentValuesToLocalStorage}></Formsy.Form>
142143
```
143-
"onChange" triggers when setValue is called on your form elements. It is also triggered when dynamic form elements have been added to the form. The "currentValues" is an object where the key is the name of the input and the value is the current value.
144+
"onChange" triggers when setValue is called on your form elements. It is also triggered when dynamic form elements have been added to the form. The "currentValues" is an object where the key is the name of the input and the value is the current value. The second argument states if the forms initial values actually has changed.
144145

145-
#### <a name="resetform">reset()</a>
146+
#### <a name="resetform">reset(values)</a>
146147
```html
147148
var MyForm = React.createClass({
148149
resetForm: function () {
@@ -157,15 +158,35 @@ var MyForm = React.createClass({
157158
}
158159
});
159160
```
160-
Manually reset the form to its pristine state.
161+
Manually reset the form to its pristine state. You can also pass an object that inserts new values into the inputs. Keys are name of input and value is of course the value.
162+
163+
#### <a name="preventExternalInvalidation">preventExternalInvalidation</a>
164+
```html
165+
var MyForm = React.createClass({
166+
onSubmit: function (model, reset, invalidate) {
167+
invalidate({
168+
foo: 'Got some error'
169+
});
170+
},
171+
render: function () {
172+
return (
173+
<Formsy.Form onSubmit={this.onSubmit} preventExternalInvalidation>
174+
...
175+
</Formsy.Form>
176+
);
177+
}
178+
});
179+
```
180+
With the `preventExternalInvalidation` the input will not be invalidated though it has an error.
161181

162182
### <a name="formsymixin">Formsy.Mixin</a>
163183

164184
#### <a name="name">name</a>
165185
```html
166186
<MyInputComponent name="email"/>
187+
<MyInputComponent name="address.street"/>
167188
```
168-
The name is required to register the form input component in the form.
189+
The name is required to register the form input component in the form. You can also use dot notation. This will result in the "form model" being a nested object. `{email: 'value', address: {street: 'value'}}`.
169190

170191
#### <a name="value">value</a>
171192
```html

build/test.js

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -6,28 +6,29 @@ var Input = React.createClass({
66
mixins: [Formsy.Mixin],
77

88
render: function () {
9-
return <input disabled={this.isFormDisabled()} />
9+
return (
10+
<div>
11+
{this.showError()}
12+
{this.getErrorMessage()}
13+
<input disabled={this.isFormDisabled()} />
14+
</div>
15+
);
1016
}
1117
});
1218

1319
var FormApp = React.createClass({
14-
getInitialState: function () {
15-
return {
16-
bool: true
17-
};
18-
},
19-
flip: function () {
20-
this.setState({
21-
bool: !this.state.bool
20+
componentDidMount: function () {
21+
this.refs.form.updateInputsWithError({
22+
'foo.bar': 'hmmm'
2223
});
2324
},
25+
onSubmit: function (model) {
26+
console.log('model', model);
27+
},
2428
render: function () {
2529
return (
26-
<Formsy.Form disabled={this.state.bool}>
27-
{this.state.bool ?
28-
<Input name="foo" /> :
29-
<Input name="bar" />
30-
}
30+
<Formsy.Form ref="form" onInvalid={this.onInvalid}>
31+
<Input name="foo.bar" />
3132
</Formsy.Form>
3233
);
3334
}

specs/Element-spec.jsx

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -558,4 +558,33 @@ it('should allow an undefined value to be updated to a value', function (done) {
558558

559559
});
560560

561+
it('should allow for dot notation in name which maps to a deep object', function () {
562+
563+
var TestInput = React.createClass({
564+
mixins: [Formsy.Mixin],
565+
render: function () {
566+
return <input/>
567+
}
568+
});
569+
570+
var TestForm = React.createClass({
571+
onSubmit: function (model) {
572+
expect(model).toEqual({foo: {bar: 'foo', test: 'test'}});
573+
},
574+
render: function () {
575+
return (
576+
<Formsy.Form>
577+
<TestInput name="foo.bar" value="foo"/>
578+
<TestInput name="foo.test" value="test"/>
579+
</Formsy.Form>
580+
);
581+
}
582+
});
583+
584+
var form = TestUtils.renderIntoDocument(<TestForm/>);
585+
var formEl = TestUtils.findRenderedDOMComponentWithTag(form, 'form');
586+
TestUtils.Simulate.submit(formEl);
587+
588+
});
589+
561590
});

specs/Formsy-spec.jsx

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -612,6 +612,20 @@ describe('Formsy', function () {
612612
formsyForm.reset();
613613
expect(input.getValue()).toBe(true);
614614
});
615+
616+
it("should be able to reset the form using custom data", function() {
617+
var form = TestUtils.renderIntoDocument(<TestForm value={ true }/>);
618+
var input = TestUtils.findRenderedComponentWithType(form, TestInput);
619+
var formsyForm = TestUtils.findRenderedComponentWithType(form, Formsy.Form);
620+
expect(input.getValue()).toBe(true);
621+
form.setProps({value: false});
622+
expect(input.getValue()).toBe(false);
623+
formsyForm.reset({
624+
foo: 'bar'
625+
});
626+
expect(input.getValue()).toBe('bar');
627+
});
628+
615629
});
616630

617631
describe('.isChanged()', function() {

specs/Validation-spec.jsx

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,4 +217,65 @@ describe('Validation', function() {
217217

218218
});
219219

220+
221+
it('should not invalidate inputs on external errors with preventExternalInvalidation prop', function () {
222+
223+
var TestInput = React.createClass({
224+
mixins: [Formsy.Mixin],
225+
render: function () {
226+
return <input value={this.getValue()}/>
227+
}
228+
});
229+
var TestForm = React.createClass({
230+
invalidate: function (model, reset, invalidate) {
231+
invalidate({
232+
foo: 'bar'
233+
});
234+
},
235+
render: function () {
236+
return (
237+
<Formsy.Form onSubmit={this.invalidate} preventExternalInvalidation>
238+
<TestInput name="foo" value="foo"/>
239+
</Formsy.Form>
240+
);
241+
}
242+
});
243+
var form = TestUtils.renderIntoDocument(<TestForm/>);
244+
var formEl = TestUtils.findRenderedDOMComponentWithTag(form, 'form');
245+
var input = TestUtils.findRenderedComponentWithType(form, TestInput);
246+
TestUtils.Simulate.submit(formEl);
247+
expect(input.isValid()).toBe(true);
248+
249+
});
250+
251+
it('should invalidate inputs on external errors without preventExternalInvalidation prop', function () {
252+
253+
var TestInput = React.createClass({
254+
mixins: [Formsy.Mixin],
255+
render: function () {
256+
return <input value={this.getValue()}/>
257+
}
258+
});
259+
var TestForm = React.createClass({
260+
invalidate: function (model, reset, invalidate) {
261+
invalidate({
262+
foo: 'bar'
263+
});
264+
},
265+
render: function () {
266+
return (
267+
<Formsy.Form onSubmit={this.invalidate}>
268+
<TestInput name="foo" value="foo"/>
269+
</Formsy.Form>
270+
);
271+
}
272+
});
273+
var form = TestUtils.renderIntoDocument(<TestForm/>);
274+
var formEl = TestUtils.findRenderedDOMComponentWithTag(form, 'form');
275+
var input = TestUtils.findRenderedComponentWithType(form, TestInput);
276+
TestUtils.Simulate.submit(formEl);
277+
expect(input.isValid()).toBe(false);
278+
279+
});
280+
220281
});

src/main.js

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,8 @@ Formsy.Form = React.createClass({
3434
onValid: function () {},
3535
onInvalid: function () {},
3636
onChange: function () {},
37-
validationErrors: null
37+
validationErrors: null,
38+
preventExternalInvalidation: false
3839
};
3940
},
4041

@@ -93,7 +94,21 @@ Formsy.Form = React.createClass({
9394
},
9495

9596
mapModel: function () {
96-
return this.props.mapping ? this.props.mapping(this.model) : this.model;
97+
if (this.props.mapping) {
98+
return this.props.mapping(this.model)
99+
} else {
100+
return Object.keys(this.model).reduce(function (mappedModel, key) {
101+
102+
var keyArray = key.split('.');
103+
while (keyArray.length) {
104+
var currentKey = keyArray.shift();
105+
mappedModel[currentKey] = keyArray.length ? mappedModel[currentKey] || {} : this.model[key];
106+
}
107+
108+
return mappedModel;
109+
110+
}.bind(this), {});
111+
}
97112
},
98113

99114
// Goes through all registered components and
@@ -152,9 +167,8 @@ Formsy.Form = React.createClass({
152167
if (!component) {
153168
throw new Error('You are trying to update an input that does not exist. Verify errors object with input names. ' + JSON.stringify(errors));
154169
}
155-
156170
var args = [{
157-
_isValid: false,
171+
_isValid: this.props.preventExternalInvalidation || false,
158172
_externalError: errors[name]
159173
}];
160174
component.setState.apply(component, args);
@@ -421,10 +435,10 @@ Formsy.Form = React.createClass({
421435
},
422436
render: function () {
423437

424-
return React.DOM.form({
438+
return React.DOM.form(utils.extend({}, this.props, {
425439
onSubmit: this.submit,
426440
className: this.props.className
427-
},
441+
}),
428442
this.traverseChildrenAndRegisterInputs(this.props.children)
429443
);
430444

src/utils.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,5 +37,15 @@ module.exports = {
3737
}
3838

3939
return a === b;
40+
},
41+
extend: function () {
42+
var objects = [].slice.call(arguments);
43+
var initialObject = objects.shift();
44+
return objects.reduce(function (returnedObject, object) {
45+
return Object.keys(object).reduce(function (returnedObject, key) {
46+
returnedObject[key] = object[key];
47+
return returnedObject;
48+
}, returnedObject);
49+
}, initialObject);
4050
}
4151
};

0 commit comments

Comments
 (0)