I recently completed an online course on Aurelia by Scott Allen where he introduced the Aurelia-Validation plugin for form validation.
As a result, my view-model now looks like this in edit.js:
import {inject} from "aurelia-framework";
import {MovieService} from "./movieService";
import {Router} from "aurelia-router";
import {Validation} from "aurelia-validation";
@inject(MovieService, Router, Validation)
export class Edit {
constructor(movieService, router, validation) {
this.service = movieService;
this.router = router;
this.validation = validation.on(this)
.ensure("movie.title").isNotEmpty().hasLengthBetween(3, 50)
.ensure("movie.releaseYear").isNotEmpty().isNumber().isBetween(1900, 2100)
.ensure("movie.starRating").isNotEmpty().isNumber().isBetween(0, 5);
}
activate(params) {
this.service.getById(params.id)
.then(movie => {
this.movie = movie;
});
}
save() {
this.validation.validate()
.then(() => {
this.service.save(this.movie)
.then(movie => {
let url = this.router.generate("home");
this.router.navigate(url);
});
})
.catch(() => {
});
}
}
I have also created a corresponding view in edit.html
<form class="form-horizontal"
validate.bind="validation"
submit.trigger="save()">
<div class="form-group">
<label class="col-sm-2" for="title">Title</label>
<div class="col-sm-10">
<input type="text" id="title" placeholder="Title" value.bind="movie.title" class="form-control"/>
</div>
</div>
<!-- More form fields here -->
<div class="pull-right">
<a route-href="route:home" class="btn btn-default" role="button">Cancel</a>
<input type="submit" class="btn btn-primary" value="Save" />
</div>
</form>
The validation system is working flawlessly, preventing invalid data submissions as expected.
My next step involves moving the validation rules out of the view model and into a separate model class. I have created a new model called movieModel.js:
import {inject} from "aurelia-framework";
import {Validation} from 'aurelia-validation';
import {ensure} from 'aurelia-validation';
@inject(Validation)
export class MovieModel {
@ensure(function(it){ it.isNotEmpty().hasLengthBetween(3, 50) })
title = "";
@ensure(function(it){ it.isNotEmpty().isNumber().isBetween(1900, 2100) })
releaseYear = "";
@ensure(function(it){ it.isNotEmpty().isNumber().isBetween(0, 5) })
starRating = "";
constructor(validation) {
this.title = "";
this.releaseYear = "";
this.starRating = "";
}
}
I then integrated the movieModel into the view model like so:
import {inject} from "aurelia-framework";
import {MovieService} from "./movieService";
import {Router} from "aurelia-router";
import {Validation} from "aurelia-validation";
import {MovieModel} from "./movieModel";
@inject(MovieService, Router, Validation, MovieModel)
export class Edit {
constructor(movieService, router, validation, movieModel) {
this.service = movieService;
this.router = router;
this.movie = movieModel;
this.validation = validation.on(this.movie);
}
activate(params) {
this.service.getById(params.id)
.then(movie => {
this.movie = movie;
});
}
save() {
this.validation.validate()
.then(() => {
this.service.save(this.movie)
.then(movie => {
let url = this.router.generate("home");
this.router.navigate(url);
});
})
.catch(() => {
});
}
}
To implement these changes in the view edit.html, I had to add the validate attribute to the input fields like so:
<input type="text" id="title" placeholder="Title" value.bind="movie.title" class="form-control"/>
changed to:
<input type="text" id="title" placeholder="Title" value.bind="movie.title" validate="title" class="form-control"/>
However, after editing a movie retrieved from the service and attempting to save it, all properties are flagged as "required" by the validation system.
I suspect that the line this.validation = validation.on(this.movie) in the view model's constructor is linking the validation to an empty instance of the model. Unfortunately, I'm unsure of how to resolve this issue.