Adds support for Knockout binding syntax to make transition from Durandal and Knockout to Aurelia simpler. You can use Aurelia and Knockout bindings side-by-side to do migration in multiple steps.
This library can be used in the browser only.
Install this plugin with npm:
npm install aurelia-knockout --save
TypeScript definitions are included in the npm package.
To get started with the Aurelia CLI you have to add some lines to aurelia.json
under aurelia_project
after installed
this plugin through npm:
Add this snippet to the dependencies
array in aurelia.json
:
...
"knockout",
{
"name": "aurelia-knockout",
"path": "../node_modules/aurelia-knockout/dist/amd",
"main": "aurelia-knockout"
}
...
Register the plugin at the startup of Aurelia:
export function configure(aurelia) => {
aurelia.use
.standardConfiguration()
.developmentLogging()
.plugin("aurelia-knockout");
...
}
To use the knockout binding syntax in your HTML view you have to add the "knockout" custom attribute to a html element which surrounds all elements which are using knockout syntax:
<template>
<div knockout>
<button data-bind="click: changeVisibility">Change Visibility</button>
<div data-bind="if: isVisible">
<span data-bind="text: firstName"></span>
<br/>
<span data-bind="text: lastName"></span>
</div>
</div>
</template>
This will apply all bindings from this element and its children to the current viewmodel. The bindingContext from Aurelia's "bind" function will be used for that. You have to add the attribute to each view with knockout syntax.
If you used Durandal as your previous SPA-Framework you want to use compositions. You can see the syntax below. As the syntax looks like knockout, the compose binding was not part of knockout itself. This plugin can handle the old Durandal composition syntax.
<template>
<div knockout>
<button data-bind="click: switchView">Switch SubView</button>
<hr>
<div data-bind="compose: view"></div>
<div data-bind="compose: 'testModel2.html'"></div>
</div>
</template>
All cases from the offical Durandal documentation should be covered:
<div data-bind="compose: 'path/to/view.html'"></div>
<div data-bind="compose: 'path/to/module'"></div>
<div data-bind="compose: { view: 'path/to/view.html' }"></div>
<div data-bind="compose: { model: 'path/to/module' }"></div>
<div data-bind="compose: { model: moduleInstance }"></div>
<div data-bind="compose: { view: 'path/to/view.html' model: 'path/to/module' }"></div>
<div data-bind="compose: { view: 'path/to/view.html' model: moduleInstance }"></div>
<div data-bind="compose: moduleInstance"></div>
<div data-bind="compose: moduleConstructorFunction"></div>
You can also pass an object as activationData which is passed as argument to the activate(activationData)
function:
<div data-bind="compose: { model: 'path/to/module', activationData: data }"></div>
The composition logic can handle Knockout Observables as parameters as well. The previous composition will be replaced. The following callback functions will be called:
compositionComplete(element, parentElement)
if the Aurelia completes the composition and
detached(element, parentElement)
before the composed view will be removed from the DOM.
To ensure full flexibility for your migration process, this plugin provides also a feature to set @bindable
properties in rewritten Aurelia sub-views from the activationData which comes from old Knockout based views:
<template>
<div data-bind="compose: { model: 'path/to/submodule', activationData: data }"></div>
</template>
The data object looks like:
{
price: ko.observable(5),
productName: "Apples"
}
<template>
Product: <span>${productName}</span>
<br/>
Price: <span>${price}</span>
</template>
The backing JavaScript code:
import {bindable} from "aurelia-framework";
import {KnockoutBindable} from "aurelia-knockout";
import {inject} from 'aurelia-dependency-injection';
@inject(KnockoutBindable)
export class ProductView {
@bindable
price;
productName;
knockoutBindable;
constructor(knockoutBindable) {
this.knockoutBindable = knockoutBindable;
}
activate(activationData) {
this.knockoutBindable.applyBindableValues(activationData, this);
}
}
This will set the value from activationData.price
to this.price
. this.productName
however, is not
updated because it has no @bindable
decorator and the variable from activationData
is no Knockout
Observable. To process non Knockout Observables anyway you have to pass false
as third parameter to the
applyBindableValues
function. If the outer value changed (and is an Observable) the corresponding inner
variable is updated too.
Subscriptions for Knockout Observables which are created from this plugin internally will be disposed automatically if the child view is unbound.
To build the code, follow these steps.
- Ensure that NodeJS is installed. This provides the platform on which the build tooling runs.
- From the project folder, execute the following command:
npm install
- To build the code, you can now run:
npm run build
-
You will find the compiled code in the
dist
folder, available in these module formats: AMD, CommonJS, ES2015, ES2017, Native Modules and System. -
See
package.json
for other tasks.
To run the unit tests, first ensure that you have followed the steps above in order to install all dependencies and successfully build the library. Once you have done that, proceed with this additional steps:
- You can run the tests with this command:
npm run test