Skip to content

Commit 1e4467a

Browse files
authored
Merge pull request #1 from matt-sanders/new-schema
Version 1.0
2 parents efba427 + b7dbd44 commit 1e4467a

File tree

10 files changed

+952
-86
lines changed

10 files changed

+952
-86
lines changed

dist/vue-formly.js

Lines changed: 611 additions & 25 deletions
Large diffs are not rendered by default.

dist/vue-formly.min.js

Lines changed: 4 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/components/FormlyField.vue

Lines changed: 44 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,52 @@
11
<template>
2-
<component :is="field.type" :model.sync="model" :field="field"></component>
2+
<component :is="type" :form.sync="form" :key="key"></component>
33
</template>
44

55
<script>
66
const Vue = require('vue');
7-
import Util, {getTypes} from '../util';
7+
import Util, {getTypes, setError} from '../util';
88
export default {
9-
props: ['field', 'model'],
10-
components: getTypes()
9+
props: ['form', 'key'],
10+
computed: {
11+
type:function(){
12+
return 'formly_'+this.form[this.key].type;
13+
}
14+
},
15+
methods: {
16+
validate:function(){
17+
let field = this.form[this.key];
18+
19+
//first check if we need to create a field
20+
if ( !this.form.$errors[this.key] ) this.$set('form.$errors.'+this.key, {});
21+
22+
//check for required fields. This whole setting,unsetting thing seems kind of wrong though..
23+
//there might be a more 'vue-ey' way to do this...
24+
if ( field.required ){
25+
if ( !this.form.$errors[this.key].required ) this.$set('form.$errors.'+this.key+'.required', true);
26+
setError(this.form, this.key, 'required', !field.value) ;
27+
}
28+
29+
//if we've got nothing left then return
30+
if ( !field.validators ) return;
31+
32+
Object.keys(field.validators).forEach((validKey) => {
33+
if ( !this.form.$errors[this.key][validKey] ) this.$set('form.$errors.'+this.key+'.'+validKey, false);
34+
if ( !field.required && !field.value ) return;
35+
36+
let validator = field.validators[validKey];
37+
38+
let valid = typeof validator == 'function' ? !validator(field) : !eval(validator);
39+
setError(this.form, this.key, validKey, valid);
40+
41+
});
42+
}
43+
},
44+
components: getTypes(),
45+
created(){
46+
this.validate();
47+
this.$watch('form.'+this.key+'.value', (val) =>{
48+
let valid = this.validate();
49+
});
50+
}
1151
}
1252
</script>

src/components/FormlyForm.vue

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,37 @@
11
<template>
22
<fieldset>
3-
<formly-field v-for="field in fields" :field="field" :model.sync="model[field.key]" ></formly-field>
3+
<template v-if="!customLayout">
4+
<formly-field v-for="field in form | formlyFields" :form.sync="form" :key="field" ></formly-field>
5+
</template>
6+
<slot></slot>
47
</fieldset>
58
</template>
69

710
<script>
811
export default {
9-
props: ['fields', 'model']
12+
props: ['form', 'customLayout'],
13+
created(){
14+
//make sure that the 'value' is always set
15+
Object.keys(this.form).forEach((key) => {
16+
if ( typeof this.form[key].value == 'undefined' ) this.$set('form.'+key+'.value', '');
17+
});
18+
19+
//set our validation options
20+
this.$set('form.$errors', {});
21+
this.$set('form.$valid', true);
22+
23+
this.$watch('form.$errors', function(val){
24+
let valid = true;
25+
Object.keys(this.form.$errors).forEach((key)=>{
26+
let errField = this.form.$errors[key];
27+
Object.keys(errField).forEach((errKey) => {
28+
if ( errField[errKey] ) valid = false;
29+
})
30+
});
31+
this.form.$valid = valid;
32+
}, {
33+
deep: true
34+
});
35+
}
1036
}
1137
</script>

src/filters/index.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
export default function(Vue){
2+
//filter valid formly fields.
3+
//this shouldn't pass any $ fields. eg $errors or $valid
4+
Vue.filter('formlyFields', (fields) => {
5+
let re = /^\$/;
6+
let valid = Object.keys(fields).filter( (key) => {
7+
return !re.test(key);
8+
});
9+
return valid;
10+
});
11+
}

src/index.js

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,18 @@
11
import Components from './components/index';
2+
import Filters from './filters/index';
23
import Util,{getTypes, addType} from './util';
34

45

56
let Formly = {
6-
7+
getTypes,
8+
addType,
79
install(Vue, options){
8-
9-
if ( Formly.installed ){
10-
return;
11-
}
1210

1311
//install our components
1412
Components(Vue);
15-
13+
Filters(Vue);
14+
15+
Vue.$formly = {getTypes, addType};
1616
}
1717
};
1818

src/util.js

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,27 @@ export default exports;
55

66
/**
77
* Allow additional templates to be added
8+
* When they're created they should be prefixed so they don't conflict with native components
9+
* eg. addType('input', {}) will be available as <formly_input></formly_input>
810
* @param {String} id
911
* @param {Object} options
1012
*/
1113
export function addType(id, options){
12-
exports.formlyFields[id] = options;
14+
exports.formlyFields['formly_'+id] = options;
1315
}
1416

1517
export function getTypes(){
1618
return exports.formlyFields;
1719
}
20+
21+
/**
22+
* Allows a field to add/remove errors to the form
23+
* @param {Object} form
24+
* @param {String} key
25+
* @param {String} err
26+
* @param {Bool} isError
27+
*/
28+
export function setError(form, key, err, isError){
29+
if ( !form.$errors[key] ) form.$errors[key] = {};
30+
form.$errors[key][err] = isError;
31+
}

