I wander if there is a mechanism similar to PropTypes to validate values of @bindable
. I think Angular has something similar too. From time to time, passing the wrong value or forgot to passing a value (maybe because of a typo) is a source of bugs and frustration to understand what is going on.
Is this possible? If so how?
1 Like
So educate me here, why use a bolt-on when it would seem you could use TypeScript to do that intrinsically? Is TS not an option?
1 Like
Thanks for that. Every project always has different needs and constraints of course so the reason for asking.
Could you not perhaps use aurelia-validation as I think it can get fired when the binding changes to stay in the ecosystem?
1 Like
Maybe. I’ll try to look into that.
1 Like
I use TS, but here is an example of how I am using it and it is working for me. Might not be the best way though.
In attached() I call a method setValidationRules() that setup the property validation requirements.
Initially I was setting up a rules collection and running them against the property, but changed that over to use the on() method. I left the full function as it shows how I was able to use a few of the more advance methods.
setValidationRules()
{
const RENTAL_NOTSELECTED = 0;
const NOINSTRUCTOR = 0;
const RENTAL_DUAL = 3;
//this.studentRules =
ValidationRules
.ensure('wind').required()
.ensure('expectedRunway').required()
.ensure('fuelLeft').required()
.ensure('fuelRight').required()
.ensure('expectedFuelUsed').required()
.ensure('weatherBriefed').satisfies((v, o: PaveTimecardData) =>
{
return o.weatherBriefed;
})
.on(this.paveTimecard.data);
//.rules;
//this.standardRules =
ValidationRules
.ensure('airport').required()
.ensure('aircraftId').required()
.ensure('timecardDate').required()
.ensure('hobbsStart').required()
.ensure('hobbsEnd').required()
.ensure('rentalTypeId').satisfies((val, o: PaveTimecardModel) =>
{
if (!o.rentalTypeId || o.rentalTypeId === RENTAL_NOTSELECTED)
{
return false;
}
return true;
})
.withMessage("Rental Type needs to be selected.")
.ensure('instructorId').satisfies((val, o: PaveTimecardModel) =>
{
if (o.instructorId === NOINSTRUCTOR)
{
return false;
}
return true;
})
.withMessage("Rental Type [Dual] requires instructor to be selected.")
.when((o: PaveTimecardModel) =>
{
return o.rentalTypeId === RENTAL_DUAL;
})
.ensure('instructorId').satisfies((val, o: PaveTimecardModel) =>
{
if (o.instructorId !== NOINSTRUCTOR)
{
let msg: string = "Rental Type does not require an instructor to be selected and will be removed on save.";
if (this.warnings.indexOf(msg) === -1)
{
this.warnings.push(msg);
}
return true;
}
return true;
})
.when((o: PaveTimecardModel) =>
{
return o.rentalTypeId !== RENTAL_DUAL;
})
.ensure('fuelAmount').satisfies((val, o: PaveTimecardModel) =>
{
if (!o.fuelPurchased)
{
return true;
}
else if (o.fuelAmount && o.fuelAmount.length > 0)
{
return true;
}
return false;
})
.when((o: PaveTimecardModel) => o.fuelPurchased)
.withMessage("Purchased Fuel checked, no entry detected.")
.on(this.paveTimecard);
//.rules;
}
I use it at various change events with the following code snippet:
this.vcontroller.validate()
.then(result => {
if(result.valid)
this.enableSaveTimecard = true;
});
I have a repeat.for in the template that displays any errors in validation that require action:
<div style="color: red;">
<ul if.bind="vcontroller.errors">
<li repeat.for="error of vcontroller.errors">
${error.message}
</li>
</ul>
</div>
Hope that helps somewhat.
2 Likes
@Jenselme there is this plugin: https://github.com/aurelia-contrib/aurelia-typed-observable-plugin that can help you to process the value before it’s assigned to a view model. You can use it to implement your assertion:
import { bindable } from 'aurelia-typed-observable-plugin';
export class MyEl {
@bindable({
coerce: val => {
const realValue = Number(val);
if (typeof realValue !== 'number') {
throw new Error('Invalid value for "myProp", given: ' + val);
}
return realValue;
}
})
myProp
}
2 Likes
This looks exactly like what I need, thanks.
1 Like