aurelia meetup paris

41
Overview Aurelia

Upload: ahmed-radjdi

Post on 14-Apr-2017

602 views

Category:

Presentations & Public Speaking


2 download

TRANSCRIPT

Overview

Aurelia

• Front-end developer

Ahmed Radjdi

1. Get started

2. Dependency Injection

3. Routing

4. Databinding

5. Composition

6. Eventing

Programme

• Aurelia is a next gen JavaScript client framework that leverage the technology of the future but target today's mobile, desktop and browser environments.

Aurelia

http://aurelia.io/

• use JSPM and SystemJS

• Written with ES6 and ES7

• Integrates with Web Components

• Designed for Evergreen Browsers as Chrome, Firefox, IE11 and Safari 8.

• (Soon IE9 and above)

Get started

Install dependencies

Install Gulp npm install -g gulp

Install JSPM npm install -g jspm

Install Yeoman generator npm install -g yo generator-aurelia

Generate Aurelia application yo aurelia

Loading appBootstrapping <!-- index.html --> <!doctype html> <html> ... <body aurelia-app=“main”> ... <script src="jspm_packages/system.js"></script> <script src="config.js"></script> <script> System.import('aurelia-bootstrapper'); </script> </body> </html>

// src/main.js export function configure(aurelia) { aurelia.use .standardConfiguration() .developmentLogging();

aurelia.start().then(a => a.setRoot()); }

Views & viewmodelsTo create a viewmodel, edit an JS file that export a class // app.js export class App { firstName = 'John'; lastName = 'Doe';

