When it comes to a modern web application, there is a high chance you will be working with a form or three. Fortunately Aurelia gives us straightforward and flexible ways of working with form elements and data.
Below are some common scenarios in which you might find yourself working with forms in Aurelia. All relatively straightforward, using concepts you are probably already familiar with from other frameworks like Angular.
Checkboxes
I have seen a little bit of confusion when it comes to working with checkbox elements in Aurelia. Once you understand what each role the binding behaviours play, you realise it is all quite straight forward.
Checkboxes: Using Strings
export class SomeViewModel {
selectedOptions = [];
someOptions = ['Option 1', 'Option 2', 'Option 3'];
}
<template>
<form role="form">
<label repeat.for="option of someOptions">
<input type="checkbox" value.bind="option.value" checked.bind="$parent.selectedOptions">${option.name}
</label>
</form>
</template>
Checkboxes: Using Objects
export class SomeViewModel {
selectedOptions = [];
someOptions = [
{value: 1, name: 'Test 1'},
{value: 2, name: 'Test 2'},
{value: 3, name: 'Test 3'}
];
}
<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>
Radio Elements
There is very little difference between checkbox and radio elements. You can have multiple checkbox elements selected, a group of radio elements only allows one option to be selected (unchecking any other selections).
Radio Elements: Using Strings
This is common use-case for working with radio elements.
export class SomeViewModel {
selectedOption = '';
someOptions = ['Option 1', 'Option 2', 'Option 3'];
}
<template>
<form role="form">
<label repeat.for="option of someOptions">
<input type="radio" value.bind="option" name="testOptions" checked.bind="$parent.selectedOption">${option}
</label>
</form>
</template>
Radio Elements: Using Objects
Once again, not too different to working with strings. We are binding to an object which might have many properties/values instead of a singular string.
export class SomeViewModel {
selectedOptions = [];
someOptions = [
{value: 1, name: 'Test 1'},
{value: 2, name: 'Test 2'},
{value: 3, name: 'Test 3'}
];
}
<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>
Select Elements
Working with select dropdown elements is a piece of cake. You can bind a select to a single value or you can also bind a select to an array of values as well.
Single Select Elements
In our below example, we are using the model.bind
functionality to bind to an object inside of an array. You can also work with strings by using the value.bind
behaviour, but model.bind
is a whole lot more flexible.
Single Select Elements: Using Strings
The most typical use-case scenario is you will find yourself populating a select elements option value with that of a value inside of a loop. If you’re looping over a simple array of colours or names, value.bind
is what you want to use.
export class SomeViewModel {
selectedVal = 3;
someOptions = ['Option 1', 'Option 2', 'Option 3'];
}
<template>
<form role="form">
<select value.bind="selectedVal">
<option repeat.for="option of someOptions" value.bind="option">${option}</option>
</select>
</form>
</template>
Single Select Elements: Using Objects
Unlike the above example, if you find yourself working with object values (a user object or some kind of object with many properties) you will find yourself seeking solace in the use of model.bind
where we can specify an object instead of a singular value.
Once again, not much of a difference from above, we are just working with an object value instead of a string.
export class SomeViewModel {
selectedVal = 3;
someOptions = [
{value: 1, name: 'Test 1'},
{value: 2, name: 'Test 2'},
{value: 3, name: 'Test 3'}
];
}
<template>
<form role="form">
<select value.bind="selectedVal">
<option repeat.for="option of someOptions" model.bind="option">${option.name}</option>
</select>
</form>
</template>
Multiple Select Elements
Working with a simple select dropdown is great and all, but how about a multiselect? Turns out things are not that different.
The only difference between the below code and the above single select code is the fact we are using the multiple
attribute on our select and we are also supply an array to the value.bind
behaviour of the select, opposed to a single value seen above.
export class SomeViewModel {
selectedValues = [];
someOptions = [
{value: 1, name: 'Test 1'},
{value: 2, name: 'Test 2'},
{value: 3, name: 'Test 3'}
];
}
<template>
<form role="form">
<select value.bind="selectedValues" multiple>
<option repeat.for="option of someOptions" model.bind="option">${option.name}</option>
</select>
</form>
</template>
Textarea
A textarea
element is just like any other form element. It allows you to bind to its value
and by default value.bind
will be two-way binding (meaning changes flow from out of the View into the ViewModel and changes in the ViewModel flow back to the View).
export class SomeViewModel {
textAreaValue = '';
}
<template>
<form role="form">
<textarea value.bind="textAreaValue"></textarea>
</form>
</template>
Contenteditable
Not exactly something that is directly associated with forms, but the contenteditable
attribute can turn a DIV into a textarea
like element. As such, I think it is a fair call to make that a innerhtml
element is a form element.
By default like other form elements, a contenteditable
element using the bind
behaviour is two way (meaning changes flow in both directions like a textarea element).
export class SomeViewModel {
textContentValue = '';
}
<template>
<form role="form">
<div textcontent.bind="textContentValue" contenteditable="true"></div>
</form>
</template>
Conclusion
Whoa. That was a lot of code. We only kind of skimmed over a few aspects of working with forms. As always, the documentation explains many of these things which can be read here. Once you know the different use-cases, working with forms becomes the easiest part of your application.
If something was not clear enough, you run into any issues or have a question about forms that is not covered here: please leave a comment and I will be happy to help you out.
Great list of tips! One problem I had with the select lists is that the id is a number in my database (and Loopback model) but Aurelia treats it as a string. Thus, if I want to change the selected item in a list (e.g. loading an existing record for display on the form) I can’t do
selectedVal = myRecord.id
instead I have to do
selectedVal = String(myRecord.id)
I don’t know if this is a problem with the Loopback model or Aurelia, but this is the only way I can get my form to show the proper selected item after loading a new record.
And I think you might have a bug with your Select with Objects code. If you’re going to make selectedVal an integer, then you need to do model.bind = “option.id”. Otherwise your mapping selectedVal to an object and not an integer. I’m a newb so I could be doing a lot wrong, but this is what I’m seeing with my test code.
This was fantastic, just bookmarked!
This doesn’t work for the checkboxes. The selectedOptions[] array will only store the object/value of the last selected checkbox in the list. It would be magic if it did work since it would have to track what item in the list corresponds to each checkbox and not be dependent upon the order of checking and unchecking checkboxes.
Actually, I see I’m wrong. For some reason you can’t mix objects and strings. Wow. I’ll move forward and test results. Thanks.
Great post. Enjoyed the corresponding section in your book:
https://leanpub.com/aurelia-for-real-world-applications
Might be helpful to add some notes on “change.trigger” for the select, and also the “selected” attribute of the option element.
Something like this:
https://github.com/cmichaelgraham/aurelia-axel-northwind/blob/master/aurelia-axel-northwind/views/pager/pager.html#L9-L11
Thanks for this. I’m just getting started with Aurelia and like it a lot so far.
I’ve run into a problem though when using `model.bind=”myClass”` checked.bind=”myArrayOfSelectedClasses”` on a list of checkboxes.
It works fine at first, then I store the user’s choice in `localStorage`, reload the page, restore the choices (as instances of the same class) but Aurelia refuses to check the checkboxes.
If I `console.dir()` the array of classes stored in LS I can see that they are of the same type as the list of all classes, but for some reason the list of all classes have additional properties like `get name()` and `set name()` (which I have not created). I assume this is the reason Aurelia believes they are _not_ the same?
How would you go about solving this issue?
Regards
“Checkboxes: Using Strings” example doesn’t work in current Aurelia v0.23.0. Don’t know how you’d get the selectedOptions array to work with multiple check in this situation. Maybe a single option using a String literal example would be useful instead.
Thanks.
By the way, under “Single Select Elements: Using Objects” I think
`model.bind=”option”`should be `model.bind=”option.value”`.