In Aurelia we already have the power to iterate arrays from within our views and for most purposes, this is fine. However, sometimes there are situations where we might want to iterate an object’s properties or values without needing to normalise it into an array.
In the Aurelia Gitter chat, this question came up, so I decided to create a couple of custom ValueConverters to help with iterating Objects like they were arrays. Other than including the Value Converters themselves, you don’t need to change anything else.
Iterating Object Properties
This first Value Converter will allow us to iterate over an Object’s properties aka its keys.
Using the above embedded Gist, lets iterate a fictional Object we have created in our ViewModel.
export class ViewModel {
constructor() {
this.myObj = {
keyOne: 'myValue1',
keyTwo: 'myValue2',
keyThree: 'myValue3',
keyFour: 'myValue4',
};
}
}
Now within our View:
The following is very simple. We will start out by iterating our Object, but we will use our custom ValueConverter to create an Array on the spot with our property values. You could easily do this within your ViewModel without using a Value Converter, but the benefit of this is that we don’t need to add any unnecessary code to our ViewModels.
<template>
<require from="ObjectKeysValueConverter"></require>
<ul>
<li repeat.for="prop of myObj | objectKeys">${prop}</li>
</ul>
</template>
Iterating Object Values
Lastly, we are going to iterate an Object’s values. Above we iterated an Object’s keys, it is pretty self-explanatory what its values are.
We are going to re-use the same fictional ViewModel and Object from above to save time. The difference being the Object’s values will be displayed, not its properties.
export class ViewModel {
constructor() {
this.myObj = {
keyOne: 'myValue1',
keyTwo: 'myValue2',
keyThree: 'myValue3',
keyFour: 'myValue4',
};
}
}
Now within our View:
No explanation is needed, same deal as above. Just watch and learn. As you can see below the only difference is we are using our other Value Converter called, “objectValues” and changing “prop” to “val” in our repeater statement.
<template>
<require from="ObjectValuesValueConverter"></require>
<ul>
<li repeat.for="val of myObj | objectValues">${val}>
</ul>
</template>
Conclusion
Honestly, that’s it. As you can see ValueConverters allow us to change how data is rendered not only within a normal context, but within things like repeaters. This allows the ViewModel to keep the data standardised and allow the View to dictate how it should be displayed.
If you have any questions or spot any issues with the code, as always, please leave a comment.
At official docs page I’ve found this sample in cheat sheet section:
Render an map with a template.
HTML
${id} ${customer.fullName}
I just wonder what ‘customers’ model should look like, for this sample to work? I’ve tried this one:
customers = {
‘id00’: { fullName: ‘name1’ },
‘id01’: { fullName: ‘name2’ }
};
but got
TypeError: Cannot read property ‘getCollectionObserver’ of null
in console
No mention of how to access the index of the iterator?
Very nice post. I’m trying it out but can’t quite get it to work.
I created an object-keys-value-converter.js file in my source directory with:
export class ObjectKeysValueConverter {
toView(obj) {
let temp = [];
for (let prop in obj) {
if (obj.hasOwnProperty(prop)) {
temp.push(obj[prop]);
}
}
return temp;
}
}
Then in an email.html file:
${label}
Show Email Address
In the email.js file:
…..
emailAddresses = {home: “”}
…..
But no email fields display in the browser.
The email.html file didn’t render correctly. Trying it again:
“`
${label}
Show Email Address
“`
Okay. I have no idea how to add html to these comments. So here’s the relevant snippet:
repeat.for=”label in emailAddresses | objectKeys”
Figured it out. “in” should be “of”:
repeat.for=”label of emailAddresses | objectKeys”
Could you please create a ValueConverter to convert the object into an Array where each value is an Object with key and value properties.
Cheers!
Hi Dwayne, good idea to have the ObjectKeys and ObjectValues converter, very handy.
Looks like your two code publish should be other way around.
The ObjectKeysValueConverter doing what the ObjectValuesValueConverter
Also for the ObjectKeysValueConverter can be done in one line using
Object.keys(obj);
Hi,
Just trying this but (unless I’m mistaken) it doesn’t have functional parity with the viewmodel maintained array. If you add an item to the array the view will update. If you add a new key to the object (for me at least) it’s not refreshing the view. I have to replace the object.
John
BRILLIANT! This is EXACTLY what I was looking for.