Component basics
Components underpin most of what you do in Aurelia. Learn all there is to know about authoring components, working with dependencies and more.
Custom elements underpin Aurelia applications. They are what you will be spending most of your time creating and can be consisted of the following:
- An HTML template (view)
- A class that constitutes the view model
- An optional CSS stylesheet
Naming Components
The component name, derived from the file name, must contain a hyphen when working with Shadow DOM (see Styling Components). This is part of the W3C Web Components standard and is designed to serve as a namespacing mechanism for custom HTML elements.
A typical best practice is to choose a two to three-character prefix to use consistently across your app or company. For example, all components provided by Aurelia have the prefix
au-
.There are numerous ways in which you can create custom components. By leveraging conventions, you can create simple components with minimal code to more verbose components that offer greater control over how they work.
There is no right or wrong way to create a component. As you will soon see, you can choose whatever works for your needs.
In Aurelia, you can export a plain Javascript class, and it will assume that it is a component as a default convention-based setting. Components, by default, are no different to vanilla Javascript classes.
This is what a basic component in Aurelia can look like. You would add logic and bindable properties (maybe), but a barebones component is just a class.
app-loader.ts
app-loader.html
export class AppLoader {
}
<p>Loading...</p>
So far, we haven't written any Aurelia-specific code.
Conventions mean Aurelia will automatically associate our component view model (
app-loader.ts
) with a file of the same name but with .html
on the end, so app-loader.html
.Don't Skip the Conventions
We highly recommend that you leverage conventions where possible. A few benefits include the following:
- Reduction of boilerplate.
- Cleaner, more portable code.
- Improved readability and learnability of code.
- Less setup work and maintenance over time.
- Ease of migration to future versions and platforms.
If you don't want to use conventions, a
@customElement
decorator allows you to create custom elements verbosely. There are benefits to explicit component creation using the @customElement
decorator, as conventions are bypassed.app-loader.ts
import { customElement } from 'aurelia';
import template from './app-loader.html';
@customElement({
name: 'app-loader',
template
})
export class AppLoader {
}
<p>Loading...</p>
There are many reasons you might want to use the
customElement
decorator. It allows you to specify a different HTML template (or inline template string). You can define the name of the HTML tag used to reference the element, the stylings and other aspects of components that Aurelia would handle for you.Here is how you can avoid a separate HTML view file entirely and define the template inline:
app-loader.ts
import { customElement } from 'aurelia';
@customElement({
name: 'app-loader',
template: '<p>Loading...</p>'
})
export class AppLoader {
}
As you can see, we only have a view model now and specify the view template inline. For simple components that require a view model, it's a nice in-between way of creating components without cluttering your application with additional files.
We've seen that the
customElement
decorator allows us to be explicit on our components. Here are some of the valid configuration options for this decorator.Allows you to configure what the HTML representation of the component will be. In the above example, specifying "app-loader" as the name of the component means we reference it in our views using
<app-loader></app-loader>
It makes no sense when you only want to configure the name to use the object notation. You can do this:
import { customElement } from 'aurelia';
@customElement('app-loader')
export class AppLoader {
}
Referencing the app-loader example again allows you to specify your template contents. You can also use the template configuration property to specify no template by providing null as a value:
import { customElement } from 'aurelia';
@customElement({
name: 'app-loader',
template: null
})
export class AppLoader {
}
If you don't specify a
template
property, Aurelia won't use conventions to find the template either.Components can have explicit dependencies declared from within the
customElement
decorator too. This means you do not have to import them from within your template using <import>
, which can be a nice explicit way to handle dependencies.import { customElement } from 'aurelia';
import { NumberInput } from './number-input';
@customElement({
name: 'app-loader',
dependencies: [ NumberInput ]
})
export class AppLoader {
}
It is worth noting that dependencies can also be specified from within the template using the
import
tag, or they can be globally registered through Aurelia's Dependency Injection layer.Aurelia also has a more verbose API for creating components in your applications. You can use this approach to create components inline without needing separate files. This approach also works nicely for testing.
import { CustomElement } from '@aurelia/runtime-html';
export class App {
MyField = CustomElement.define({
name: 'my-input',
template: '<input value.bind="value">'
})
}
By calling
CustomElement.define
we can create a component using familiar syntax to the verbose decorator approach above, including dependencies and more.While it is good to know what APIs Aurelia provides, in most instances, you won't need to define custom elements inside your Aurelia applications using the
define
functionality. This is helpful when writing tests, which you can learn about here.More often than not, when you create a component in Aurelia, you want to create a view and view model-based component. However, Aurelia uniquely allows you to create components using just HTML.
When working with HTML-only components, the file name becomes the HTML tag. Say you had a component called
app-loader.html
you would reference it using <app-loader></app-loader>
Say you wanted to create a loader component that didn't require any logic and displayed a CSS loader. It might look something like this. We will call this
app-loader.html
app-loader.html
<p>Loading...</p>
To use this component, import and reference it:
<import from="./app-loader.html"></import>
<app-loader></app-loader>
Aurelia infers the component's name from the file name (it strips off the .html file extension).
Sometimes you want a custom element with bindable properties. Aurelia allows you to do this without needing a view model using the
<bindable>
custom element. The following is what you would have used the @bindable
decorator for inside your view model if you had one.app-loader.html
<bindable name="loading"></bindable>
<p>${loading ? 'Loading...' : ''}</p>
Using it in your application would look like this:
<import from="./app-loader.html"></import>
<app-loader loading.bind="mybool"></app-loader>
This replaces the equivalent of a view model-based component, which would use the
bindable
decorator.If you worked with Aurelia 1, you might have been familiar with a feature
@noView
that allowed you to mark your components as viewless (they had a view model but no accompanying view). You probably think, "This sounds a lot like a custom attribute", but not quite. However, there are situations where a custom element without a view is needed.One prime example of a viewless component is a loading indicator using the nprogress library.
import nprogress from 'nprogress';
import { bindable, customElement } from 'aurelia';
import 'nprogress/nprogress.css';
@customElement({
name: 'loading-indicator'
})
export class LoadingIndicator {
@bindable loading = false;
loadingChanged(newValue) {
if (newValue) {
nprogress.start();
} else {
nprogress.done();
}
}
}
In this example, the nprogress library handles adding and removing styles/elements from the DOM, so we omit the template. By choosing not to specify a template, this component will not have an accompanying view.
While a viewless component is a very specific need, and in many cases, a custom attribute is a better option, omitting the
template
property of customElement
you can achieve the same thing.To enable the custom markup to be available within your template or globally, you must register your element. To use a component, you must register it globally or within the component, you would like to use it within.
To register your component to be used globally within your application, you will use
.register
in main.ts
import Aurelia from 'aurelia';
import { MyApp } from './my-app';
import { SomeElement } from './path-to/some-element';
Aurelia
.register(SomeElement)
.app(MyApp)
.start();
To learn more about working with Aurelia's Dependency Injection and registering dependencies, consult the Dependency Injection documentation to learn more.
Adding your element to be used within the template is as easy as adding the following line in your
.html
file. Paths for the import
element are relative, so ensure that your import paths are correct if you encounter any issues.<import from="./path-to/some-element"></import>
In some scenarios, you want the ability to use a component but remove the tags from your markup leaving behind the inner part and removing the outer custom element tags. You can achieve this using containerless functionality.
When using containerless functionality, because the tags are removed from the DOM, you will lose the ability to reference the element container tags. This can make tasks such as using third-party libraries or writing tests more difficult as you lose the reference. We recommend avoiding the use of containerless where possible.
If you are creating components using the
customElement
decorator, you can denote components are containerless using the containerless
boolean configuration property.import { customElement, ICustomElementViewModel } from 'aurelia';
@customElement({
name: 'my-component',
containerless: true
})
export class MyComponent implements ICustomElementViewModel {
constructor() {
}
}
Aurelia provides a convenient decorator to mark your components as containerless.
import { ICustomElementViewModel } from 'aurelia';
import { containerless } from '@aurelia/runtime-html';
@containerless
export class MyComponent implements ICustomElementViewModel {
constructor() {
}
}
When referencing our component using
<my-component></my-component>
Aurelia will strip out the tags and leave the inner part.Inside of your views, you can denote a containerless component by using the
<containerless>
element. Unlike the other approaches, you specify this element inside your HTML views.<containerless>
<p>My custom element markup</p>
Last modified 3mo ago