This site runs best with JavaScript enabled.

Real-World Examples of Aurelia's Compose Tag


Let me show you a few examples of how we use the <compose> tag

When I first learned about the <compose> element I thought I'd use it everywhere. Well, in about 16 months I've been using Aurelia full-time, I think I personally have only used it twice, but I'm sure I'll use it more as we build out more reusable components.

The <compose> tag let's you dynamically insert custom components. It turns out that most of the time I don't need this because I know exactly what elements I want to use.

So when does it make sense to use <compose>. Here are the two instances I have used it.

Pattern Library

Our UI/UX team has developed a pattern library with code examples for our team to use. This lets us keep our styles consistent as we develop new pages.

The way we have it set up, each of the pages has a similar design and layout. Each pattern category is in a directory called patterns. There is a directory for each pattern with a similar structure.

So here is an excerpt of app.js:

1export class App {
2 configureRouter(config, router) {
3 config.addPipelineStep('authorize', AuthorizeStep);
4 this.router = router;
5 config.title = 'Pattern Library';
6 config.map([
7 {
8 route: '',
9 name: 'intro',
10 moduleId: './patterns/intro/index',
11 nav: true,
12 title: 'Intro',
13 },
14 {
15 route: 'accordions',
16 name: 'accordions',
17 moduleId: './patterns/accordions/index',
18 nav: true,
19 title: 'Accordions',
20 },
21 ...

So you can see our Accordions section is located in patterns/accordions

Here is the patterns/accordions/index.html component.

1<template>
2 <require from="../../components/pattern-index"></require>
3
4 <pattern-index
5 title="Accordions"
6 patterns="accordions/patterns"
7 links.bind="links"
8 ></pattern-index>
9</template>

Notice we are importing a pattern-index component. Here is what that component looks like:

pattern-index.html:

1<template>
2 <div class="fader-side-bar fader-side-bar--top"></div>
3 <div class="layout__middle__side">
4 <div class="layout__middle__side__inner">
5 <ul class="nav nav--stacked">
6 <li class="${link.class}" repeat.for="link of links">
7 <a click.delegate="subNavActivate($index)" href="#${link.href}"
8 >\${link.title}</a
9 >
10 </li>
11 <li class="push-bottom-medium"></li>
12 </ul>
13 </div>
14 </div>
15 <div class="fader-side-bar fader-side-bar--bottom"></div>
16
17 <div class="layout__middle__main">
18 <h1>${title}</h1>
19 <compose view="../patterns/${patterns}.html"></compose>
20 </div>
21</template>

pattern-index.js

1import {bindable, containerless} from 'aurelia-framework';
2
3@containerless export class PatternIndex { @bindable title = 'INCLUDE TITLE
4ATTRIBUTE'; @bindable patterns; @bindable links;
5
6 subNavActivate(index) {
7 const linkCount = this.links.length;
8
9 for (let i = 0; i < linkCount; i += 1) {
10 this.links[i].class = '';
11 }
12
13 this.links[index].class = 'active';
14 return true;
15 }
16
17}

So for the purpose of this article, there are really only two things to highlight. Here is where we are calling the pattern-index component:

1<pattern-index
2 title="Accordions"
3 patterns="accordions/patterns"
4 links.bind="links"
5></pattern-index>

Notice we pass in the patterns attribute.

And this is how we use the compose tag in the component:

1<compose view="../patterns/${patterns}.html"></compose>

So basically we replacing the <compose> tags with the patterns/accordions/patterns.html component, which looks like this:

1<template>
2 <require from="./accordion-basic"></require>
3 <require from="./accordion-close-others"></require>
4
5 <accordion-basic></accordion-basic>
6 <accordion-close-others></accordion-close-others>
7</template>

Here is an example of the accordion page:

Accordion Page

Data Table

I won't go so deep into this one, but basically give you a high level overview.

We have a <data-table> component in our system where you can bind an array of objects and basically print them out in a table. It is a very useful component and we use it in a number of places.

The problem is that sometimes you might want a column that doesn't have anything to do with your data. For example, we have one table that has a menu of actions for each row of data. This column is only applicable to this particular data array.

screenshot example

So how do we add this in to our table? Here is what our data-cell.html portion of our component looks like:

1<template>
2 <require from="./record-value"></require>
3
4 <div
5 data-th.bind="col.label"
6 get-class.call="col.getClass(record)"
7 class.bind="cls"
8 >
9 <compose
10 if.bind="col.view"
11 view.bind="col.view"
12 view-model.bind="col.viewModel"
13 model.bind="{record: record}"
14 ></compose>
15 <record-value
16 if.bind="!col.view"
17 prop.bind="record[col.key]"
18 record.bind="record"
19 get-value.call="col.getValue(record)"
20 title="${col.getValue(record)}"
21 ></record-value>
22 </div>
23</template>

Standard data in the array will use the <record-value> component, but if we want to we can specify a view and view-model to use for this column and it will be generated in this column.

We could probably refactor this code to have the <compose> render the <record-value> component if a view and view-model are not specified. Then we can remove the if.bind and simply have the <compose> tag there.

Hopefully this has been helpful. Feel free to share a comment in areas where you have found <compose> useful.

Discuss on TwitterEdit post on GitHub

Share article
Dustin Davis

Dustin Davis is a software engineer, people manager, hacker, and entreprenuer. He loves to develop systems and automation. He lives with his wife and five kids in Utah.

Join the Newsletter



Dustin Davis