Skip to content

Commit

Permalink
feat: update
Browse files Browse the repository at this point in the history
  • Loading branch information
foxhound87 committed Jun 28, 2016
1 parent f74c388 commit 3f2a384
Show file tree
Hide file tree
Showing 11 changed files with 301 additions and 33 deletions.
Empty file added CHANGELOG.md
Empty file.
80 changes: 80 additions & 0 deletions DOCUMENTATION.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
# FORM

## Form Constructor

new Form({ fields, schema, options, extend });

* **fields**: represents the fields: name, label, value.

* **schema**: the json-schema for the validation.
See [json-schema.org](http://json-schema.org)

* **options** the additional options for ajv.
See [github.com/epoberezkin/ajv#options](https://github.com/epoberezkin/ajv#options)

* **extend** add custom validation keyword for using in the json-schema

## Form API

#### syncValue()
Synchronizes the value of the field `onChange` event.

You must define the name of the field to use this method.

#### isValid()
Check if the form is valid (boolean).

#### isDirty()
Check if the form is dirty (boolean).

#### fieldKeys()
Get an array with all fields names.

#### values()
Get an object with all fields values.

#### clear()
Clear the form to empty values.

#### reset()
Reset the form to initials values.

#### update(obj)
Pass an object to update the form with new values.

#### validate()
Check if the form is valid (boolean).

#### invalidate(err)
Invalidate the form passing a generic error message (string).

---

# Custom Validation Keyword

```
// define an 'extend' object with the custom keyword
const extend = {
range: {
type: 'number',
compile: (sch, parentSchema) => {
const min = sch[0];
const max = sch[1];
return parentSchema.exclusiveRange === true
? (data) => (data > min && data < max)
: (data) => (data >= min && data <= max);
},
},
};
// then use it the schema
var schema = { "range": [2, 4], "exclusiveRange": true };
// pass all to the form constructor
new Form({ fields, schema, extend });
```
10 changes: 3 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,8 @@

---

## TODO:

- Add new features
- Add documentation
- Add examples
- Add unit tests
## API Documentation
See the [Changelog](https://github.com/foxhound87/mobx-ajv-form/blob/master/CHANGELOG.md) or the [Documentation](https://github.com/foxhound87/mobx-ajv-form/blob/master/DOCUMENTATION.md) for all the details.

## Install

Expand Down Expand Up @@ -53,7 +49,7 @@ const fields = {
};
// create the form
export default new Form(fields, schema);
export default new Form({ fields, schema });
```

Pass the form to a react component:
Expand Down
27 changes: 19 additions & 8 deletions src/form.field.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@ export default class Field {
name;
label;
@observable $value;
@observable $valid = false;
@observable interacted = false;
@observable disabled = false;
@observable valid = false;
@observable errorMessage = null;
// @observable interactive = true;
originalValue = null;
originalErrorMessage = null;
validateFunction = null;
silent = null;
Expand Down Expand Up @@ -67,8 +67,14 @@ export default class Field {
this.validateFunction = validate || (() => Promise.resolve());
}

@computed
get isValid() {
return this.valid;
return this.$valid;
}

@computed
get isDirty() {
return this.originalValue !== this.$value;
}

@computed
Expand Down Expand Up @@ -103,21 +109,27 @@ export default class Field {
this.setInvalid(false);
}

@action
update(obj) {
this.$value = obj;
return;
}

@action
setValid() {
this.valid = true;
this.$valid = true;
this.errorMessage = null;
}

@action
setInvalid(showErrors = true) {
if (!_.isBoolean(this.$value)) this.valid = false;
if (!_.isBoolean(this.$value)) this.$valid = false;
this.errorMessage = showErrors ? this.originalErrorMessage : null;
}

@action
setInvalidWithMessage(message, showErrors = true) {
this.valid = false;
this.$valid = false;
this.errorMessage = showErrors ? message : null;
}

Expand Down Expand Up @@ -146,7 +158,6 @@ export default class Field {

@action
handleAjvValidationRules(showErrors) {
// const values = this.form.values();
const validate = this.form.ajvValidate;
const formIsValid = validate({ [this.name]: this.$value });

Expand All @@ -155,7 +166,7 @@ export default class Field {
const fieldErrorObj = _.find(validate.errors, (item) =>
_.includes(item.dataPath, `.${this.name}`));

// if fieldErrorObj is not undefined, the current field is valid.
// if fieldErrorObj is not undefined, the current field is invalid.
if (!_.isUndefined(fieldErrorObj)) {
// the current field is now invalid
// add additional info to the message
Expand Down
57 changes: 41 additions & 16 deletions src/form.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,16 @@ export default class Form {
@observable validating = false;
@observable genericErrorMessage = null;

ajvValidate = false;
ajvInstance;
ajvOptions;
ajvSchema;

constructor(obj = {}, ajvSchema = false, ajvOptions = {}) {
const keys = Object.keys(obj);
this.initAjv(ajvSchema, ajvOptions);
this.initFields(keys, obj);
ajvValidate = null;
ajvInstance = null;
ajvOptions = null;
ajvSchema = null;
ajvExtend = null;

constructor({ fields = {}, schema = false, options = {}, extend = null }) {
const keys = Object.keys(fields);
this.initAjv(schema, options, extend);
this.initFields(keys, fields);
}

@action
Expand All @@ -28,39 +29,53 @@ export default class Form {
}

@action
initAjv(ajvSchema, ajvOptions = {}) {
initAjv(ajvSchema, ajvOptions = {}, ajvExtend) {
if (!ajvSchema) return;
// set ajv schema and options
this.ajvSchema = ajvSchema;
this.ajvOptions = ajvOptions;
this.ajvExtend = ajvExtend;
// create ajv instance
this.ajvInstance = new AJV(_.merge(this.ajvOptions, {
allErrors: true,
coerceTypes: true,
}));
// extend with custom keywords
if (this.ajvExtend) {
_.forEach(this.ajvExtend, (val, key) =>
this.ajvInstance.addKeyword(key, val));
}
// create ajvInstance validator (compiling rules)
this.ajvValidate = this.ajvInstance.compile(this.ajvSchema);
}

@computed get valid() {
fieldKeys() {
return Object.keys(this.fields);
}

@computed
get isValid() {
if (this.validating) {
return false; // consider the form invalid until the validation process finish
}
return this
.fieldKeys()
.reduce((seq, key) => {
const field = this.fields[key];
seq = seq && field.valid; // eslint-disable-line no-param-reassign
seq = seq && field.isValid; // eslint-disable-line no-param-reassign
return seq;
}, true);
}

values() {
return _.mapValues(this.fields, 'value');
@computed
get isDirty() {
return this
.fieldKeys()
.some(key => this.fields[key].isDirty);
}

fieldKeys() {
return Object.keys(this.fields);
values() {
return _.mapValues(this.fields, 'value');
}

@action
Expand All @@ -83,6 +98,16 @@ export default class Form {
this.genericErrorMessage = null;
}

@action
update(obj) {
this
.fieldKeys()
.forEach((key) =>
this.fields[key].update(obj[key]));
}

/* validation */

@action
validate() {
this.validating = true;
Expand Down
33 changes: 33 additions & 0 deletions tests/data/a.form.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import Form from '../../src/index';
import extend from './extend';

const fields = {
username: {
label: 'Username',
value: 'SteveJobs',
},
email: {
label: 'Email',
value: '[email protected]',
},
password: {
label: 'Password',
value: 'thinkdifferent',
},
devSkills: {
label: 'Dev Skills',
value: 5,
},
};

const schema = {
type: 'object',
properties: {
username: { type: 'string', minLength: 6, maxLength: 20 },
email: { type: 'string', format: 'email', minLength: 5, maxLength: 20 },
password: { type: 'string', minLength: 6, maxLength: 20 },
devSkills: { range: [5, 10] },
},
};

export default new Form({ fields, schema, extend });
29 changes: 29 additions & 0 deletions tests/data/b.form.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import Form from '../../src/index';
import extend from './extend';

const fields = {
username: {
label: 'Username',
},
email: {
label: 'Email',
},
password: {
label: 'Password',
},
devSkills: {
label: 'Dev Skills',
},
};

const schema = {
type: 'object',
properties: {
username: { type: 'string', minLength: 6, maxLength: 20 },
email: { type: 'string', format: 'email', minLength: 5, maxLength: 20 },
password: { type: 'string', minLength: 6, maxLength: 20 },
devSkills: { range: [1, 10] },
},
};

export default new Form({ fields, schema, extend });
33 changes: 33 additions & 0 deletions tests/data/c.form.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import Form from '../../src/index';
import extend from './extend';

const fields = {
username: {
label: 'Username',
value: '',
},
email: {
label: 'Email',
value: '',
},
password: {
label: 'Password',
value: '',
},
devSkills: {
label: 'Dev Skills',
value: 1,
},
};

const schema = {
type: 'object',
properties: {
username: { type: 'string', minLength: 6, maxLength: 20 },
email: { type: 'string', format: 'email', minLength: 5, maxLength: 20 },
password: { type: 'string', minLength: 6, maxLength: 20 },
devSkills: { range: [1, 10] },
},
};

export default new Form({ fields, schema, extend });
Loading

0 comments on commit 3f2a384

Please sign in to comment.