Skip to content

Commit 143df53

Browse files
committed
Make error validation immutable
Previously the model errors object was being cleared out on save and then the validator was appending all errors to that empty object after the result set came back. This is problematic for frameworks like vue.js which rely on object observers, as an observer change event is not fired when the new errors are added unless the developer specifically requests a deep object watch, with is both computationally expensive and something that has to be done every time. This replaces the behavior by creating a new errors object each time the validator is run and replacing the one currently on the model with it.
1 parent 06fcad9 commit 143df53

File tree

2 files changed

+39
-5
lines changed

2 files changed

+39
-5
lines changed

src/util/validation-errors.ts

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,24 +11,28 @@ export default class ValidationErrors {
1111

1212
static apply(model: Model, payload: Array<Object>) {
1313
let instance = new ValidationErrors(model, payload);
14-
instance.apply();
14+
let errors = instance.apply();
1515
}
1616

1717
apply() {
18+
let errorsAccumulator = {}
19+
1820
this.payload['errors'].forEach((err) => {
1921
let meta = err['meta'];
2022
let metaRelationship = meta['relationship'];
2123

2224
if (metaRelationship) {
2325
this._processRelationship(this.model, metaRelationship);
2426
} else {
25-
this._processResource(this.model, meta);
27+
this._processResource(errorsAccumulator, meta);
2628
}
2729
});
30+
31+
this.model.errors = errorsAccumulator
2832
}
2933

30-
private _processResource(model: Model, meta: Object) {
31-
model.errors[meta['attribute']] = meta['message'];
34+
private _processResource(errorsAccumulator: object, meta: Object) {
35+
errorsAccumulator[meta['attribute']] = meta['message'];
3236
}
3337

3438
private _processRelationship(model: Model, meta: Object) {
@@ -38,10 +42,14 @@ export default class ValidationErrors {
3842
return (r.id === meta['id'] || r.temp_id === meta['temp-id']);
3943
});
4044
}
45+
4146
if (meta['relationship']) {
4247
this._processRelationship(relatedObject, meta['relationship']);
4348
} else {
44-
this._processResource(relatedObject, meta);
49+
let relatedAccumulator = {}
50+
this._processResource(relatedAccumulator, meta);
51+
relatedObject.errors = relatedAccumulator
4552
}
53+
4654
}
4755
}

test/integration/validations-test.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,32 @@ describe('validations', function() {
110110
});
111111
});
112112

113+
it('instantiates a new error object instance after save', function(done) {
114+
let originalErrors = instance.errors = {foo: 'bar'};
115+
let result = instance.save({ with: { books: 'genre' }});
116+
let postSavePreValidateErrors = instance.errors;
117+
118+
expect(postSavePreValidateErrors).not.to.equal(originalErrors);
119+
120+
result.then(() => {
121+
done()
122+
}).catch(done)
123+
})
124+
125+
it('instantiates a new error object instance after validate', function(done) {
126+
let result = instance.save({ with: { books: 'genre' }});
127+
128+
let postSavePreValidateErrors = instance.errors;
129+
130+
result.then((val) => {
131+
let postValidateErrors = instance.errors;
132+
133+
expect(postValidateErrors).not.to.equal(postSavePreValidateErrors);
134+
135+
done()
136+
}).catch(done)
137+
})
138+
113139
it('applies errors to nested hasMany relationships', function(done) {
114140
instance.save({ with: { books: 'genre' }}).then((success) => {
115141
expect(instance.isPersisted()).to.eq(false);

0 commit comments

Comments
 (0)