@computedFrom('firstName', ‘lastName') get fullName(){ return `${this.firstName} ${this.lastName}`; } submit(){ alert(`Welcome, ${this.fullName}!`); } }

To create a view, edit an HTML file with an HTMLTemplate inside <!-- app.html --> <template> <input type="text" value.bind="firstName"> <input type="text" value.bind="lastName"> <p>${fullName}</p> <input type="submit" click.trigger="submit()">Submit</button> </template>

Dependency Injection

Dependency InjectionInject services to constructor import {inject} from 'aurelia-framework'; import {HttpClient} from 'aurelia-fetch-client';

@inject(HttpClient) export class MyViewModel { constructor(http) { this.http = http; } }

DI container assumes that everything is a singleton instance. To provide new instances, use transient decorator import {transient} from 'aurelia-framework';

@transient() export class MyViewMdel { ... }

Dependency InjectionLazy.of import {Lazy, inject} from 'aurelia-framework'; import {HttpClient} from 'aurelia-fetch-client';

@inject(Lazy.of(HttpClient)) export class MyViewModel { constructor(getHttp) { this.getHttp = getHttp; } }

All.of import {All, inject} from 'aurelia-framework'; import {Message} from ‘my-messages-class‘;

@inject(All.of(Message)) export class MyViewModel { constructor(messages) { this.messages = messages; } }

Optional.of import {Optional, inject} from 'aurelia-framework'; import {HttpClient} from 'aurelia-fetch-client';

@inject(Optional.of(HttpClient)) export class MyViewModel { constructor(http) { this.http = http; } }

Routing

Configure routes

export class MyViewModel { configureRouter(config, router){ this.router = router;

config.title = 'Router'; config.map([ // Static routes - Matches the string exactly. { route: ['', 'home'], name: 'home', moduleId: 'home/index' }, { route: 'users', name: 'users', moduleId: 'users/index'}, // Parameterized routes - Matches the string and then parses an id // parameter. Your view-model's activate callback will be called with an // object that has an id property set to the value that was extracted // from the url. { route: 'users/:id', name: 'user', moduleId: 'users/detail' }, // Wildcard routes - Matches the string and then anything that follows // it. Your view-model's activate callback will be called with an object // that has a path property set to the wildcard's value { route: 'files*path', name: 'files', moduleId: 'files/index'} ]); } }

Navigation// app.js export class App { configureRouter(config, router){ this.router = router;

config.title = 'Router'; config.map([ { route: 'home', name: 'home', moduleId: 'home/index', nav: true }, { route: 'users', name: 'users', moduleId: 'users/index', nav: true }, { route: 'users/:id', name: 'user', moduleId: 'users/detail' } ]); } }

<!-- app.html --> <template> <div class=“spinner" show.bind="router.isNavigating"></div> <ul> <li repeat.for="item of router.navigation"> <a href.bind="item.href">${item.title}</a> </li> </ul>

<router-view></router-view> </template>

Intercept navigationexport class MyViewModel { configureRouter(config) { config.title = 'Aurelia'; config.addPipelineStep('authorize', AuthorizeStep); config.map([ { route:'home', name: 'home', moduleId: 'home/index', nav: true }, { route:'users', name: 'users', moduleId: 'users/index', nav: true }, { route:'users/:id', name: 'user', moduleId: 'users/detail' } ]); } }

class AuthorizeStep { run(routingContext, next) { // some logic return next.cancel(new Redirect('login')); // or return next(); } }

Multiple viewports

export class EditorViewModel { configureRouter(config) { config.map([ { route: 'edit', viewPorts: { left: { moduleId: 'editor' }, right: { moduleId: 'preview' } } } ]); } }

<template> <div class="left-side"> <router-view name="left"></router-view> </div> <div class="right-side"> <router-view name="right"></router-view> </div> </template>

ActivationActivation Lifecycle

export class MyViewModel { canActivate(params, routeConfig, navigationInstruction) { // some logic return true; // false or Promise or navigation command } activate(params, routeConfig, navigationInstruction) { // some logic return true; // false or Promise } canDeactivate() { // some logic return true; // false or Promise or navigation command } deactivate() { // some logic return true; // false or Promise } }

CompositionComposition Lifecycle export class MyViewModel { bind(bindingContext) { // some logic } unbind() { // some logic } attached() { // some logic } detached() { // some logic } }

Demo Githoubhttps://github.com/aradjdi/AureliaGithoub

git checkout step-1

Databinding

CommandsBind, one-way, two-way & one-time <template> <input type.one-time="inputType" name.one-way="inputName" value.two-way="inputValue">

</template>

Default binding behaviour is one-way binding for everything except form controls which is two-way binding. The example bellow is equivalent : <template> <input type.one-time="inputType" name.bind="inputName" value.bind=“inputValue">

</template>

Delegate, trigger & call <template> <form submit.delegate="submit($event)"> <button type="submit">Submit</button> </form> </template>

Templating attributesShow - Changes visibility of Element<template> <div show.bind="isSaving" class="spinner"></div> </template>

If - Add or remove the Element <template> <div if.bind="isSaving" class="spinner"></div> </template>

Repeat - Render a template multiple times <template> <ul> <li repeat.for="item of items">${item.content}</li> </ul> </template>

Expression, variable, converterString interpolation - ${expression} <template> <div class="dot ${color} ${isHappy ? 'green' : 'red'}"></div>

</template>

Value Converter - [expression] | [converterName]:[parameterExpression] <template> <title>${title | upper}</title> <input type="text" value.bind="fullName | upper">

</template>

Expression, variable, converterRef - create a local name for an element

• element.ref="someIdentifier" - Create a reference to the HTMLElement in the DOM.

• attribute-name.ref="someIdentifier"- Create a reference to a custom attribute's view-model.

• view-model.ref="someIdentifier"- Create a reference to a custom element's view-model.

• view.ref="someIdentifier"- Create a reference to a custom element's view instance (not an HTML Element).

• controller.ref="someIdentifier"- Create a reference to a custom element's controller instance.

<template> <input type="file" element.ref=“image"> <!-- use image without adding as attribute in viewmodel --> <img src.bind="image.value"/>

</template>

Others attributesAnd many others :

• value • model • checked • style • css • innerhtml • textcontent • … • create custom attribute also

Two-way data-binding is supported with contenteditable elements :

<template> <div textcontent.bind="contentString" contenteditable="true"></div> </template>

Binding computed propertiesSimple property > Object.observe > emulating Object.observe

Computed properties > Dirty checkin

To prevent Dirty checking import {computedFrom} from 'aurelia-framework';

export class Welcome{ firstName = 'John'; lastName = 'Doe';

@computedFrom('firstName', 'lastName') get fullName(){ return `${this.firstName} ${this.lastName}`; } }

Composition

ViewsCompose a view or a viewmodel <template> <compose view-model.bind="viewmodelName"></compose> </template>

<template> <compose view.bind="viewName"></compose> </template>

Aurelia can dynamically load components or per-view style sheets <template> <require from=“styles.css”></require> … </template>

Custom ElementCustom Elements add new tags to your HTML markup. // hello-world.js import {customElement, bindable} from 'aurelia-framework';

@customElement(‘hello-world') export class HelloWorld { @bindable to;

sayHello(){ alert(`Hello ${this.to}!`); } }

<!-- hello-world.html --> <template> <button click.trigger="sayHello()">Say Hello To ${to}</button> </template>

<!-- welcome.html --> <template> <require from=“hello-world”></require>

<input type="text" ref="name"> <hello-world to.bind=“name.value"></hello-world> </template>

Custom ElementCustom Elements as template. <!-- hello-world.html --> <template bindable="to"> <div>Hello ${to}</div> </template>

<!-- welcome.html --> <template> <require from=“hello-world.html”></require>

<input type="text" ref="name"> <hello-world to.bind=“name.value"></hello-world> </template>

Custom ElementCustom Elements without view // hello-world.js import {customElement, noView, bindable} from 'aurelia-framework';

@customElement(‘hello-world’) @noView() export class HelloWorld { @bindable to;

toChanged(newValue, oldValue) { alert(`Hello ${this.to}!`); } }

<!-- welcome.html --> <template> <require from=“hello-world”></require>

<input type="text" ref="name"> <hello-world to.bind=“name.value"></hello-world> </template>

Custom Element Decorator

• @useView(path) • @useShadowDom() • @inlineView(markup, dependencies?) • @containerless() • @children(selector) • @child(selector) • @processContent(false|Function) • … • create custom decorator also

Creating Decorator :

Demo Githoubhttps://github.com/aradjdi/AureliaGithoub

git checkout step-2

Killer featureRecursive Elements <!-- tree.html --> <template bindable="nodes"> <require from=“node”></require>

<ul> <li repeat.for="node of nodes”> <node if.bind=“node.isLeaf" content.bind=“node.value"></node> <tree if.bind=“node.isTree" nodes.bind=“node.nodes”></tree> </li> <ul>

</template>

Demo Githoubhttps://github.com/aradjdi/AureliaGithoub

git checkout step-3

Eventing

EventAggregatorPublishing on a channel - Subscribing to a channel // a-publisher.js import {inject} from 'aurelia-framework'; import {EventAggregator} from 'aurelia-event-aggregator';

@inject(EventAggregator) export class APublisher { constructor(eventAggregator) { this.eventAggregator = eventAggregator; }

publish(){ this.eventAggregator.publish('channel name here', data); } }

// b-subscriber.js import {inject} from 'aurelia-framework'; import {EventAggregator} from 'aurelia-event-aggregator';

@inject(EventAggregator) export class BSubscriber { subscription; constructor(eventAggregator) { this.eventAggregator = eventAggregator; }

unsubscribe() { this.subscription.dispose(); } subscribe() { this.subscription = this.eventAggregator.subscribe(‚Àòchannel name here', payload => {...}); } }

Event AggregatorPublishing on a message - Subscribing to a message //some-massage.js export class SomeMessage{ }

//a-publisher.js import {inject} from 'aurelia-framework'; import {EventAggregator} from 'aurelia-event-aggregator'; import {SomeMessage} from './some-message';

@inject(EventAggregator) export class APublisher { constructor(eventAggregator) { this.eventAggregator = eventAggregator; }

publish() { this.eventAggregator.publish(new SomeMessage()); } }

//b-subscriber.js import {inject} from 'aurelia-framework'; import {EventAggregator} from 'aurelia-event-aggregator'; import {SomeMessage} from './some-message';

@inject(EventAggregator) export class BSubscriber { subscription; constructor(eventAggregator) { this.eventAggregator = eventAggregator; }

unsubscribe(){ this.subscription.dispose(); } subscribe(){ this.subscription = this.eventAggregator.subscribe(SomeMessage, message => {...}); } }

Demo Githoubhttps://github.com/aradjdi/AureliaGithoub

git checkout step-4

Conclusion

• aurelia-animate

• aurelia-animator-velocity

• aurelia-flux

• aurelia-validation

• aurelia-breeze

Questions ?

Thank you