test/unit/specs/FormlyField.spec.js

Lines changed: 141 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -23,53 +23,178 @@ describe('FormlyField', () => {
2323

2424
it('should take on the type of another component', () => {
2525

26-
Vue.component('test', {
27-
props: ['field'],
28-
template: '<div id="testComponent">{{field.type}}</div>'
26+
Vue.component('formly_test', {
27+
props: ['form', 'key'],
28+
template: '<div id="testComponent">{{form[key].type}}</div>'
2929
});
3030

3131
let data = {
32-
schema:{
33-
type: 'test'
32+
form:{
33+
$errors: {},
34+
$valid: {},
35+
test: {
36+
type: 'test'
37+
}
3438
}
3539
};
3640

37-
createForm('<formly-field :field="schema"></formly-field>', data);
41+
createForm('<formly-field :form="form" key="test"></formly-field>', data);
3842

3943
let innerElem = vm.$el.querySelector('#testComponent');
4044

41-
expect(innerElem.textContent).to.contain(data.schema.type);
45+
expect(innerElem.textContent).to.contain(data.form.test.type);
4246

4347
});
4448

4549
it('should mimic the model of the parent', (done) => {
4650

47-
Vue.component('test', {
48-
props: ['model'],
49-
template: '<input type="text" id="testInput" v-model="model">'
51+
Vue.component('formly_test', {
52+
props: ['form', 'key'],
53+
template: '<input type="text" id="testInput" v-model="form[key].value">'
5054
});
5155

5256
let data = {
53-
schema: {
54-
type: 'test'
55-
},
56-
testModel: 'foo'
57+
form: {
58+
$errors: {},
59+
$valid: {},
60+
search: {
61+
type: 'test',
62+
value: 'foo'
63+
}
64+
}
5765
};
5866

59-
createForm('<formly-field :field="schema" :model.sync="testModel"></formly-field>', data);
67+
createForm('<formly-field :form.sync="form" key="search"></formly-field>', data);
6068

6169
let input = vm.$el.querySelector('#testInput');
6270

6371
expect(input.value).to.contain('foo');
6472

6573
//change the value and expect a change
66-
vm.testModel = 'bar';
74+
vm.form.search.value = 'bar';
6775

6876
setTimeout(() => {
6977
expect(input.value).to.equal('bar');
7078
done();
7179
}, 0);
7280

7381
});
82+
83+
describe('Validation', ()=>{
84+
85+
before(()=>{
86+
Vue.component('formly_test', {
87+
props: ['form', 'key'],
88+
template: '<div></div>'
89+
});
90+
});
91+
92+
function createValidField(data){
93+
return createForm('<formly-field :form.sync="form" key="search"></formly-field>', data);
94+
};
95+
96+
it('should handle required values', (done) => {
97+
98+
let data = {
99+
form: {
100+
$valid: true,
101+
$errors: {},
102+
search: {
103+
type: 'test',
104+
value: '',
105+
required: true
106+
}
107+
}
108+
};
109+
110+
createValidField(data);
111+
expect(vm.form.$errors.search.required).to.be.true;
112+
113+
vm.$set('form.search.value','testing');
114+
setTimeout(()=>{
115+
expect(vm.form.$errors.search.required).to.be.false;
116+
done();
117+
},0);
118+
119+
});
120+
121+
it('should take an expression', (done) => {
122+
let data = {
123+
form: {
124+
$valid: true,
125+
$errors: {},
126+
search: {
127+
type: 'test',
128+
value: 'testing',
129+
validators: {
130+
expression: 'field.value == "test"'
131+
}
132+
}
133+
}
134+
};
135+
136+
createValidField(data);
137+
expect(vm.form.$errors.search.expression).to.be.true;
138+
139+
vm.$set('form.search.value', 'test');
140+
setTimeout(()=>{
141+
expect(vm.form.$errors.search.expression).to.be.false;
142+
done();
143+
},0);
144+
});
145+
146+
it('should not require non-required values', (done) => {
147+
let data = {
148+
form: {
149+
$valid: true,
150+
$errors: {},
151+
search: {
152+
type: 'test',
153+
value: '',
154+
validators: {
155+
expression: 'field.value == "test"'
156+
}
157+
}
158+
}
159+
};
160+
161+
createValidField(data);
162+
expect(vm.form.$errors.search.expression).to.be.false;
163+
164+
vm.$set('form.search.value', 'testing');
165+
setTimeout(()=>{
166+
expect(vm.form.$errors.search.expression).to.be.true;
167+
done();
168+
},0);
169+
});
170+
171+
it('should take a function', (done) => {
172+
let data = {
173+
form: {
174+
$valid: true,
175+
$errors: {},
176+
search: {
177+
type: 'test',
178+
value: 'testing',
179+
validators: {
180+
expression: function(field){
181+
return field.value == 'test';
182+
}
183+
}
184+
}
185+
}
186+
};
187+
188+
createValidField(data);
189+
expect(vm.form.$errors.search.expression).to.be.true;
190+
191+
vm.$set('form.search.value', 'test');
192+
setTimeout(()=>{
193+
expect(vm.form.$errors.search.expression).to.be.false;
194+
done();
195+
},0);
196+
});
197+
198+
});
74199

75200
});

0 commit comments

Comments
 (0)