Jmadih
@Jmadih
@JaafarMADIH
Jaafar MADIH
Bonus :
* si on a le temps
Text
Misko Hevery et Adam Abronsw
Il fallait que ça change !
L'annonce de la release Angular 2 le 15/09/2016
Performance
http://rafaelaudy.github.io/angular-1-and-2-state/
“Today, Angular2 is 5x faster than Angular 1”
Misko, 02/10/15
Perspectives
Les objectifs de ce reboot
Text
http://www.typescriptlang.org/docs/
ES5
ES2015
ES2016
ES+
TS
Text
Architecture globale
Une application est un arbre de modules et de composants
Un arbre de modules
AppModule
CoreModule
SharedModule
FeaturesModule
Un module
Une classe avec un décorateur @NgModule
Avec des paramètres :
declarations, imports , exports, providers, bootstrap
Un module est un arbre de composants
AppComponent
HeaderComponent
FooterComponent
WComponent
ToolBarComponent
BannerComponent
AppComponent
Un composant
Un composant
a un cycle de vie
Un composant
a un cycle de vie
Un composant
a un cycle de vie
3 catégories de bindings
3 catégories de bindings
<-- Interpolation --!>
<-- Angular 1 --!>
<h1>{{vm.employee.name}}</h1>
<-- Angular 2 --!>
<h1>{{employee.name}}</h1>
<-- One way binding property --!>
<-- Angular 1 --!>
<h1 ng-bind="vm.employee.name"></h1>
<-- Angular 2 --!>
<h1 [innerText]="employee.name"></h1>
<span [style.backgroundColor]="employee.favouriteColor"></span> Exemples
<-- Two way binding property --!>
<-- Angular 1 --!>
<input ng-model="vm.employee.name"/>
<-- Angular 2 --!>
<input [(ngModel)]="employee.name"/>
<-- Event binding--!>
<-- Angular 1 --!>
<button ng-click="vm.sendForm()">Send</h1>
<-- Angular 2 --!>
<button (click)="sendForm()">Send</h1> 3 catégories de bindings
Exemples
attributs vs propriétés
attributs vs propriétés
<input type="text" value="foo"> <!-- attributs value et type -->
Le navigateur interprète cette balise et crée un objet node en mémoire :
input:
...
value: "foo" // <-- propriétés value et type
type: "text"
...attributs vs propriétés
Pas d'attributs, on est que sur des propriétés (des éléments, composants ou directives)
<button [disabled]="true" >Click</button>
//EQUIVALENT
<button disabled>Click</button>attributs vs propriétés
Et si on veut cibler un attribut ?
Utiliser:
<td [attr.colspan]="5*5">cell</td>
<div [class.isSpecial]="isSpecial">Special class</div>
<div [style.color]="isSpecial ? 'red' : 'green'">Special class</div>
3 catégories
//Element event
<button (click)="onSave()"></button>
//Component event
<hero-detail (deleted)="onDeleted($event)">
//Directive event
<input (change)="firstName = $event" />
On passe la référence de l'événement grâce à $event
Modifie le DOM en ajoutant ou en supprimant des éléments.
<div *ngIf="sopra">{{sopra}}</div>
<div *ngFor="let soprasien of soprasiens">{{soprasien}}</div>
<div [ngSwitch]="status">
<template [ngSwitchCase]="'in-mission'">In Mission</template>
<template [ngSwitchCase]="'ready'">Ready</template>
<template ngSwitchDefault>Unknown</template>
</div><!-- *ngIf -->
<div *ngIf="sopra">{{sopra}}</div>
<!-- Equivalent à ça -->
<template [ngIf]="sopra" >
<div>{{sopra}}</div>
</template>
<!-- *ngFor-->
<div *ngFor"let collab of sopra.collabs;trackBy:custumTrackBy">
{{collab}}
</div>
<!-- Equivalent à ça -->
<template ngFor [ngForOf]="sopra.collabs" [ngTrackBy]="custumTrackBy">
<div>{{sopra}}</div>
</template>Modifie le DOM en ajoutant ou en supprimant des éléments.
Change l'apparence ou le comportement d'un élement
<div [style.width.px]="size"></div>
<td [attr.colspan]="5*5">cell</td>
<div [class.isSpecial]="isSpecial">Special class</div>
<div [style.color]="isSpecial ? 'red' : 'green'">Special class</div>
//superSkill.directive.ts
import {Directive, ElementRef, Renderer} from '@angular/core';
@Directive({
selector: 'superSkill'
})
export class SuperSkillDirective {
@Input() score;
constructor(el: ElementRef, renderer: Renderer) {
if(score > 90){
renderer.setElementStyle(el.nativeElement, 'background', 'red');
}
}
}
//skill.component.html
<div superSkill [score]="skill._score">Super Skill</div>
CUSTUM
this._element.nativeElement.style.color = 'orange';
this._element.nativeElement.innerHTML = ':/';
this._renderer.setElementStyle(this.element.nativeElement, 'color', '#0f0');
this._renderer.setElementProperty(
this.element.nativeElement, 'innerHTML', ':)'
);
Juste une directive avec un template
<app-component>
Hello world...
</app-component>filter
@Pipe()
app.filter('myFilter', function() {
return function(input, optional1, optional2) {
var output;
// Do filter work here
return output;
}
});import {Pipe,
PipeTransform} from '@angular/core';
@Pipe({
name: 'mypipe'
})
export class MyPipe implements PipeTransform{
transform(value: number, args: any[]) {
return newValue;
}
}
import {ModuleWithProviders} from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import {IntroductionComponent} from "./introduction/introduction.component";
import {SkillsComponent} from "./skills/skills.component";
import {AboutComponent} from "./about/about.component";
const routes: Routes = [
{path: '', redirectTo: 'introduction', pathMatch: 'full'},
{path : 'introduction', component: IntroductionComponent},
{path : 'skills', component: SkillsComponent },
{path : 'about', component : AboutComponent},
{path : '**', component: IntroductionComponent}
];
export const AppRouting: ModuleWithProviders = RouterModule.forRoot(routes, {useHash: true});Configuration
import {ModuleWithProviders} from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import {IntroductionComponent} from "./introduction/introduction.component";
import {SkillsComponent} from "./skills/skills.component";
import {SkillsResolve} from "./skills/skills.resolve";
import {AboutComponent} from "./about/about.component";
const routes: Routes = [
{path: '', redirectTo: 'introduction', pathMatch: 'full'},
{path : 'introduction', component: IntroductionComponent},
{path : 'skills', component: SkillsComponent, resolve: {skills: SkillsResolve} },
{path : 'about', component : AboutComponent},
{path : '**', component: IntroductionComponent}
];
export const AppRouting: ModuleWithProviders = RouterModule.forRoot(routes, {useHash: true});Utilisation d'un resolver
import {ModuleWithProviders} from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import {IntroductionComponent} from "./introduction/introduction.component";
import {SkillsComponent} from "./skills/skills.component";
import {SkillsResolve} from "./skills/skills.resolve";
import {AboutComponent} from "./about/about.component";
const routes: Routes = [
{path: '', redirectTo: 'introduction', pathMatch: 'full'},
{path : 'introduction', component: IntroductionComponent},
{path : 'skills',
component: SkillsComponent,
resolve: {skills: SkillsResolve},
canActivate : ['CanAlwaysActivateGuard']},
{path : 'about', component : AboutComponent},
{path : '**', component: IntroductionComponent}
];
export const AppRouting: ModuleWithProviders = RouterModule.forRoot(routes, {useHash: true});import {ModuleWithProviders} from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import {IntroductionComponent} from "./introduction/introduction.component";
import {SkillsComponent} from "./skills/skills.component";
import {SkillsResolve} from "./skills/skills.resolve";
const routes: Routes = [
{path: '', redirectTo: 'introduction', pathMatch: 'full'},
{path : 'introduction', component: IntroductionComponent},
{path : 'skills',
component: SkillsComponent,
resolve: {skills: SkillsResolve},
canActivate : ['CanAlwaysActivateGuard']},
{path : 'about', loadChildren : 'app/about/about.module#AboutCompnent'},
{path : '**', component: IntroductionComponent}
];
export const AppRouting: ModuleWithProviders = RouterModule.forRoot(routes, {useHash: true});
Lazy loading
Configuration complète
Template
<!--app.component.html-->
<header>
<app-menubar></app-menubar>
</header>
<body>
<router-outlet></router-outlet>
</body>
<footer>
<app-footer></app-footer>
</footer><!-- menubar.component.html-->
<md-card class="banner">
<button class="btn" routerLink="/introduction" md-button>Home</button>
<button class="btn" md-raised-button routerLink="/skills" color="primary">Skills </button>
<button class="btn" md-raised-button routerLink="/about" color="primary">About </button>
</md-card>
import {Component, Output, Input, EventEmitter, AfterViewInit} from '@angular/core';
@Component({
selector: 'app-skill-form',
templateUrl: './form.component.html',
styleUrls: ['form.component.scss']
})
export class SkillsFormComponent implements AfterViewInit {
@Input() model: any = {};
@Output() submit$;
constructor() {
this.submit$ = new EventEmitter();
}
ngAfterViewInit() {
console.log(this.model);
}
submit(model){
this.submit$.emit(model);
}
}
<!-- skills.component.html -->
<div class="parentCards">
<div class="cards">
<br>
<img class="skicon" src="/assets/images/skills.jpg" />
<br>
<br>
<div *ngFor="let skill of skills">
<md-card>
<md-card-actions>
<button md-button (click)="edit(skill)" color="primary">EDIT</button>
</md-card-actions>
</md-card>
<br/>
</div>
</div>
</div>
<section class="dialog-modal" [ngClass]="{ 'active': dialogStatus == 'active' }">
<app-skill-form [model]="model" (submit$)="save($event)"></app-skill-form>
</section>
import { Injectable } from '@angular/core';
import {Http} from "@angular/http";
import {BASE_URL} from "../shared/constants";
import {Skills} from "./skills";
@Injectable()
export class SkillsService {
constructor(private _http: Http) { }
get(){
return this._http.get(`${BASE_URL}/api/skills`)
.map( res => res.json() );
}
update(skill : any){
return this._http.put(`${BASE_URL}/api/skills/${skill._id}`, skill)
.map( res => res.json() );
}
}
Local :
@NgModule({
...
providers: [
SkillsService
],
bootstrap: [AppComponent]
})
export class AppModule { }Global :
(root injector)
Accessible par tous les composants sous le module
@Component({
selector: 'app-skills',
templateUrl: './skills.component.html',
styleUrls: ['./skills.component.scss'],
provider : [SkillsService]
})
export class SkillsComponent implements OnInit {
constructor(private _service : SkillsService) { }
}Accessible que dans ce composant et ces enfants
Configuration des providers :
// Values
{ provide: 'CanAlwaysActivateGuard',
useValue: () => {
return true;
}
}
// Alias
{ provide: SkillsService, useClass : SkillsClass}
{ provide: MockedData, useExisting : SkillsService}
// Factory
{ provide: SkillsService,
useFactory : SkillsServiceFactory,
deps: [Logger, UserService]
}
//skills.service.provider.ts
let SkillsServiceFactory= (logger: Logger, userService: UserService) => {
return new SkillsService(logger, userService.user.isAuthorized);
};
2 types :
Template Driven Forms
Model Driven Forms
// app.module.ts
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms';
import { AppComponent } from './app.component';
@NgModule({
imports: [ BrowserModule, FormsModule ], // import forms module here
declarations: [ AppComponent ],
bootstrap: [ AppComponent ]
})
export class AppModule { }Déclaration
Template Driven Forms
// form.component.ts
import {Component} from '@angular/core';
import {Skill} from "../skills";
@Component({
selector: 'app-skill-form',
templateUrl: './form.component.html',
styleUrls: ['form.component.scss']
})
export class SkillsFormComponent implements OnInit {
skill: Skill = {};
ngOnInit(){
this.skill= {
img: '',
title: '';
score: 0;
};
}
save(value, valid){
console.log(value,valid);
}
}
Syntaxes
Template Driven Forms
// form.component.html
<div>
<h1>Add skill</h1>
<form #f="ngForm" (ngSubmit)="save(f.value, f.valid)" novalidate>
<div>
<label>Image</label>
<input type="text"
name="img" [(ngModel)]="skill.img"
#img="ngModel" required>
</div>
<div>
<label>title</label>
<input type="text"
name="title" [(ngModel)]="skill.title"
#title="ngModel" required>
</div>
<div>
<label>score</label>
<input type="number"
name="title" [(ngModel)]="skill.score"
#score="ngModel" required>
</div>
<button type="submit">Submit</button>
</form>
</div>Syntaxes
Template Driven Forms
Les états d'un contrôle
● control.pristine : l’utilisateur n’a pas interagit avec le contrôle
● control.dirty : l’utilisateur a déjà interagit avec le contrôle
● control.valid : le contrôle est valide
● control.invalid : le contrôle n’est pas valide
● control.touched : le contrôle a perdu le focus
● control.untouched : le contrôle n’a pas encore perdu le focus
Click
Pour + d'infos :
Template Driven Forms
// app.module.ts
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { ReactiveFormsModule } from '@angular/forms';
import { AppComponent } from './app.component';
@NgModule({
imports: [ BrowserModule, ReactiveFormsModule], // import forms module here
declarations: [ AppComponent ],
bootstrap: [ AppComponent ]
})
export class AppModule { }Model Driven Forms
Déclaration
Model Driven Forms
Syntaxe
<md-card class="md-whiteframe-2dp">
<md-card-content>
<form novalidate [formGroup]="form">
<table>
<tr>
<div [class.errors]="form.controls._title.errors">
<md-input placeholder="Title" formControlName="_title" minlength="2"
required name="_title" ></md-input>
<span *ngIf="form.controls._title.errors?.minlength"><br>Title is 2 chars min</span>
</div>
</tr>
</table>
</form>
</md-card-content>
<md-card-actions align="end">
<button md-button type="button" (click)="cancel()">Cancel</button>
<button md-button color="primary" type="submit" (click)="submit(form.value)"
[disabled]="!form.valid">Save</button>
</md-card-actions>
</md-card>
<md-card class="md-whiteframe-2dp">
<md-card-content>
<form novalidate [formGroup]="form">
<table>
<tr>
<div [class.errors]="form.controls._title.errors">
<md-input placeholder="Title" formControlName="_title">
</md-input>
<span *ngIf="!form.controls['_title'].valid"><br>Title is 2 chars min</span>
</div>
</tr>
</table>
</form>
</md-card-content>
<md-card-actions align="end">
<button md-button type="button" (click)="cancel()">Cancel</button>
<button md-button color="primary" type="submit" (click)="submit(form.value)"
[disabled]="!form.valid">Save</button>
</md-card-actions>
</md-card>
Model Driven Forms
Syntaxe
<md-card class="md-whiteframe-2dp">
<md-card-content>
<form novalidate [formGroup]="form">
<table>
<tr>
<div [class.errors]="form.controls._title.errors">
<md-input placeholder="Title" formControlName="_title" minlength="2"
required name="_title" ></md-input>
<span *ngIf="form.controls._title.errors?.minlength"><br>Title is 2 chars min</span>
</div>
</tr>
</table>
</form>
</md-card-content>
<md-card-actions align="end">
<button md-button type="button" (click)="cancel()">Cancel</button>
<button md-button color="primary" type="submit" (click)="submit(form.value)"
[disabled]="!form.valid">Save</button>
</md-card-actions>
</md-card>
import {Component, OnInit} from '@angular/core';
import {Mode} from "../../shared/mode.enum";
import {FormGroup, FormControl} from "@angular/forms";
@Component({
selector: 'app-skill-form',
templateUrl: './form.component.html',
styleUrls: ['form.component.scss']
})
export class SkillsFormComponent implements OnInit {
constructor() {
this.form = this._buildForm();
}
private _buildForm() {
return new FormGroup({
_img: new FormControl('', Validators.compose([Validators.required])),
_title : new FormControl('', Validators.compose([Validators.required, Validators.minLength(2)])),
_score: new FormControl('', Validators.compose([Validators.required,Validators.maxLength(2)]))
});
}
}IDE
Languages
Builders
Les commandes de base
Tout de suite !
Initier un projet
Structurer son application :
Header
Body
Footer
*ngFor, *ngIf
CRUD on Skills avec un
Model Driven Form
@Input, @Output
No way !
Utilisation des composants
Click me
Click me
Jmadih
@Jmadih
@JaafarMADIH
Jaafar MADIH