Get the Newsletter

Aurelia Release Notes - June 2018

Posted by The Aurelia Core Team on June 24, 2018

June is a significant month for Aurelia. Our latest set of releases adds some great new features for our templating engine, allowing more seamless Webpack builds and an even more unobtrusive programming model. We've also made huge improvements to our binding engine, including adding new keywords and beefing up performance. Finally, the Aurelia Store plugin was released! Read on to get the details from the Aurelia Core Team.

Improvements to Binding

Template Literals

The parser now recognizes the backtick (`) character and processes the encapsulated content of two backticks as a template literal . They behave the same way as they do in JavaScript, and you can use them in "normal" .bind="..." expressions as well as in \${...} interpolation expressions. Examples:

Template literal vs. Non-template equivalent

  • `foo` vs. 'foo'
  • `Hello, \${firstName} \${lastName}!` vs. 'Hello, ' + firstName + ' ' + lastName + '!'
  • `Price: $\${dollars}.\${cents}` vs. 'Price: $' + dollars + '.' + cents
  • `Escaped $\{template\}` vs. 'Escaped \${template}'

Sometimes this is easier to read and/or work with, and sometimes it isn't. But now there is a choice.

Tagged Templates

A lesser known (but quite powerful) feature of template literals is tagging. It's the same syntax as literal templates, with the addition of a "tag" (a reference to a function) on its left-hand side. It receives the string parts of the template as an array via the first argument, and any expressions (the parts between ${ and }) as the rest of the arguments.

You can think of it as an inline toView value converter special for strings. A few examples to demonstrate the syntax:

Tagged template vs. Non-template equivalent

  • process`foo` vs. process(['foo'])
  • process`Hello, \${firstName} \${lastName}!` vs. process(['Hello, ', ' ', '!'], firstName, lastName)
  • process`Price: $\${dollars}.\${cents}` vs process(['Price: $', '.', ''], dollars, cents)
  • process`Escaped $\{template\}` vs. process(['Escaped \${template}'])

Per the spec, an additional property raw is placed on the strings array which contains the unescaped string parts. For example: process`foo\tbar` enables leveraging raw in the view model as below:

    
  process(strings) {
    // returns 'foo    bar (raw: foo\tbar)'
    return strings[0] + ' (raw: ' + strings.raw[0] + ')';
  }
  
  

To illustrate the behavior, this is a generic function to process a tagged template literal (similar to how this happens inside the framework):

    
  process(strings, ...expressions) {
    let result = strings[0];
    
    for (let i = 0; i < expressions.length; i++) {
        result += expressions[i] + parts[i + 1];
    }
    
    return result;
  }
  
  

Tagged templates become an interesting tool when there is complex string processing to be done with (nested) conditionals and/or custom transformers. Creating and importing (or globally registering) a value converter may be overkill for something you only need to do once. Tagged templates give you more ways to move some logic to the view model and make things dynamic, whilst keeping the view expressive.

Here are a few examples:

Inline

    
  <span>Enabled: ${item.enabled ? '[X]' : '[ ]'}</span>
  
  

With a tagged template

    
  <span>Enabled: ${mark`${item.enabled}`}</span>
  
  
    
  mark(_, enabled) {
    return enabled ? '[X]' : '[ ]';
  }
  
  

A more contrived example (as it can be solved in other ways):

Inline

    
  <div>${displayDate ? (date | formatDate:'YYYYMMDD') : 'N/A'}</div>  
  
  

With a tagged template

    
  <div>${displayDate ? formatDate`${date}YYYYMMDD` : 'N/A'}</div>
  
  
    
  formatDate(parts, date) {
    return moment(date).format(parts[1]);
  }
  
  

Support for instanceof, in, typeof and void

We've added support for the binary operators instanceof and in, and the unary operators typeof and void, which may come in handy especially for dynamic composition or some exotic behaviors. Here are a few examples:

instanceof

    
  <address-details if.bind="person.address instanceof addressCtor" address.bind="person.address">
  </address-details>
  
  
    
  constructor() {
    this.person = new Person();
    this.addressCtor = Address;
  }
  
  

in + typeof

    
  <template repeat.for="prop of optionalProperties">
    <primitive-element if.bind="prop in model && model[prop] && typeof model[prop] !== 'object'"
        item.bind="model[prop]" name.bind="prop">
    </primitive-element>
    <object-element if.bind="prop in model && model[prop] && typeof model[prop] === 'object'"
        item.bind="model[prop]" name.bind="prop">
    </object-element>
    <null-element if.bind="prop in model && model[prop] == null" name.bind="prop">
    </null-element>
  </template>
  
  
    
  constructor() {
    this.optionalProperties = ['foo', 'bar', 'baz', 'qux'];
    this.model = {
      foo: 'someText', // displayed by primitive-element
      bar: { }, // displayed by object-element
      baz: null // displayed by null-element
    }; // no element is displayed for the 'qux' property as it doesn't exist on model
  }
  
  

void

A more rarely used operator that simply evaluates the right-hand side expression and always returns undefined. A (contrived) example of how this may be useful:

    
  <some-element if.bind="model.requiredProp ? true : void message = 'RequiredProp is missing'">
  </some-element>
  
  

Non-ASCII characters in variable names

In the previous version we only supported variable names with characters in the ASCII range ($, _, a-z, A-Z for IdentifierStart and $, _, 0-9, a-z, A-Z for IdentifierPart). In response to a request from a German user we decided to add support for all 1,353 characters from the Latin script, which (we think) should cover nearly all use cases. You may now do things like this, and the parser won't complain about it:

    
  <input value.bind="Straße">
  <custom-form model.bind="enquète"></custom-form>
  
  

Feel free to reach out if you need any of the remaining 100,000 Unicode characters, which are valid identifier parts.

New Templating Features

We are very excited to announce a set of new features in Aurelia that enable an even simpler way of writing and sharing your applications and plugins. What could previously only be done with the @bindable decorator and conventional view strategy can now alternatively be done with the vanilla static properties $resource and $view. Declaring global resources and adding or writing plugins can now be done by using view model classes and configure functions directly instead of their string module paths.

The following example demonstrates how to write and share a <check-box/> custom element plugin using the new $resource and $view static properties in place of conventions, decorators and bindables:

    
  export class CheckBox {
    static $resource() {
      return {
        name: 'check-box',
        bindables: [
          'label',
          {
            name: 'checked',
            defaultBindingMode: 'twoWay'
          }
        ]
      };
    }
  
    static $view() {
      return `
        <template>
          <label>
            <input type="checkbox" checked.bind="checked">
            \${label}
          </label>
        </template>
      `;
    }
  
    // more implementation...
  }
  
  

The name resource metadata can be omitted if minification is configured to not mangle class names.

Once you've got a custom element like CheckBox you can now take advantage of new resources APIs that allow it to be registered by Type rather than string module id. Here's how you would register CheckBox as a global resource:

    
  export function configure(aurelia) {
    aurelia.use.globalResources([CheckBox])
  }
  
  

Additionally, you could use the same CheckBox element as a local resources in a specific component instead:

    
  export class ContactsPage {
    static $view() {
      return {
        dependencies: [CheckBox],
        template: `
          <template>
            <contact>
              <check-box checked.bind="isRegistered"></check-box>
            </contact>
            <!-- ... -->
          </template>
        `
      };
    }
  
    // more implementation...
  }
  
  

You can also use dependencies: () => [import('/path/to/check-box')] to enable code splitting with popular bundlers!

Notice that there is no trace of any imports from Aurelia. This new capability helps commonly shared resources (custom elements/attributes/value converters/etc) focus on business logic, while still being reusable.

For those who prefer to use decorators over static class fields/methods, we now have @view and @resource decorators that map to the $view and $resource static properties. The way they work is similar to the @inject decorator and static inject field/method:

Instead of this:

    
  export class CheckBox {
    static $view = {
      // ...
    }
  
    static $resource = {
      // ...
    }
  }
  
  

You could also do this:

    
  import { view, resource } from 'aurelia-framework';
  
  @resource({
    // ...
  })
  @view({
    // ...
  })
  export class Checkbox {
  
  }
  
  

In addition to the ways you can use classes described above, you can also leverage them with aurelia.setRoot(...).

Instead of writing this:

    
  export function configure(aurelia: Aurelia) {
    aurelia.setRoot('path/to/app-root');
  }
  
  

It's now possible to use the view model class directly, like this:

    
  import { App } from 'path/to/app-root';
  
  export function configure(aurelia: Aurelia) {
    aurelia.setRoot(App);
  }
  
  

This new capability is enabled via the enhanced API in our composition engine, which also enables the element to work with view model classes instead of a string. As a result, instead of this:

    
  <compose view-model="path/to/${selectedViewModel}-editor"></compose>
  
  
    
  export class Editor {
    selectedViewModel: string;
  
    switch(type: 'date' | 'time') {
      this.selectedViewModel = vm;
    }
  }
  
  

It's now possible to write this:

    
  <compose view-model.bind="selectedViewModel"></compose>
  
  
    
  import { TimeEditor } from 'path/to/time-editor';
  import { DateEditor } from 'path/to/date-editor';
  
  export class Editor {
    selectedViewModel: Function;
  
    switch(type: 'date' | 'time') {
      this.selectedViewModel = type === 'date' ? DateEditor : TimeEditor;
    }
  }
  
  

Besides the above capabilities of linking view dependencies / global resources statically, Aurelia now supports static plugin / feature registration scenarios, as demonstrated via following example of adding aurelia-dialog plugin:

Instead of writing

    
  // main.js
  
  export function configure(aurelia) {
    aurelia
      .use
      .plugin(PLATFORM.moduleName('aurelia-dialog'), config => {
        config.useDefaults();
        config.settings.lock = true;
        config.settings.centerHorizontalOnly = false;
        config.settings.startingZIndex = 5;
        config.settings.keyboard = true;
      });
  }
  
  

It's now possible to write this:

    
  // main.js
  import { configure as configureAureliaDialog } from 'aurelia-dialog';
  
  export function configure(aurelia) {
    aurelia
      .use
      .plugin(configureAureliaDialog, config => {
        config.useDefaults();
        config.settings.lock = true;
        config.settings.centerHorizontalOnly = false;
        config.settings.startingZIndex = 5;
        config.settings.keyboard = true;
      });
  }
  
  

This is the first release of a series of releases that will continue to enable scenarios like this. Currently, there is still work in progress to enable direct view model class reference with the router, so developers who want to leverage modern bundler capabilities can easily compose and build Aurelia applications without any plugin.

Aurelia Store

In case you missed it a couple weeks ago, we recently published the 1.0 release of the Aurelia Store plugin . The new Store plugin enables a single, consistent state solution based on RxJS observables, with seamless integration into Aurelia via both declarative and imperative programming models. You can read the documentation here to learn how to get up and running with this new and powerful state solution.

This has been just a few highlights of things we're excited about. There have been many more features, bug fixes and performance improvements released this month. Please find the details below and we hope you enjoy the new releases!

Major Releases

aurelia-webpack-plugin

No changes from the release candidate. This release moves us out of RC and into full release.

Minor Releases

Minor updates include new features, updates to bugs and performance improvements.

aurelia-logging 1.5.0

Features

  • logging: Allow isDebugEnabled to be returned ( 9e7d571 ), closes #41

aurelia-history-browser 1.2.0

Bug Fixes

  • history-browser: Ensure all navigate() paths return a value ( 47beb9b )
  • index: revert return types ( 0481af4 )
  • typing: update return types ( e1a574a )

aurelia-http-client 1.3.0

  • Fix unit tests.
  • Enable error responses to be shaped like Error objects, addressing some issues with BlueBird.

aurelia-fetch-client 1.4.0

Features

  • fetch-client: add retry functionality ( d16447a )
  • http-client: Expose buildRequest helper API ( 33d364d )
  • http-client: Expose HttpClient to interceptors ( 36518bc )
  • http-client: Forward Request from response interceptor ( cc91034 )
  • interface: add signal to RequestInit interface ( 7a056c0 )

aurelia-route-recognizer 1.2.0

Bug Fixes

  • Require leading slashes to optional segments ( 27f72a5 )

aurelia-dependency-injection 1.4.0

Bug Fixes

  • injection: fail with autoinject and own static inject ( e230bda )
  • resolver: use own property 'inject' in autoinject and parameter decorators ( 724ff08 )
  • resolvers: remove unusable asValue option of the factory decorator ( 5739152 )

Features

  • resolvers: allow using Factory.of and NewInstance.of with registered handlers ( 991cbb5 )

aurelia-binding 2.1.0

Bug Fixes

  • ast: preserve evaluation context for the tag in tagged template literals ( 8528baa )
  • doc: ICollectionObserverSplice Typescript interface ( 92a0006 )
  • EventManager: remove unnecessary dereference ( e2f8866 )
  • expression-cloner: add literal template ( d324785 )
  • literal-template: only throw on invalid function when mustEvaluate is truthy ( 766571d )
  • parser: allow $parent as an argument of binding behaviors and value converters ( f76de45 ), closes #608
  • typescript: put back missing collection properties to ICollectionObserverSplice ( b4f7b28 )
  • typings: accept Set for collectionObserver ( 87ca285 )
  • parser: left-to-right associativity for nested binary expressions with same precedence ( d2d867e )
  • parser: use loop instead of array.fill for IE10 compat ( a7080bd )

Features

  • parser: add basic support for template literals ( 39021ef )
  • parser: add expression operators 'in', 'instanceof', 'typeof', 'void' ( e849661 )
  • parser: add support for non-ASCII identifiers in the Latin-1 supplement block ( dfaca35 ), closes #640
  • parser: add support for tagged template literals ( b9497d2 )

Performance Improvements

  • parser: add node microbench script ( 37dfcb8 )

aurelia-loader-webpack 2.2.1

Bug Fixes

  • eachModule: shortcircuit the iteration when callback returns true ( d5a6d73 )
  • loader-webpack: handle HMR changes also in async modules ( #40 ) ( 36c5de3 )

aurelia-router 1.6.0

Bug Fixes

  • app-router: await navigation result ( 3d1c416 )
  • interfaces: Undo interface export ( e21fc9c )
  • navigation: add NavigationResult type ( 089aa37 )
  • redirect: do not use Promise.reject on redirect ( 487c33d ), closes #480
  • router: define title property ( 2ad2280 )
  • router: make addRoute split list of routes into multiple routeconfigs with single route each ( 0cd5423 )
  • router-configuration: merge options instead of assign in exportRouter ( d563f8a )
  • router-configuration: throw early on invalid pipeline steps ( e6e2f62 )
  • typings: moduleId: string|null ( a7cdb68 )

Features

  • router: add configurable title separator ( d2a1896 )
  • router: redirect with token parameters ( aafa070 ), closes #483
  • type declarations: improve type of RouterConfiguration ( c1775e5 )

aurelia-templating 1.8.0-rc.1

Bug Fixes

  • BehaviorPropertyObserver: use Object.is() for comparison ( bf8ec9a )
  • child-observation: unset property in unbind ( 7989015 )
  • ShadowDOM: unbind view when remove ( b4abe6e )
  • StaticViewStrategy: adjust interface for doc ( 65aef43 )
  • view-slot: fix return type ( 66a86de )

Features

  • CompositionEngine: compose accepts view model constructor ( 524dee4 )
  • decorators: add decorators for view / resource config ( 2af6b28 )
  • resource: aurelialess aurelia ( e7da973 )
  • resources: interop with custom element / attribute decorators ( e06668e )

aurelia-framework 1.3.0-rc.1

Features

  • Aurelia: ability to define root with constructor ( 15fc9dd )
  • config: accepts classes beside module id string ( 80a3d39 )

aurelia-i18n 2.3.0

Features

  • i18n: allow multiple attributes with the same key by listing them comma-separated ( 666cfba )

aurelia-validation 1.2.0

Bug Fixes

  • property-access: make number and string property keys work the same way ( bca8d33 )
  • property-accessor-parser: handle numeric property keys ( 7288a73 )
  • StandardValidator: change to handle empty rule sets ( 85145b8 ), closes #483
  • validation-messages: assign the parser in the constructor ( 7ba58da )
  • ValidationMessageProvider: increase visibility of parser field ( b7636b5 ), closes #464

Features

  • Validation: support integer property keys ( e74ca86 ), closes #474

aurelia-ux 0.11.0

Bug Fixes

  • style: update properties that do not have a watcher on theme change
  • theming: remove ensureDefaultTheme
  • input: conform to new material design standards, add floating label
  • input: add border radius to top of input
  • input: apply correct css variables
  • input: correct background colors and inherit font color
  • input: align hover effect with material specifications
  • input: increase animation length and add variables
  • style: add border radius to outline button

Patch Releases

Patch updates include only fixes to bugs and performance improvements.

aurelia-pal-nodejs 1.1.1

Bug Fixes

  • mutation-observer: MutationObserver not handling changes to CharacterData.data

aurelia-matadata 1.0.4

Bug Fixes

  • metadata: IE cross origin frame Access Denied on Window['frameElement'] in Origin.get ( 5e678d7 )
  • metadata: IE cross origin frame Access Denied on Window['frameElement'] in Origin.get - add test ( 553c5a2 )

aurelia-task-queue 1.3.1

Performance Improvements

  • Improved internal micro task queue flush initiation.