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
|
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
- module2
- shared
- module2.module.ts
- module2-routing.module.ts
- component2
- asset contains site images
- environments
gitignore
.gitignore
|
# See http://help.github.com/ignore-files/ for more about ignoring files.
# Compiled output
/dist
/tmp
/out-tsc
/bazel-out
# Node
/node_modules
npm-debug.log
yarn-error.log
# IDEs and editors
.idea/
.project
.classpath
.c9/
*.launch
.settings/
*.sublime-workspace
# Visual Studio Code
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
.history/*
# Miscellaneous
/.angular/cache
.sass-cache/
/connect.lock
/coverage
/libpeerconnection.log
testem.log
/typings
# System files
.DS_Store
Thumbs.db
|
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
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 {}
|
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]>
|
ngFor
|
<ul>
<li *ngFor="let item of items; let i = index">
{{ item.name }} - {{ i }}
</li>
</ul>
|
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
],
|
|
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
],
|
|
<div *ngIf="user">{{ user.name }}</div>
<div *ngIf="myObject.MyProperty === 10; else autreTexte">Texte</div>
<ng-template #autreTexte>
<div>Autre texte</div>
</ng-template>
|
|
<div>{{ today | date: 'EEEE dd MMMM yyyy' }}</div>
<!-- Mardi 18 Septembre 2018 -->
|
|
<!-- 1000,00 € -->
{{ prix | currency:'EUR':'symbol':'.2-2'}}
|
app/app.module.ts
|
import { HttpClientModule } from '@angular/common/http';
@NgModule({
imports: [
BrowserModule,
// import HttpClientModule after BrowserModule.
HttpClientModule,
],
|
app/components/items/items.component.ts
|
import { Component, OnInit } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Item } from '../data/item';
export class ItemsComponent implements OnInit {
public items: Item[] = [];
constructor(private http: HttpClient) {}
ngOnInit(): void {
this.http.get<Item[]>('/api/items').subscribe(data => this.items = data);
this.http.post<Item>('/api/items', newItemToAdd).subscribe(newItemFromServer => ...);
this.http.delete('/api/items/42').subscribe();
this.http.put<Item>('/api/items', updatedItem).subscribe();
this.http.get<Item[]>('/api/items').subscribe(
data => this.items = data,
error => this.handleError(error));
}
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}`);
}
}
}
|
app/data/items.ts
|
export interface Item {
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) |
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');
}
|
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>
|
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
|
<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>
|
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() {
}
|
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 />
|
La date ne s'affiche pas correctement dans le champs input.
|
<input type="date" [ngModel] ="myDate | date:'yyyy-MM-dd'" (ngModelChange)="myDate = $event">
|
Événements
Clavier
|
<input type="text" (keydown.enter)="validate()">
|
|
<input type="text" (focus)="focusFunction()" (focusout)="validate()">
|
$event
|
<input type="text" (keydown.enter)="validate($event)">
|
|
validate(event: Event) {
let sourceElement = event.srcElement;
}
|
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()
|
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');
}
}
|
|
yarn add @ng-bootstrap/ng-bootstrap
|
app.module.ts
|
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
@NgModule({
imports: [ NgbModule ],
})
|
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"
],
|
navmenu.component.html
|
<div class='main-nav'>
<div class='navbar navbar-inverse'>
<div class='navbar-header'>
<button type='button' class='navbar-toggle' data-toggle='collapse' data-target='.navbar-collapse'>
<span class='sr-only'>Toggle navigation</span>
<span class='icon-bar'></span>
<span class='icon-bar'></span>
<span class='icon-bar'></span>
</button>
<a class='navbar-brand' [routerLink]="['/home']">Home</a>
</div>
<div class='clearfix'></div>
<div class='navbar-collapse collapse'>
<ul class='nav navbar-nav'>
<li [routerLinkActive]="['link-active']">
<a [routerLink]="['/home']">
<span class='glyphicon glyphicon-home'></span> Home
</a>
</li>
<li [routerLinkActive]="['link-active']">
<a [routerLink]="['/menu1']">
<span class='glyphicon glyphicon-picture'></span> Menu1
</a>
</li>
</ul>
</div>
</div>
</div>
|
navmenu.component.css
|
li .glyphicon {
margin-right: 10px;
}
/* Highlighting rules for nav menu items */
li.link-active a,
li.link-active a:hover,
li.link-active a:focus {
background-color: #4189C7;
color: white;
}
/* Keep the nav menu independent of scrolling and on top of other items */
.main-nav {
position: fixed;
top: 0;
left: 0;
right: 0;
z-index: 1;
}
@media (min-width: 768px) {
/* On small screens, convert the nav menu to a vertical sidebar */
.main-nav {
height: 100%;
width: calc(16.66666667% - 20px);
}
.navbar {
border-radius: 0px;
border-width: 0px;
height: 100%;
}
.navbar-header {
float: none;
}
.navbar-collapse {
border-top: 1px solid #444;
padding: 0px;
}
.navbar ul {
float: none;
}
.navbar li {
float: none;
font-size: 15px;
margin: 6px;
}
.navbar li a {
padding: 10px 16px;
border-radius: 4px;
}
.navbar a {
/* If a menu item's text is too long, truncate it */
width: 100%;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
}
|
Font Awesome
|
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).
Ne lister que src/styles.css dans styles et utiliser @import dans src/styles.css.
L'ordre des @import est respecté lors de la concaténation. |
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
|
app.module.ts
|
import 'hammerjs';
import 'mousetrap';
import { ModalGalleryModule } from '@ks89/angular-modal-gallery';
@NgModule({
imports: [
ModalGalleryModule.forRoot()
|
*.component.ts
|
import {
AccessibilityConfig,
Action,
AdvancedLayout,
ButtonEvent,
ButtonsConfig,
ButtonsStrategy,
ButtonType,
Description,
DescriptionStrategy,
DotsConfig,
GalleryService,
GridLayout,
Image,
ImageModalEvent,
LineLayout,
PlainGalleryConfig,
PlainGalleryStrategy,
PreviewConfig
} from '@ks89/angular-modal-gallery';
images: Image[] = [
new Image(0, { img: '../assets/images/1.png' }),
new Image(1, { img: '../assets/images/2.png' })
];
|
*.component.html
|
<ks-modal-gallery [id]="1" [modalImages]="images"></ks-modal-gallery>
<ks-modal-gallery *ngFor="let album of albums; let i = index"
[id]="i" [modalImages]="album.images"
</ks-modal-gallery>
|
Attributs
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);
}
|
|
<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')
};
|
|
<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' }}
|
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
- 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
|
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
|
|
# 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>
|
|
import { NgZone } from '@angular/core';
this.http.get<Class[]>(`/api/class`).subscribe(data => {
this.ngZone.run(() => {
this.bindedValue = data;
});
});
|