Forms and Validation
Using Aurelia forms and bindings, aurelia-form and aurelia-validation plugins.
At the end also an example of Using Breeze with Validations
Text input
Let's make a simple signup form with email and password entries.
signup.ts
import User from './models/user';
export class Signup {
email = '';
password = '';
signup() {
var myUser = new User({ email: this.email, password: this.password });
myUser.save();
};
}
The signup
method in this example uses a hypothetical User
model class to save the user data.
signup.html
<template>
<form role="form" submit.delegate="signup()">
<label for="email">Email</label>
<input type="text" value.bind="email" placeholder="Email">
<label for="password">Password</label>
<input type="password" value.bind="password" placeholder="Password">
<button type="submit">Signup</button>
</form>
</template>
For <input>
elements, value.bind
creates a two-way binding by default.
We use submit.delegate
to delegate form submission to the signup
function of the VM.
Text area
export class SomeViewModel {
textAreaValue = '';
}
<template>
<form role="form">
<textarea value.bind="textAreaValue"></textarea>
</form>
</template>
Content editable
Any non-input field can be made contenteditable via contenteditable="true"
<template>
<form role="form">
<div textcontent.bind="textContentValue" contenteditable="true"></div>
</form>
</template>
Create registration form
Now let's create a simple registration form:
firstName
lastName
email
registration-form.js
import { computedFrom } from 'aurelia-framework';
export class RegistrationForm {
firstName = '';
lastName = '';
email = '';
@computedFrom('firstName', 'lastName')
get fullName() {
return `${this.firstName} ${this.lastName}`;
}
submit() {
// todo: call server...
}
}
We use the @computedFrom('firstName', 'lastName')
decorator on the fullName
getter method, to recompute the fullName
whenever firstName
or lastName
changes.
registration-form.html
<template>
<form submit.delegate="submit()">
<div class="form-group">
<label class="control-label" for="first">First Name</label>
<input type="text" class="form-control" id="first" placeholder="First Name" value.bind="firstName">
</div>
<div class="form-group">
<label class="control-label" for="last">Last Name</label>
<input type="text" class="form-control" id="last" placeholder="Last Name"
value.bind="lastName">
</div>
<div class="form-group">
<label class="control-label" for="email">Email</label>
<input type="text" class="form-control" id="email" placeholder="Email"
value.bind="email">
</div>
<button type="submit" class="btn btn-primary">Submit</button>
</form>
</template>
Single selection
export class MyForm {
constructor() {
this.isChecked = false;
}
submit(){
console.log("isChecked: " + this.isChecked);
}
}
Form with a checkbox, where checked
is bound to isChecked
property on the VM via checked.bind="isChecked"
<template>
<form role="form" submit.delegate="submit()">
<label for="checkbox">Checkbox</label>
<input type="checkbox" id="checkbox" checked.bind="isChecked"><br/>
<button type="submit">SUBMIT</button>
</form>
</template>
For single select, we need to change the selected
property to a string or number such as selectedVal = 3;
<template>
<form role="form">
<select value.bind="selectedVal">
<option repeat.for="option of someOptions" model.bind="option">${option.name}</option>
</select>
</form>
</template>
Radio buttons
<template>
<form role="form">
<label repeat.for="option of someOptions">
<input type="radio" model.bind="option" name="testOptions" checked.bind="$parent.selectedOptions">${option.name}
</label>
</form>
</template>
Multi selection
Multi selection is where the user can select multiple values, typically either via a group of checkboxes or a drop down multi selector.
Checkboxes
For a view model selectedOptions
and a list of Objects with value
and name
we have several display options.
export class SomeViewModel {
selectedOptions = [];
someOptions = [
{value: 1, name: 'Test 1'},
{value: 2, name: 'Test 2'},
{value: 3, name: 'Test 3'}
];
}
Group of check boxes via repeat.for
on someOptions
Array.
<template>
<form role="form">
<label repeat.for="option of someOptions">
<input type="checkbox" model.bind="option" checked.bind="$parent.selectedOptions">${option.name}
</label>
</form>
</template>
Multiple select drop down via repeat.for
on someOptions
Array for <option>
elements.
<template>
<form role="form">
<select value.bind="selectedValues" multiple>
<option repeat.for="option of someOptions" model.bind="option">${option.name}</option>
</select>
</form>
</template>
Using aurelia-form plugin
What Aurelia form does, is leverage Aurelia's strengths, and create a standardized way of describing forms simply using objects and arrays. Some of the features this plugin provides include:
- multi css-framework support
- aurelia-orm support (forms based on entity definition, no need to make a schema)
- validation
- concise way of describing forms using schemas
- ...
Installation
npm install aurelia-form
Then add the plugin in your plugins configuration, such as in main.ts
.plugin('aurelia-form')
Usage
aurelia-form
requires a schema definition of the form as follows:
export class Signup {
constructor() {
let schema = [{
key: 'email',
type: 'string'
},{
key: 'password',
type: 'string'
}];
let credentials = {
email: '',
password: ''
};
let submit = (credentials) => {
console.log(credentials);
};
this.loginForm = {schema, credentials, submit};
}
}
signup.html
<template>
<schema-form
submit.delegate="loginForm.submit(loginForm.credentials)"
schema.bind="this.loginForm.schema"
model.bind="this.loginForm.credentials"
></schema-form>
<template>
Aurelia-form leverages aurelia-view-manager
Models
At the highest level is the Form model
let user = {
name: 'John'
email: '[email protected]'
};
The element defines presentation details for the form field of the specific model attribute targeted by key
.
let nameElement = {
// Hide the label when is false.
label: false,
type: 'string',
// key what the property to alter in the User model
key: 'name'
}
You can add an attributes
object to specify specific attribute values of the field.
let nameElement = {
// ...
attributes: {
readonly: true
}
}
A schema is a collection of elements:
let username = {
type:'text',
key: 'username'
};
let password = {
type:'password',
key: 'password'
};
this.credentialsSchema = [
username,
password
];
Having a schema is not enough. We also need a model to populate it. That is where the key property in our schema definitions comes into play.
Types
- text
- button
- color
- date
- datetime
- 'datetime-local'
- month
- number
- password
- range
- search
- tel
- time
- url
- week
Collections
Collections are used when having a one to many relationship between "things". An example would be a shoppingCart
with products
.
/* model */
let shoppingCart = {
name: 'Mario',
products: [
{name: 'cheesy pizza' 10, amount: 2},
{name: 'meat pizza' 10, amount: 2},
{name: 'hot-dog crust pizza' 10, amount: 2}
]
}
let productSchema = [
nameElement,
amountElement
];
let productElement = {
type: 'collection',
key: 'products'
schema: productSchema
};
let shoppingCartSchema = [
nameElement,
productElement
];
fieldset nested
TODO
checkboxes
TODO
radios
TODO
select
TODO
actions buttons
TODO
submit
TODO
association
TODO
conditional
TODO
Model
/* ./person.js */
export class Person {
userSchema = userSchema; // assuming we defined userSchema
groupSchema = groupSchema; // assuming we defined groupSchema
/* minimal required models (just the objects) */
userModel = {};
groupModel = {
owner = userModel
};
}
Components
Aurelia-form provides several components. They give different levels of granularity when building a form. You might want to reuse the schema and the model, but would only want several of the form-fields to be rendered. Or you want a fancy layout that requires you to have a form form-field here and there. Aurelia-form let's you decide.
Generate a complete form using the schema
<schema-form
schema.bind="userSchema"
model.bind="userModel">
</schema-form>
Aurelia-form supports the aurelia-orm project. It does so by providing a public custom component named entity-form. To use it you must create an entity and pass it to the entity bindable. Read more about entities in the aurelia-orm docs.
<entity-form entity.bind="entity"></entity-form>
Generates all the form fields without the
<form-fields
model.bind="userModel"
schema.bind="userSchema">
</form-fields>
When things get really detailed you can choose to only generate a single form field.
<form-field
value.bind="model.name"
element.bind="nameElement">
</form-field>
Form
The Form
class of aurelia-form enables you to to get started a bit faster. When using the Form class you do have to install aurelia-validatejs as a dependency. Let's see what it gives us out of the box.
The class can be used in several ways. You can extend the view model or make new instances that are part of the view model's state.
class Welcome extends Form {
constructor() {
super();
http('products.json').then(products => {
/***
* Model is reserved property on the Form class. It is used to
* trigger the registration of the validator.
*/
this.model = products;
});
}
}
When used together with the schema-form element you get a nice synergy in which it will call the validate method of the extending Form class instance. Now that you have seen the extends version. Let's see the constructor version of it.
/* constructor */
import {Form} from 'aurelia-form';
class Welcome extends {
constructor() {
/* a new form */
let credentials = new Form();
/* schema */
let password = {
type: 'password',
key: 'password'
};
let username = {
type: 'string',
key: 'username'
};
credentials.schema = [
username,
password
];
/* model */
credentials.model = {
username: '',
password: ''
};
/* validation */
credentials.validator
.ensure('username')
.required()
.ensure('password')
.required();
/* avoid having `this` written everywhere */
this.credentials = credentials
}
}
All form related state is scoped to the form instance. Seeing what data belongs to what form is easy to discover. defining multiple forms in a view model is no problem.
<schema-form
messages.bind = "credentials.messages"
schema.bind = "credentials.schema"
model.bind = "credentials.model">
Translations
Aurelia-form uses aurelia-i18n to perform translations.
Translation requires that aurelia-i18n is configured with t
as the translating attribute.
Customization
See customization page.