« Angular » : différence entre les versions
(→Format) |
|||
(32 versions intermédiaires par le même utilisateur non affichées) | |||
Ligne 125 : | Ligne 125 : | ||
= Component = | = Component = | ||
A component represents a view. | A component represents a view. [https://angular.io/cli/generate generate] | ||
<kode lang='bash'> | <kode lang='bash'> | ||
# from src/app | |||
ng generate component '[component-name]' | ng generate component '[component-name]' | ||
# CREATE src/app/[component-name]/[component-name].component.html | # CREATE src/app/[component-name]/[component-name].component.html | ||
Ligne 133 : | Ligne 134 : | ||
# CREATE src/app/[component-name]/[component-name].component.css | # CREATE src/app/[component-name]/[component-name].component.css | ||
# | # --dry-run | ||
# | # --inline-template use inline template instead of creating an html file | ||
# --inline-style use inline style instead of creating a css file | |||
# --skip-tests do not create "spec.ts" test files | |||
# | # Error could not find an NgModule | ||
# --skip-import do not import this component into the owning NgModule | |||
# --skip- | |||
</kode> | </kode> | ||
Ligne 148 : | Ligne 150 : | ||
<filebox fn='src/app/[component-name]/[component-name].component.ts'> | <filebox fn='src/app/[component-name]/[component-name].component.ts'> | ||
import { Component } from '@angular/core'; | import { Component } from '@angular/core'; | ||
@Component({ | @Component({ | ||
// nom de la directive pour l’utilisation comme tag HTML | // nom de la directive pour l’utilisation comme tag HTML | ||
Ligne 162 : | Ligne 162 : | ||
styles: `div { background: pink; }` | styles: `div { background: pink; }` | ||
}) | }) | ||
export class | export class ComponentName { | ||
// propriété bindée dans le template | // propriété bindée dans le template | ||
pageTitle: string = 'My Title'; | pageTitle: string = 'My Title'; | ||
Ligne 192 : | Ligne 192 : | ||
{{info | À la création d'un nouveau {{boxx|component}}, penser à le déclarer dans le {{boxx|module}}.}} | {{info | À la création d'un nouveau {{boxx|component}}, penser à le déclarer dans le {{boxx|module}}.}} | ||
= Module = | <filebox fn='src/app/app.module.ts'> | ||
import { ComponentName } from './[component-name]/[component-name].component'; | |||
@NgModule({ | |||
declarations: [ | |||
ComponentName | |||
] | |||
}) | |||
export class AppModule {} | |||
</filebox> | |||
= [https://angular.io/guide/feature-modules Feature Module] = | |||
Permet d'organiser les components par groupes. Un module par fonctionnalité. | Permet d'organiser les components par groupes. Un module par fonctionnalité. | ||
<kode lang='powershell'> | <kode lang='powershell'> | ||
ng generate module | # from src/app | ||
ng generate module [module-name] | |||
# --dry-run / -d | # --dry-run / -d | ||
# create a folder [module-name] and a file [module-name]/[module-name].module.ts | |||
</kode> | </kode> | ||
<filebox fn=' | <filebox fn='src/[module-name]/[module-name].module.ts'> | ||
import { | import { ComponentName } from './[component-name]/[component-name].component'; | ||
// | // un module pour la gestion des items | ||
@NgModule({ | |||
// declare the components used in this module | |||
declarations: [ ], | |||
// | // declare the components to be used outside of this module | ||
import { | exports: [ ComponentName ] | ||
}) | |||
export class ModuleName {} | |||
</filebox> | |||
Importing the feature module into the app module. | |||
<filebox fn='src/app/app.module.ts'> | |||
import { ModuleName } from '../[module-name]/[module-name].module'; | |||
@NgModule({ | @NgModule({ | ||
// déclare les modules externes qui seront mis à disposition des components du module | // déclare les modules externes qui seront mis à disposition des components du module | ||
imports: [ | imports: [ ModuleName ] | ||
}) | }) | ||
export class AppModule {} | export class AppModule {} | ||
</filebox> | </filebox> | ||
<filebox fn='src/app/app.component.html'> | |||
<filebox fn=' | <[component-name]></[component-name]> | ||
</filebox> | |||
import { | = [https://angular.io/guide/inputs-outputs#sending-data-to-a-child-component Sending data to a child component] = | ||
<filebox fn='src/app/child.component.ts'> | |||
import { Component, Input } from '@angular/core'; // import Input | |||
export class ChildComponent { | |||
@Input() name = ''; // decorate the property with @Input() | |||
} | |||
</filebox> | |||
<filebox fn='src/app/parent.component.html'> | |||
<child [name]="childName"></child> <!-- use property bindingm --> | |||
</filebox> | |||
// | <filebox fn='src/app/parent.component.ts'> | ||
export class ParentComponent { | |||
childName = 'Child Name'; | |||
} | |||
</filebox> | </filebox> | ||
Ligne 374 : | Ligne 378 : | ||
</kode> | </kode> | ||
= [https://angular.io/api/common/ | = Format = | ||
== [https://angular.io/api/common/DecimalPipe Decimal] == | |||
<kode lang='html'> | |||
{{ 123.456 | number:'4.0-2':'en' }} <!-- 0,123.46 --> | |||
{{ 123.456 | number:'4.0-2':'fr' }} <!-- 0 123,46 --> | |||
</kode> | |||
== [https://angular.io/api/common/PercentPipe Percentage] == | |||
<kode lang='html'> | <kode lang='html'> | ||
< | <!-- 5% --> | ||
{{ 0.05 | percent }} | |||
</kode> | </kode> | ||
= [https://angular.io/api/common/CurrencyPipe Currency | == [https://angular.io/api/common/CurrencyPipe Currency] == | ||
<kode lang='html'> | <kode lang='html'> | ||
<!-- 1000,00 € --> | <!-- 1000,00 € --> | ||
{{ | {{ 1000 | currency:'EUR':'symbol':'1.2-2'}} | ||
</kode> | |||
== [https://angular.io/api/common/DatePipe Date] == | |||
<kode lang='html'> | |||
<div>{{ today | date: 'EEEE dd MMMM yyyy' }}</div> | |||
<!-- Mardi 18 Septembre 2018 --> | |||
</kode> | </kode> | ||
Ligne 392 : | Ligne 409 : | ||
@NgModule({ | @NgModule({ | ||
imports: [ | imports: [ | ||
// import HttpClientModule after BrowserModule. | // import HttpClientModule after BrowserModule. | ||
HttpClientModule | BrowserModule, HttpClientModule | ||
], | ], | ||
</filebox> | </filebox> | ||
<filebox fn=' | <filebox fn='item/item-list/item-list.component.ts'> | ||
import { Component, OnInit } from '@angular/core'; | import { Component, OnInit } from '@angular/core'; | ||
import { HttpClient } from '@angular/common/http'; | import { HttpClient } from '@angular/common/http'; | ||
import { Item } from '../ | import { Item } from '../item/item.module'; | ||
export class | export class ItemListComponent implements OnInit { | ||
public items: Item[] = []; | public items: Item[] = []; | ||
url: string = 'http://localhost:5235'; // useful only if the service is hosted on another url | |||
constructor(private http: HttpClient) {} | constructor(private http: HttpClient) {} | ||
ngOnInit() | ngOnInit() { | ||
this.http.get<Item[]>( | |||
this.http.get<Item[]>(`${this.url}/item`).subscribe({ | |||
next: result => this.items = result, | |||
error: error => this.handleError(error) | |||
}); | |||
this.http.post<Item>('/api/items', newItemToAdd).subscribe(newItemFromServer => ...); | this.http.post<Item>('/api/items', newItemToAdd).subscribe(newItemFromServer => ...); | ||
this.http.delete('/api/items/42').subscribe(); | this.http.delete('/api/items/42').subscribe(); | ||
this.http.put<Item>('/api/items', updatedItem).subscribe(); | this.http.put<Item>('/api/items', updatedItem).subscribe(); | ||
} | } | ||
Ligne 433 : | Ligne 451 : | ||
</filebox> | </filebox> | ||
<filebox fn=' | <filebox fn='item/item.module.ts'> | ||
export interface Item { | export interface Item { | ||
id: number; | |||
name: string; | name: string; | ||
} | } | ||
</filebox> | </filebox> | ||
{{warn | Les objets récupérés sont de type {{boxx|Object}} et pas vraiment du type du cast.<br> | {{warn | Les objets récupérés sont de type {{boxx|Object}} et pas vraiment du type du cast.<br> | ||
Les attributs sont accessibles mais pas les méthodes. ([https://stackoverflow.com/questions/34031448/typescript-typeerror-myclass-myfunction-is-not-a-function lien])}} | Les attributs sont accessibles mais pas les méthodes. ([https://stackoverflow.com/questions/34031448/typescript-typeerror-myclass-myfunction-is-not-a-function lien])}} |
Dernière version du 22 janvier 2024 à 18:56
Liens
CLI
# build l'application angular
ng build
# --prod pour la production
# build l'application angular et la rebuild uniquement ce qui est nécessaire à chaque modification de fichiers
ng build --watch
# lancer le service et ouvrir http://localhost:4200/
ng serve --open
|
Hierarchie des dossiers et fichiers
src
- main.ts is where the app starts running
- favicon.ico is the app's icon, just as you would find in any web site
- index.html is the app's top level HTML template
- style.css is the app's top level style sheet
- app
- app.component.ts app-root component, top-level Angular component in the app
- app.component.html component HTML template
- app.component.css component styles
- shared
- app.module.ts
- app.component.*
- component1
- component1.component.*
- module2
- shared
- module2.module.ts
- module2-routing.module.ts
- component2
- component2.component.*
- asset contains site images
- environments
gitignore
afficher.gitignore |
Astuces
Titre de l'onglet et favicon
src/index.html |
<head>
<title>MonTitre</title>
<link rel="icon" type="image/x-icon" href="favicon.ico">
|
String et substitution de variable
let myVar = 'texte';
let otherVar = `Contenu de myVar: ${myVar}`;
|
Object to string
console.log(JSON.stringify(myObject));
|
Timeout
setTimeout(() => {
// code
}, 1000); // delay en ms
|
Propriété get
date: Date;
get formattedDate() {
return moment(this.date).format('MMMM YYYY');
}
|
Component
A component represents a view. generate
# from src/app
ng generate component '[component-name]'
# CREATE src/app/[component-name]/[component-name].component.html
# CREATE src/app/[component-name]/[component-name].component.spec.ts
# CREATE src/app/[component-name]/[component-name].component.ts
# CREATE src/app/[component-name]/[component-name].component.css
# --dry-run
# --inline-template use inline template instead of creating an html file
# --inline-style use inline style instead of creating a css file
# --skip-tests do not create "spec.ts" test files
# Error could not find an NgModule
# --skip-import do not import this component into the owning NgModule
|
- src/app
- [component-name]
- [component-name].component.css
- [component-name].component.html
- [component-name].component.ts
- [component-name]
src/app/[component-name]/[component-name].component.ts |
import { Component } from '@angular/core';
@Component({
// nom de la directive pour l’utilisation comme tag HTML
selector: '[component-name]',
templateUrl: './[component-name].component.html',
// inline template
template: `<div>Hello World</div>`,
styleUrls: ['./[component-name].component.css'],
// inline style
styles: `div { background: pink; }`
})
export class ComponentName {
// propriété bindée dans le template
pageTitle: string = 'My Title';
persons: any[] = [
{ "Name": "Billy" },
{ "Name": "John" }
];
add() {
alert("add");
}
|
src/app/[component-name]/[component-name].component.html |
<!-- si persons est null ou vide, table n'est pas affichée -->
<table *ngIf='persons && persons.length'>
<tr *ngFor='let person of persons'>
<td>{{ person.Name }}</td>
<a (click)="addItem()"><i class="fa fa-plus-circle"></i></a>
|
<body>
<!-- utilisation de la directive my-new-component -->
<[component-name]></[component-name]>
|
À la création d'un nouveau component, penser à le déclarer dans le module. |
src/app/app.module.ts |
import { ComponentName } from './[component-name]/[component-name].component';
@NgModule({
declarations: [
ComponentName
]
})
export class AppModule {}
|
Feature Module
Permet d'organiser les components par groupes. Un module par fonctionnalité.
# from src/app
ng generate module [module-name]
# --dry-run / -d
# create a folder [module-name] and a file [module-name]/[module-name].module.ts
|
src/[module-name]/[module-name].module.ts |
import { ComponentName } from './[component-name]/[component-name].component';
// un module pour la gestion des items
@NgModule({
// declare the components used in this module
declarations: [ ],
// declare the components to be used outside of this module
exports: [ ComponentName ]
})
export class ModuleName {}
|
Importing the feature module into the app module.
src/app/app.module.ts |
import { ModuleName } from '../[module-name]/[module-name].module';
@NgModule({
// déclare les modules externes qui seront mis à disposition des components du module
imports: [ ModuleName ]
})
export class AppModule {}
|
src/app/app.component.html |
<[component-name]></[component-name]> |
Sending data to a child component
src/app/child.component.ts |
import { Component, Input } from '@angular/core'; // import Input
export class ChildComponent {
@Input() name = ''; // decorate the property with @Input()
}
|
src/app/parent.component.html |
<child [name]="childName"></child> <!-- use property bindingm -->
|
src/app/parent.component.ts |
export class ParentComponent {
childName = 'Child Name';
}
|
ngFor
<ul>
<li *ngFor="let item of items; let i = index">
{{ item.name }} - {{ i }}
</li>
</ul>
|
Filtre / Pipe
items.component.html |
<input type="text" [(ngModel)]="filterText" autofocus />
<!-- champs filtre avec bouton clear et un watermark -->
<div class="btn-group">
<input type="search" placeholder="Filtre" [(ngModel)]="filterText" autofocus>
<span id="filterclear" class="glyphicon glyphicon-remove-circle" (click)="clearFilter()"></span>
</div>
<ul>
<li *ngFor="let item of items | itemsFilter: filterText">
{{ item.name }}
</li>
</ul>
|
items.component.css |
#filterclear {
position: absolute;
right: 5px;
top: 0;
bottom: 0;
height: 14px;
margin: auto;
font-size: 14px;
cursor: pointer;
}
|
items.component.ts |
export class ItemsComponent {
filterText: string = "";
clearFilter() {
this.filterText = "";
}
}
|
items-filter.pipe.ts |
import { Pipe, PipeTransform } from '@angular/core';
import { Item } from 'ClientApp/app/shared/item';
@Pipe({
name: 'itemsFilter',
pure: false
})
export class ItemsFilterPipe implements PipeTransform {
transform(items: Item[], filterText: string): Item[] {
if (!items || !filterText) {
return items;
}
filterText = filterText.toLowerCase();
// test si item.name contient filterText
return items.filter(item => item.name.toLowerCase().indexOf(filterText) !== -1);
}
}
|
app.module.ts |
/* erreur Can't bind to 'ngModel' since it isn't a known property of 'input' */
import { FormsModule } from '@angular/forms';
import { ItemsFilterPipe } from './components/items/items-filter.pipe';
@NgModule({
imports: [
FormsModule
],
declarations: [
ItemsFilterPipe
],
|
OrderBy - Sort
yarn add ngx-order-pipe
# pour angular < 5 utiliser la version 1.1.3
yarn add ngx-order-pipe@1.1.3
|
<ul>
<!-- orderBy: expression : reverse : caseInsensitive : comparator -->
<li *ngFor="let item of items | orderBy: 'name' : true">
{{ item.name }}
</li>
</ul>
|
app.module.ts |
import { OrderModule } from 'ngx-order-pipe';
@NgModule({
imports: [
OrderModule
],
|
ngIf
<div *ngIf="user">{{ user.name }}</div>
<div *ngIf="myObject.MyProperty === 10; else autreTexte">Texte</div>
<ng-template #autreTexte>
<div>Autre texte</div>
</ng-template>
|
Format
Decimal
{{ 123.456 | number:'4.0-2':'en' }} <!-- 0,123.46 -->
{{ 123.456 | number:'4.0-2':'fr' }} <!-- 0 123,46 -->
|
Percentage
<!-- 5% -->
{{ 0.05 | percent }}
|
Currency
<!-- 1000,00 € -->
{{ 1000 | currency:'EUR':'symbol':'1.2-2'}}
|
Date
<div>{{ today | date: 'EEEE dd MMMM yyyy' }}</div>
<!-- Mardi 18 Septembre 2018 -->
|
HttpClient
app/app.module.ts |
import { HttpClientModule } from '@angular/common/http';
@NgModule({
imports: [
// import HttpClientModule after BrowserModule.
BrowserModule, HttpClientModule
],
|
item/item-list/item-list.component.ts |
import { Component, OnInit } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Item } from '../item/item.module';
export class ItemListComponent implements OnInit {
public items: Item[] = [];
url: string = 'http://localhost:5235'; // useful only if the service is hosted on another url
constructor(private http: HttpClient) {}
ngOnInit() {
this.http.get<Item[]>(`${this.url}/item`).subscribe({
next: result => this.items = result,
error: error => this.handleError(error)
});
this.http.post<Item>('/api/items', newItemToAdd).subscribe(newItemFromServer => ...);
this.http.delete('/api/items/42').subscribe();
this.http.put<Item>('/api/items', updatedItem).subscribe();
}
private handleError(error: HttpErrorResponse) {
if (error.error instanceof ErrorEvent) {
// A client-side or network error occurred. Handle it accordingly.
console.error('An error occurred:', error.error.message);
} else {
// The backend returned an unsuccessful response code.
// The response body may contain clues as to what went wrong,
console.error(`Backend returned code ${error.status}, body was: ${error.error}`);
}
}
}
|
item/item.module.ts |
export interface Item {
id: number;
name: string;
}
|
Les objets récupérés sont de type Object et pas vraiment du type du cast. Les attributs sont accessibles mais pas les méthodes. (lien) |
map
Projection d'un tableau dans un nouveau tableau.
import { map } from 'rxjs/operators'; // pour l'utilisation comme délégué
let array1: number[] = [ 1, 2, 3 ];
let array2: string[] = array1.map((item, index, array) => {
// item: T = 1
// index: number = 0
// array: T[] = array1
return item.toString();
});
// [ '1', '2', '3' ]
const numToString = map((val: number) => val.toString());
let array2: string[] = numToString(array1);
|
Bouton
item.component.html |
<button (click)="myAction()">My Action</button>
|
item.component.ts |
myAction() {
alert('action');
}
|
ngClass et ngStyle
Affections conditionnelles de styles et classes CSS.
<div [ngStyle]="{'color':myVar > 0 ? 'green' : 'red' }"
[ngClass]="{'myCssClass':myVar > 0}">
</<div>
<div [style.color]="getColor(myVar)"
[class.myCssClass]="myVar > 0">
</<div>
|
Template reference
Choix d'un template en fonction d'une variable:
<ng-container [ngTemplateOutlet]='templateChoice === 1 ? template1 : template2'
[ngTemplateOutletContext]="{arg1:arg1}">
</ng-container>
<ng-template #template1 let-arg1="arg1">
</ng-template>
|
Understanding Dynamic Scoping and TemplateRef
Tooltip
<div data-toggle="tooltip" data-placement="top" title="texte"></div>
<!-- binding du tooltip avec myVar -->
<div data-toggle="tooltip" data-placement="top" [title]="myVar"></div>
|
Form
app/app.module.ts |
import { FormsModule } from '@angular/forms';
@NgModule({
imports: [
FormsModule
]
})
export class AppModule { }
|
Binding
myform.component.html |
<form (submit)="onSubmit()" #myClassForm="ngForm">
<div class="form-group">
<label for="field1">Item name</label>
<input type="text" class="form-control" id="field1" name="field1" [(ngModel)]="model.field1" required />
<!-- [()] : 2-ways binding -->
<select [(ngModel)]="model.choice">
<option [value]="choice" *ngFor="let choice of choices">{{choice}}</option>
</select>
<!-- checkbox, nécessite un name ou standalone -->
<input type="checkbox" id="checkbox1" name="checkbox1"
[(ngModel)]="model.checkbox1" [ngModelOptions]="{standalone: true}" (change)="onChange(model.checkbox1)">
</div>
<button type="submit" class="btn btn-success">Submit</button>
|
myform.component.ts |
model = {
field1: "",
choice: "un"
};
choices = [ "un", "deux", "trois" ];
onSubmit() {
}
|
model undefined dans le template
Si l'initialisation de model se fait dans ngOnInit, model est undefined dans le constructeur ainsi que dans le template.
<!-- utiliser ?. pour éviter d'avoir une erreur -->
{{ model?.name }}
<!-- ?. ne peut être utiliser avec les binding 2-ways -->
<input [(ngModel)]="model && model.name" />
<!-- utiliser ngIf -->
<input *ngIf='model' [(ngModel)]="model.name" />
|
// autre solution: initialiser à la déclaration avec un objet vide
model: Item = <Item>{};
|
Validation
myform.component.html |
<form (submit)="onSubmit()" #myform="ngForm" novalidate>
<div class="form-group">
<input type="text" class="form-control" name="field1" [(ngModel)]="mydata.field1" #field1="ngModel" required />
<div class="text-danger" *ngIf="field1.touched && field1.invalid && field1.errors.required">Field1 is required</div>
</div>
<div class="form-group">
<input type="submit" class="btn btn-success" value="submit" [disabled]="myform.invalid" />
</div>
|
Autofocus
autofocus ne fonctionne pas nativement avec Angular.
Pour Angular 4.4.7, utiliser angular-autofocus-fix
app/app.shared.module.ts |
import { AutofocusModule } from 'angular-autofocus-fix'
@NgModule({
imports: [
AutofocusModule
|
<input type="text" autofocus />
|
input date
La date ne s'affiche pas correctement dans le champs input.
<input type="date" [ngModel] ="myDate | date:'yyyy-MM-dd'" (ngModelChange)="myDate = $event">
|
FormArray
Événements
Clavier
<input type="text" (keydown.enter)="validate()">
|
Focus
<input type="text" (focus)="focusFunction()" (focusout)="validate()">
|
$event
<input type="text" (keydown.enter)="validate($event)">
|
validate(event: Event) {
let sourceElement = event.srcElement;
}
|
Menu contextuel
ngx-contextmenu: npm, git, demo, custom css
<div [contextMenu]="myContextMenu" [contextMenuSubject]="myObject">Texte</div>
<context-menu #myContextMenu>
<ng-template contextMenuItem (execute)="myFunction($event.item)">Context Menu 1</ng-template>
</context-menu>
|
import { ViewEncapsulation } from '@angular/core';
@Component({
encapsulation: ViewEncapsulation.None // for custom css in context menu
myFunction(myObject: MyClass) { /* */ }
|
.dropdown-menu > li > a:hover {
text-decoration: none;
}
.dropdown-menu > li > a {
display: block; /* prend toute la largeur */
padding: 0 10px;
color: white;
}
.dropdown-menu {
min-width: 0; /* set to 10rem */
}
|
yarn add ngx-contextmenu @angular/cdk
# @angular/cdk@^6.0.0
|
app.module.ts |
import { ContextMenuModule } from 'ngx-contextmenu';
@NgModule({
imports: [
ContextMenuModule.forRoot()
|
Langue
src/app/app.module.ts |
import { LOCALE_ID } from '@angular/core';
import { registerLocaleData } from '@angular/common';
import localeFr from '@angular/common/locales/fr';
@NgModule({
providers: [ {provide: LOCALE_ID, useValue: 'fr' } ]
})
export class AppModule {
constructor() {
registerLocaleData(localeFr, 'fr');
}
}
|
ng-bootstrap
yarn add @ng-bootstrap/ng-bootstrap |
app.module.ts |
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
@NgModule({
imports: [ NgbModule ],
})
|
Tooltip
Bootstrap
Bootstrap 4
yarn add bootstrap jquery popper.js |
src/styles.css |
/* modifier ce fichier plutôt que angular.json pour être sur de l'ordre de concaténation du css */
@import url("~bootstrap/dist/css/bootstrap.min.css");
|
angular.json |
"styles": [
"src/styles.css",
"node_modules/bootstrap/dist/css/bootstrap.min.css"
],
"scripts": [
"node_modules/jquery/dist/jquery.min.js",
"node_modules/bootstrap/dist/js/bootstrap.min.js"
]
|
Bootstrap 3
yarn add bootstrap3 |
angular.json |
"styles": [
"src/styles.css",
"node_modules/bootstrap3/dist/css/bootstrap.min.css"
],
|
affichernavmenu.component.html |
affichernavmenu.component.css |
Font Awesome
Font Awesome 5
Font Awesome 4
yarn add font-awesome
|
src/styles.css |
/* modifier ce fichier plutôt que angular.json pour être sur de l'ordre de concaténation du css */
@import url("~font-awesome/css/font-awesome.css");
|
angular.json |
"styles": [
"src/styles.css",
"node_modules/font-awesome/css/font-awesome.min.css"
],
|
<i class="fa fa-edit"></i>
|
Global style
angular.json |
{
"projects": {
"ClientApp": {
"architect": {
"build": {
"options": {
"styles": [
"src/styles.css",
"src/other-styles.css"
],
|
La modification du fichier angular.json nécessite de recompiler le projet à la main. |
Tous les styles sont concaténés au sein d'un seul fichier lors du build. L'ordre des fichiers listés dans style n'est pas respecté lors de la concaténation (bug). |
src/styles.css |
@import url("src/other-styles.css");
|
Dark style
src/styles.css |
html, body { height: 100%; }
|
src/app/app.component.css |
/* dark theme */
.container-fluid {
background-color: #1f1f1f;
color: #cccccc;
min-height: 100%; /* take the full height */
}
|
Animation
app.module.ts |
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
@NgModule({
imports: [
BrowserAnimationsModule
],
|
test/test.component.ts |
import {style, state, animate, transition, trigger} from '@angular/animations';
@Component({
animations: [
trigger('fadeInOut', [
transition(':enter', [ // :enter is alias to 'void => *'
style({ opacity: 0 }),
animate(500, style({ opacity: 1 }))
]),
transition(':leave', [ // :leave is alias to '* => void'
animate(500, style({ opacity: 0 }))
])
])
]
})
|
test/test.component.html |
<div [@fadeInOut]>
|
Accèder aux éléments du DOM
let element = document.getElementById('myId');
(element as HTMLInputElement).value = "My Value";
document.querySelector(':focus').blur();
|
Versions
Version | Date | Commentaire |
---|---|---|
Angular 7 | Octobre 2018 | |
Angular 6 | Mars 2018 | |
Angular 5 | Octobre 2017 | |
Angular 4 | Mars 2017 | Pas de version 3. |
Angular 2 | Septembre 2016 | TypeScript. Réécriture complète d'AngularJS. |
AngularJS 1 | Octobre 2010 | Javascript |
Langages
Langage | Description |
---|---|
EcmaScript 5 | Sortie Décembre 2009. |
EcmaScript 2015 / 6 | Orienté objet. Sortie Juin 2015. fonctionnalités. |
TypeScript | Superset de JS. Stong types. Orienté objet. |
Dart |
AngularJS vs Angular 2+
AngularJS
- Javascript
- Design basé sur MVC
Angular 2+
- Typescript
- Design basé sur Service/Controller
moment.js
yarn add moment ngx-moment |
app.module.ts |
import { MomentModule } from 'ngx-moment';
@NgModule({
imports: [
MomentModule
|
test/test.component.html |
{{myDate | amDateFormat:'LL'}} |
test/test.component.ts |
import * as moment from 'moment';
const formattedDate = moment(myDate).format('LL');
|
jquery / jquery-ui
yarn add jquery jquery-ui-dist |
angular.json |
"scripts": [
"node_modules/jquery/dist/jquery.min.js",
"node_modules/jquery-ui-dist/jquery-ui.min.js"
]
|
test/test.component.ts |
import * as $ from 'jquery'; // pour jquery seulement
declare var $: any; // pour jquery et jquery-ui, à placer après les imports avant la classe
ngOnInit() {
$(document).ready(function() { // pas forcément nécessaire
$('#id').MonthPicker({ Button: false });
});
|
angular-modal-gallery
Galerie d'images pour Angular 4,5,6.
# installation
yarn add @ks89/angular-modal-gallery hammerjs mousetrap
yarn add @types/hammerjs @types/mousetrap
# pour angular 6
yarn add rxjs-compat
|
afficherapp.module.ts |
afficher*.component.ts |
afficher*.component.html |
close outside | [enableCloseOutside]="false" |
infinite sliding but NO side previews | [slideConfig]="{infinite: true, sidePreviews: {show: false}}" |
bouton open / ouverture automatique
import { GalleryService } from '@ks89/angular-modal-gallery';
constructor(private galleryService: GalleryService) {}
openGallery() {
this.galleryService.openGallery(1, 0); // id-gallery, img
}
// ouvrir la galerie au chargement de la page
ngOnInit() {
this.delay(1).then(() => { this.galleryService.openGallery(1, 0); } );
}
delay(ms: number) {
return new Promise( resolve => setTimeout(resolve, ms) );
}
// avec await
async ngOnInit() {
await this.delay(1); // on attend 1ms avant d’exécuter la suite
this.galleryService.openGallery(1, 0);
}
|
Afficher une seule image
<ks-modal-gallery [id]="1" [modalImages]="images"
[plainGalleryConfig]="plainGalleryRow"></ks-modal-gallery>
|
plainGalleryRow: PlainGalleryConfig = {
strategy: PlainGalleryStrategy.ROW,
layout: new LineLayout({ width: '80px', height: 'auto' }, { length: 1, wrap: true }, 'flex-start')
};
|
bouton delete
<ks-modal-gallery [buttonsConfig]="customButtonsConfig"
(buttonBeforeHook)="onButtonBeforeHook($event)"></ks-modal-gallery>
|
customButtonsConfig: ButtonsConfig = {
visible: true,
strategy: ButtonsStrategy.CUSTOM,
// les modèles des boutons par défaut se trouvent dans libs/angular-modal-gallery/src/components/upper-buttons
buttons: [
{
className: 'delete-image',
type: ButtonType.DELETE,
ariaLabel: 'Delete the current image',
title: 'Delete the current image',
fontSize: '20px'
},
{
className: 'close-image',
type: ButtonType.CLOSE,
ariaLabel: 'Close this modal image gallery',
title: 'Close this modal image gallery',
fontSize: '20px'
}
]
};
// possibilité de passer le tableau d'images en argument
onButtonBeforeHook(event: ButtonEvent) {
console.log('onButtonBeforeHook ', event);
if (!event || !event.button) {
return;
}
// Invoked after a click on a button, but before that the related
// action is applied.
// For instance: this method will be invoked after a click
// of 'close' button, but before that the modal gallery
// will be really closed.
if (event.button.type === ButtonType.DELETE) {
console.log('image to delete: id ' + event.image.id + 'path ' + event.image.modal.img);
// path = /images/<photoFileName>.jpg
// delete the image file
this.http.delete('/api' + event.image.modal.img).subscribe();
// remove the current image from the array of images and reassign it
this.images = this.images.filter((img: Image) => event.image && img.id !== event.image.id);
}
}
|
Preview Config (liste des images en bas)
<!-- afficher 5 images -->
<ks-modal-gallery [id]="1" [modalImages]="images"
[previewConfig]="{ number: 5 }">
</ks-modal-gallery>
|
Valeur par défaut: { visible: true, number: 3, arrows: true, clickable: true, size: { height: '50px', width: 'auto' }}
ngx-chart
yarn add @swimlane/ngx-charts |
app.module.ts |
import { NgxChartsModule } from '@swimlane/ngx-charts';
imports: [
NgxChartsModule
]
|
test/test.component.html |
<ngx-charts-bar-vertical-2d
[results]="barVertical2dData"
xAxis="true"
yAxis="true">
</ngx-charts-bar-vertical-2d>
<ngx-charts-line-chart
[results]="lineChartData"
xAxis="true"
yAxis="true"
showGridLines
roundDomains="true"
[xAxisTickFormatting]="xAxisTickFormatting">
</ngx-charts-line-chart>
|
test/test.component.ts |
import { Component, OnInit, ViewEncapsulation } from '@angular/core';
@Component({
encapsulation: ViewEncapsulation.None
})
barVertical2dData = [
{
'name': 'Groupe1',
'series': [
{ 'name': 'Texte1', 'value': 100 },
{ 'name': 'Texte2', 'value': 200 }
]
},
{
'name': 'Groupe2',
'series': [
{ 'name': 'Texte1', 'value': 300 },
{ 'name': 'Texte2', 'value': 400 }
]
},
];
lineChartData = [{
name: 'line1',
series: [
{ 'name': new Date(2018, 7, 1, 0, 0, 0, 0), 'value': 23.56 },
{ 'name': new Date(2018, 8, 1, 0, 0, 0, 0), 'value': 57.45 },
{ 'name': new Date(2018, 9, 1, 0, 0, 0, 0), 'value': 199.25 },
{ 'name': new Date(2018, 10, 1, 0, 0, 0, 0), 'value': 154.32 }
]
}];
xAxisTickFormatting(value: Date) {
if (value.getDate() < 9) {
return moment(value).format('MMM YYYY');
} else {
return '';
}
}
|
Debug
Firefox
Firefox → Inspect Element → Debugger → Webpack → src → app → *.ts
Ajouter des point d'arrêts
Visual Studio Code
- Installer Debugger for Firefox
- Ajouter un nouvel élément de lancement
.vscode\launch.json |
{
"name": "Debug Angular",
"type": "firefox",
"request": "launch",
"reAttach": true,
"url": "http://localhost:4200",
"webRoot": "${workspaceFolder}"
}
|
- Lancer le service Angular :
ng serve
- Lancer le debugage depuis VS code: Debug → Debug Angular → Run
Publication / Déploiement / Publish
ng build --prod
# créé le dossier dist/myproject
|
/etc/nginx/nginx.conf |
try_files $uri $uri/ /index.html;
|
Variables d'environnement
src/environment.ts |
export const environment = {
production: false,
myvar: 'dev'
};
|
src/environment.prod.ts |
export const environment = {
production: true,
myvar: 'prod'
};
|
src/app/app.component.ts |
import { environment } from '../environments/environment';
environment.myvar;
|
# configuration dans angular.json → configurations
ng serve -c production
ng build --prod
|
Installation
# installer angular cli global
npm install -g @angular/cli
# global avec yarn
yarn global add @angular/cli
# local avec yarn
yarn add @angular/cli
# créer un projet angular dans le dossier MyAngularApp
ng new MyAngularApp
# --dry-run
# --minimal : moins de fichiers
# lancer le service et l'ouvrir dans un navigateur
cd MyAngularApp
ng serve --open
# build
ng build
# --watch recompile ce qu'il faut lors de modifications
|
Windows
# check if node.js is installed
node --version
# install node.js LTS
winget install OpenJS.NodeJS.LTS
|
Erreurs
Can't resolve 'rxjs/add/operator/map'
Vérifier que rxjs et rxjs-compat sont installés.
Got interpolation ({{}}) where expression was expected
Ne pas utiliser {{ }} dans un événement (click)
<a (click)="delete({{ item.id }})"><i class="fa fa-trash"></i></a>
<a (click)="delete(item.id)"><i class="fa fa-trash"></i></a>
|
UI pas mise à jour après la modification d'une valeur bindée
import { NgZone } from '@angular/core';
this.http.get<Class[]>(`/api/class`).subscribe(data => {
this.ngZone.run(() => {
this.bindedValue = data;
});
});
|