Reputation: 75686
I'm using aurelia-validate and my validation works fine if I use variables, but I need it to validate properties of an object rather than a variable:
Here's what works:
import {Validation} from 'aurelia-validation';
import {ensure} from 'aurelia-validation';
import {ItemService} from './service';
export class EditItem {
static inject() {
return [Validation, ItemService];
}
@ensure(function(it){
it.isNotEmpty()
.hasLengthBetween(3,10);
})
name = '';
@ensure(function(it){
it.isNotEmpty()
.hasMinLength(10)
.matches(/^https?:\/\/.{3,}$/) //looks like a url
.matches(/^\S*$/); //no spaces
})
url = '';
constructor(validation, service) {
this.validation = validation.on(this);
this.service = service;
}
activate(params){
return this.service.getItem(params.id).then(res => {
console.log(res);
this.name = res.content.name; //populate
this.url = res.content.url;
});
}
update() {
this.validation.validate().then(
() => {
var data = {
name: this.name,
url: this.url
};
this.service.updateItem(data).then(res => {
this.message = "Thank you!";
})
}
);
}
}
Here's what I'm trying to do (but doesn't work)...also I'm not sure if it's better to keep the properties on the class or have a property called this.item
which contains the properties (this is the typical angular way):
import {Validation} from 'aurelia-validation';
import {ensure} from 'aurelia-validation';
import {ItemService} from './service';
export class EditItem {
static inject() {
return [Validation, ItemService];
}
@ensure(function(it){
it.isNotEmpty()
.hasLengthBetween(3,10);
})
this.item.name; //no assignment here should happen
@ensure(function(it){
it.isNotEmpty()
.hasMinLength(10)
.matches(/^https?:\/\/.{3,}$/) //looks like a url
.matches(/^\S*$/); //no spaces
})
this.item.url; //no assignment?
constructor(validation, service) {
this.validation = validation.on(this);
this.service = service;
this.item = null;
}
activate(params){
return this.service.getItem(params.id).then(res => {
console.log(res);
this.item = res.content; //populate with object from api call
});
}
update() {
this.validation.validate().then(
() => {
var data = {
name: this.item.name,
url: this.item.url
};
this.service.updateItem(data).then(res => {
this.message = "Thank you!";
})
}
);
}
}
Can someone give me some guidance here on how to use a validator against an existing object (for an edit page)?
Upvotes: 2
Views: 2224
Reputation: 11
Use the .on method of the validator to apply your rules to object properties.
The example below is called after I retrieve an object named stock, it validates that the quantity is not empty and is numeric only. Hope this helps...
let stock = {
name: 'some name'
minimumQuantity: '1'
};
applyRules() {
ValidationRules
.ensure((m: EditStock) => m.minimumQuantity)
.displayName("Minimum Quantity")
.required()
.withMessage(`\${$displayName} cannot be blank.`)
.matches( /^[0-9]*$/)
.withMessage(`\${$displayName} must be numeric only.`)
.on(this.stock);
}
Upvotes: 1
Reputation: 373
The validation works in all kinds of situations, but using the @ensure decorator can only be used to declare your rules on simple properties (like you found out).
Hence...
Option a: replace the ensure decorator with the fluent API 'ensure' method, this supports 'nested' or 'complex' binding paths such as:
import {Validation} from 'aurelia-validation';
import {ItemService} from './service';
export class EditItem {
static inject() {
return [Validation, ItemService];
}
constructor(validation, service) {
this.validation = validation.on(this)
.ensure('item.url')
.isNotEmpty()
.hasMinLength(10)
.matches(/^https?:\/\/.{3,}$/) //looks like a url
.matches(/^\S*$/)
.ensure('item.name')
.isNotEmpty()
.hasLengthBetween(3,10);
this.service = service;
this.item = null;
}
activate(params){
return this.service.getItem(params.id).then(res => {
console.log(res);
this.item = res.content; //populate with object from api call
});
}
update() {
this.validation.validate().then(
() => {
var data = {
name: this.item.name,
url: this.item.url
};
this.service.updateItem(data).then(res => {
this.message = "Thank you!";
})
}
);
}
}
Note: you can set up your validation even before item is set. Cool, no?
Option b: Since the validation rules are specific to the item, you could move your validation rules inside your item class using the @ensure decorator inside that class instead.
You can then set up validation in your VM after you've retrieved the item: this.validation = validation.on(this.item);
or, your service can set up the validation when it returns your item to your VM and make it an intrinsic part of the model: item.validation = validation.on(item);
Option a is easiest and seems to match your experience. Option b is more maintainable, as the validation rules for your model will live on the model, not on the view-model. However if you go with option b, you might have to adjust your HTML a bit to make sure validation hints appear.
Upvotes: 4