Aurelia 2 is a complete rewrite of Aurelia that shares many of the same loved and familiar features of Aurelia 1. Understandably, in the spirit of progress, not everything is the same. In this section, we are going to guide you through what has changed and how you can migrate over your Aurelia 1 applications to Aurelia 2.
An quickest way to get an application in v1 up an running in v2 is to include the compat package. It can be done via 2 steps:
installing the compat package via
include the compat package into your app:
In v2, preventDefault
is no longer called by default. This breaking change could show up in unexpected places:
click events: in v1, clicking on a button inside a form will not submit the form, while it will in v2, as the click event default behavior is no longer prevented
drag events: in v1, implementing drag/drop will have preventDefault
called automatically, but in v2, they will need to be explicitly called by the application
Sometimes, if it's desirable to call preventDefault
in an event binding, use prevent
modifier, like the following example:
Read more about modifiers in event modifier doc here
In v2, when trying to bind with a non-existent property, the closest boundary scope will be selected, instead of the immediate scope of the binding (v1 behavior).
observeProperty
has been renamed to observe
In v1, if you happen to use .observeProperty
method from bindings in your application/library, then change it to observe
instead. The parameters of the signature remain the same.
sourceExpression
has been renamed to ast
In v1, if you happen to use .sourceExpression
property from bindings in your application/library, then change it to ast
instead. The type of the property remains the same.
In v1, enhance
method on an Aurelia
instance has the signature:
In v2, enhance
method on an Aurelia
instance has the signature:
Parent container and resources can be specified through this config.
In v2, in order to get a reference to the underlying component view model, use component.ref
instead of view-model.ref This is to make terminologies consistent as we are moving towards component oriented terms.
The primary property of If
has been renamed from condition
to value
. If you are using if.bind
, you are not affected. If you are using the multi prop binding syntax, the template looks like this:
Change it to:
BindingEngine
has been removed in v2, but can still be imported from @aurelia/compat-v1
package for ease of migration. The collectionObserver
method on the compat package of BindingEngine
is not the same with v1, per the follow comparison: v2
v1
.delegate
command has been removed, use .trigger
instead. With shadow DOM, even though .delegate
works, it doesn't feel as natural as .trigger
, and the performance benefits .delegate
command used to give when browsers were slow adding many event listeners is no longer as big.
.call
command has been removed, use lambda functions instead to create function that preserves the this
context. Refer to lambda expression
<compose>
has been renamed to <au-compose>
. The bindable properties of this component have also been changed:
viewModel -> component
view -> template
model remains the same
Examples migration fix:
In Aurelia 2, all bindings are passed through to the underlying custom element composition, so component.ref
(view-model.ref
in v1) no longer means getting a reference to the composer, but the composed view model instead.
Read more about dynamic composition in v2 in this dynamic composition doc and dynamic ui composition doc.
Templates no longer need to have <template>
tags as the start and ending tags. Templates can be pure HTML with enhanced Aurelia markup but <template>
doesn't need to be explicitly defined.
PLATFORM.moduleName
is gone. This was to address a limitation in Aurelia 1. Aurelia 2 now works well with all bundlers and does not require the addition of this code to use code splitting or tell the bundler where template code is.
Better intellisense support for TypeScript applications. Using the new injection interfaces, you can now inject strongly typed Aurelia packages such as Fetch Client, Router or Internationalization. These packages are prefixed with an "I" such as IHttpClient
, IRouter
and so on.
Remove automatic au- prefix
Remove auto-conversion of Aurelia element -> WC element. Applications need to explicitly define this. This should make mix-matching & controlling things easier.
The first entry point of an Aurelia application is the main HTML page-loading and hosting.
All the initial settings for starting and working with an Aurelia project are done in this file.
What does PLATFORM.moduleName
do?
Whenever you reference a module by string, you need to usePLATFORM.moduleName("moduleName")
to wrap the bare string. PLATFORM.moduleName
is designed to teachWebpack
about Aurelia's dynamic loading behavior.
What is a globalResources
?
When you create a view in Aurelia, it is completely encapsulated so you mustrequire
components into an Aurelia view. However, certain components are used so frequently across views that it can become very tedious to import them over and over again. To solve this problem, Aurelia lets you explicitly declare certain "view resources" as global.
What is a feature
?
Sometimes you have a whole group of components or related functionality that collectively form a "feature". This "feature" may even be owned by a particular set of developers on your team. You want these developers to be able to manage the configuration and resources of their own feature, without interfering with the other parts of the app. For this scenario, Aurelia provides the "feature".
What is a plugin
?
Similar to features, you can install 3rd party plugins. The main difference is that a "feature" is provided internally by your application, while a plugin is installed from a 3rd party source through your package manager.
What does setRoot()
do?
Instantiates the root component and adds it to the DOM.
One of the best and most exciting changes has been made in this section.
What happened to PLATFORM.moduleName
?
Aurelia 2 works with any bundler without limitation or specific configuration so I'm sure you guessed it, you don't need PLATFORM.moduleName("moduleName")
anymore.
Is globalResources
still supported?
Yes, Any component or class you add to the applicationregister()
will be globally accessible through DI.
How can I have a plugin
?
If you are creating aplugin
, then the usual practice is to export
a configuration object. That can be registered in the client code. As a best practice, we recommend an alternate approach to registering each component individually in this way. Instead, create a folder where you keep all your shared components. In that folder, create a registry.ts
module where you re-export your components. Then, import that registry module and pass it to the application's register method at startup.
For example:
What happened to feature
?
This is conceptually similar toplugin
so you can do the same for internal use.
Where is the setRoot()
?
The app()
method is equivalent of the setRoot()
.
The root of any Aurelia application is a single
component, which contains everything within the application, actually, the root component.
To import any style, component or etc, you should userequire
.
Wrapping the whole HTML content viatemplate
isnecessary
.
Unlike version 1, There is a convention for loading your CSS file when the name is the same as the component, just like my-app.css
, so you don't need to import it manually.
To import any style, component or etc you should use import
. An alternative to require
in version 1. By default, the components you create aren't global. What that means is that you can't use a component within another component, unless that component has been imported.
Wrapping the whole HTML content via template
is optional
.
Every component instance has a life-cycle that you can tap into. This makes it easy for you to perform various actions at particular times
Which life-cycle hooks are most used?
Such cases can be summarized.
A dependency injection container is a tool that can simplify the process of decomposing such a system. Oftentimes, when developers go through the work of destructuring a system, they introduce a new complexity of "re-assembling" the smaller parts again at runtime. This is what a dependency injection container can do for you, using simple declarative hints.
Writing debug output while developing is great. This is how you can do this with Aurelia.
Write an appender.
In the main(.js|.ts)
You can register LoggerConfiguration
as following
Usage
How to write an appender
?
How to write a sink
?
How to register appender
and sink
into the Aurelia container?
Finally, The usage
| Name | Aurelia 1 | Aurelia 2 | Description | | ---- | - | - | | ref | ✓ | ✓ | | | view-model.ref | ✓ | ✓ | deprecated in v2 | | component.ref | ✗ | ✓ | Not in v1 |
{% hint style="info" } In v2, if an expression return a function, that function will be use as the handler for the event. V1 only evaluates the expression. {% endhint }
General
Event
Repeater
@computedFrom
tells the binding system which expressions to observe. When those expressions change, the binding system will re-evaluate the property (execute the getter).
In Aurelia 2, The framework automatically computes observation without the need for any configuration or decorator.
This feature is totally new for Aurelia 2.
aurelia-app
attribute helps us to introduce our entry point, themain.ts
file, which includes the configurations of the project.
In Aurelia 2, it is a little different, you need to call your root component (<my-app>
in this example) but
What happened to aurelia-app
?
There is noaurelia-app
in Aurelia 2. Themain.ts
file will detect via the project configuration.
Aurelia 1 has a restriction and the community made an plugin that is called after all child components are attached, and after all two-way bindings have completed. Theattached
life-cycle in version 2 covers this scenario.
constructor
constructor
✗
define
✗
✗
hydrating
✗
✗
hydrated
✗
✗
created
created
✗
binding
bind
✓
bound
✗
✓
attaching
✗
✓
attached
attached
✓
detaching
✗
✓
unbinding
unbind
✓
dispose
✗
✗
binding
Fetch data (working with API services & Ajax calls), initialize data/subscriptions.
bound
Any work that relies on fromView/twoWay binding data coming from children, Defining router hooks.
attached
Use anything (like third-party libraries) that touches the DOM.
unbinding
Cleanup data/subscriptions, maybe persist some data for the next activation.
dispose
One way cleanup all the references/resources. This is invoked only once and is irreversible
container.createChild()
DI.createContainer()
-
container.registerSingleton(key: any, fn?: Function)
Registration.singleton(key: any, value: Function): IRegistration
-
container.registerTransient(key: any, fn?: Function)
Registration.transient(key: any, value: Function): IRegistration
-
container.registerInstance(key: any, instance?: any)
Registration.transient(key: any, value: any): IRegistration
-
container.registerHandler(key, handler)
Registration.callback(key: any, value: ResolveCallback): IRegistration
-
container.registerResolver(key: any, resolver: Resolver)
container.registerResolver(key: any, resolver: IResolver)
-
container.autoRegister(key: any, fn?: Function
✗
-
✗
Registration.alias(originalKey: any, aliasKey: any): IRegistration
-
container.get(MyService)
container.get(MyService)
-
✗
container.getAll(MyService)
-
@singleton
✓
-
@transient
✓
-
@inject(MyService)
@inject(MyService)
-
@autoinject()
✗
@inject(Lazy.of(MyService))
@inject(lazy(MyService))
-
@inject(All.of(MyService))
@inject(all(MyService))
-
@inject(Optional.of(MyService))
@inject(optional(MyService))
-
@inject(Parent.of(MyService))
✗
-
@inject(Factory.of(MyService))
@inject(factory(MyService))
-
@inject(NewInstance.of(MyService))
@inject(newInstanceForScope(MyService))
-
✗
@inject(newInstanceOf(MyService))
-
canActivate
if the component can be activated.
activate
when the component gets activated.
canDeactivate
if the component can be deactivated.
deactivate
when the component gets deactivated.
canLoad
canActivate
✓
loading
activate
✓
canUnload
canDeactivate
✓
unloading
deactivate
✓
${ }
✓
one-way
✓
to-view
✓
from-view
✓
two-way
✓
one-time
✓
bind
✓
call
✓
trigger
✓
delegate
✓
capture
✓
$this
✓
The view-model that your binding expressions are being evaluated against.
$event
✓
The DOM Event in delegate
, trigger
, and capture
bindings.
$parent
✓
✓
$parent.$parent.$parent.name
✓
✓
$index
✓
✓
$first
✓
✓
$last
✓
✓
$even
✓
✓
$odd
✓
✓
$length
✗
✓
✓
✗
property
${a}
syntax:
✓
✓
observation:
✓
✓
member
${a.b}
syntax:
✓
✓
observation:
✓
✓
value conveter
${a | convert: value }
syntax:
✓
✓
observation:
✓
✓
binding behavior
${a & behavior: config }
syntax:
✓
✓
observation:
✗
✗
function call
${doThing(param)}
syntax:
✓
✓
observation:
✓
✓
array methods
${items.join(', ')}
syntax:
✓
✓
observation (on array):
✗
✓
lambda
${items.filter(x => x.v > 70)}
syntax:
✗
✓
observation:
✗
✓
Aurelia 1 was released in 2015. In the years that have passed, an ecosystem of user-created libraries and plugins has been created. If you are a plugin author of a v1 plugin or looking to port over a plugin to v2, this is the section you are looking for.
Before proceeding, you should read through the parent section to understand the differences between v1 and v2.
In Aurelia 2, the plugin
method you called in v1 is no more. Plugins in Aurelia 2 are no different to components and other resources. You pass them to the register
method inside your bootstrap code to load them.
There are numerous ways you can create Aurelia 2 plugins or port over v1 plugins. However, the easiest reference point is to look at how other plugins have been ported over to Aurelia 2.
Please keep in mind these are third-party plugins, and the Aurelia team claims no responsibility for the quality and safety of the code. Use them as a reference point for your own Aurelia 2 plugins, but use precaution.