Today's release comes with what we believe is the final set of big breaking changes. We've also got new features, improvements and a few other goodies.

Changes

ES2016 Metadata

There's currently work being done to standardize how metadata is attached to classes, members, etc. In this release, we remove Aurelia's metadata implementation and replace it with the emerging ES2016 standard. This causes breaking changes if you were working directly with metadata. However, if you were using Aurelia's decorators or the fallback apis, you should not see any changes, since this is mostly an implementation detail. If you are interested in the emerging Metadata Reflection API, you can read the detailed spec and have a look at the polyfill that we've incorporated.

Positive Side Effects

Now that we've changed the way that metadata works, we've been able to remove some inheritance that was previously required by the framework. For example, in the dependency injection library, we were able to remove the base classes InstanceActivator and Registration . Now, if you want to customize how DI works using these extensibility points, you just implement a class with a particular api and then use the decorator api to associate it with a class. A related benefit is that, by removing some inheritance throughout the framework, we do get a performance increase in certain areas.

There's another benefit to this that relates to yesterday . The latest version of TypeScript will emit type metadata for classes using the Reflect Metadata API if you compile your code with the --emitDecoratorMetadata flag. To support this, there's a new decorator we've added which you can use on your TypeScript classes:

@ autoinject ( ) export class Foo { constructor ( public bar : Bar ) { } }

By using @autoinject on a class in combination with the --emitDecoratorMetadata compiler flag, Aurelia's dependency injection system will be able to automatically determine the constructor types. Sweet!

Enumerations

While JavaScript doesn't have an enumeration type, there are several areas in Aurelia where we use enumeration-ish values. We've made a couple of breaking changes in order to make this consistent across all our libraries. All enumerations are now named using singular words. This means that the levels enum in the logging library is now named logLevel and is exported as follows:

export var logLevel = { none : 0 , error : 1 , warn : 2 , info : 3 , debug : 4 } ;

The binding modes, which were not grouped together at all, are now grouped under the export bindingMode as follows:

export var bindingMode = { oneTime : 0 , oneWay : 1 , twoWay : 2 } ;

The same type of change was made to the router's, also formerly ungrouped, activation strategies:

export var activationStrategy = { noChange : 'no-change' , invokeLifecycle : 'invoke-lifecycle' , replace : 'replace' } ;

Naming Consistency

There were a few places across the libraries where different naming schemes were being used. We've tried to patch those up. In particular, we've replaced the use of "uri" through our APIs with a consistent use of "url". This is a breaking API change to HttpRequestBuilder and HttpRequestMessage . To update, replace uses of withUri , withBaseUri , and uri with withUrl , withBaseUrl , and url , as appropriate. Another place was our plugin API. Previously plugins needed to implement an install method but everything else in Aurelia used a configure terminology. We've change plugins to now use a configure method so that it's the same everywhere.

Routing

By far, the biggest set of changes in these releases are to the router. There were a number of long-standing issues with the router that have now been resolved, thanks to the excellent work of core team member Bryan Smith .

Router Configuration

Prior to this release, configuring the router has been a bit odd. One needed to have a router injected and then set it as a property named "router". Additionally, if you wanted to inject a router to control navigation but not to set up a child navigation, there were problems. To address these issues, we've changed the way configuring routers works. To configure a router, you now implement a configureRouter method. The method will receive the configuration object as its first parameter and the router instance as is second parameter. If you need the router, perhaps because you want to use the navigation model, then you can just store it, but if not, you can ignore it. Here's a look at the navigation skeleton's app class after this change:

export class App { configureRouter ( config , router ) { config . title = 'Aurelia' ; config . map ( [ { route : [ '' , 'welcome' ] , moduleId : './welcome' , nav : true } , { route : 'flickr' , moduleId : './flickr' , nav : true } , { route : 'child-router' , moduleId : './child-router' , nav : true } ] ) ; this . router = router ; } }

As as result of this change, you can now have a router injected into any class without accidentally creating a new child navigation system. The router that is injected will be the closest router in the navigation hierarchy and can be used to do programmatic navigation or route generation.

Route Generation

The latest version of the router now has full support for route generation so that you no longer need to hard-code route patterns in your HTML or construct them in your JavaScript. To leverage this, simply name the route in your configuration. If you want to generate a route based on data in JavaScript, you use the generate method like so:

let href = router . generate ( routeName , params ) ;

The generation mechanism understands whether you are running in hash change or push state mode, how your routes are parameterized and how to construct query strings. Just give it the name and a params object with key/value pairs for the route parameters and query string data.

You will often want to generate routes in your HTML so that you can link to different app states, but without hardcoding the route patterns. Aurelia now provides a route-href custom attribute that you can use to accomplish this. It takes route and params properties. Here's an example using it, based on our contact app sample:

< a route - href = "route: contacts; params.bind: {id:contact.id}" > < / a >

This will generate the route named "contacts" based on the provided "id" parameter.

Activation Parameters

As part of the work on route generation, and in an effort to encapsulate knowledge of route patterns, we've changed the way the canActivate and activate hooks receive data. We've simplified the provided data by combining the route parameters and query string values into a single key/value pair object. This makes the api mirror the generation api and allows the developer to not need to remember or lookup wether a given input is coming from a route parameter or a query string. The updated lifecycle method signature is as follows: