Angular

De Banane Atomic
Aller à la navigationAller à la recherche

Liens

CLI

Bash.svg
# 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

.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

Ts.svg
let myVar = 'texte';
let otherVar = `Contenu de myVar: ${myVar}`;

Object to string

Ts.svg
console.log(JSON.stringify(myObject));

Timeout

Ts.svg
setTimeout(() => {
  // code
}, 1000); // delay en ms

Propriété get

Ts.svg
date: Date;

get formattedDate() {
  return moment(this.date).format('MMMM YYYY');
}

Component

A component represents a view. generate

Bash.svg
# 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>
Html.svg
<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é.

Powershell.svg
# 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

Html.svg
<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

Bash.svg
yarn add ngx-order-pipe
# pour angular < 5 utiliser la version 1.1.3
yarn add ngx-order-pipe@1.1.3
Html.svg
<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

Html.svg
<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

Html.svg
{{ 123.456 | number:'4.0-2':'en' }} <!-- 0,123.46 -->
{{ 123.456 | number:'4.0-2':'fr' }} <!-- 0 123,46 -->

Percentage

Html.svg
<!-- 5% -->
{{ 0.05 | percent }}

Currency

Html.svg
<!-- 1000,00 € -->
{{ 1000 | currency:'EUR':'symbol':'1.2-2'}}

Date

Html.svg
<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.

Ts.svg
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.

Html.svg
<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:

Html.svg
<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

Html.svg
<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.

Html.svg
<!-- 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" />
Ts.svg
// 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
Html.svg
<input type="text" autofocus />

input date

La date ne s'affiche pas correctement dans le champs input.

Html.svg
<input type="date" [ngModel] ="myDate | date:'yyyy-MM-dd'" (ngModelChange)="myDate = $event">

FormArray

Événements

Clavier

Html.svg
<input type="text" (keydown.enter)="validate()">

Focus

Html.svg
<input type="text" (focus)="focusFunction()" (focusout)="validate()">

$event

Html.svg
<input type="text" (keydown.enter)="validate($event)">
Ts.svg
validate(event: Event) {
  let sourceElement = event.srcElement;
}

Menu contextuel

ngx-contextmenu: npm, git, demo, custom css

Html.svg
<div [contextMenu]="myContextMenu" [contextMenuSubject]="myObject">Texte</div>

<context-menu #myContextMenu>
  <ng-template contextMenuItem (execute)="myFunction($event.item)">Context Menu 1</ng-template>
</context-menu>
Ts.svg
import { ViewEncapsulation } from '@angular/core';

@Component({
  encapsulation: ViewEncapsulation.None  // for custom css in context menu

myFunction(myObject: MyClass) { /* */ }
Css.svg
.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 */
}
Bash.svg
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

Bash.svg
yarn add @ng-bootstrap/ng-bootstrap
app.module.ts
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';

@NgModule({
  imports: [ NgbModule ],
})

Tooltip

Bootstrap

Bootstrap 4

Powershell.svg
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

Powershell.svg
yarn add bootstrap3
angular.json
"styles": [
  "src/styles.css",
  "node_modules/bootstrap3/dist/css/bootstrap.min.css"
],

navmenu

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

Font Awesome 5

Font Awesome 4

Powershell.svg
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"
],
Html.svg
<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

Ts.svg
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

Bash.svg
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

Bash.svg
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.

Bash.svg
# 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

Ts.svg
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

Html.svg
<ks-modal-gallery [id]="1" [modalImages]="images"
                  [plainGalleryConfig]="plainGalleryRow"></ks-modal-gallery>
Ts.svg
plainGalleryRow: PlainGalleryConfig = {
    strategy: PlainGalleryStrategy.ROW,
    layout: new LineLayout({ width: '80px', height: 'auto' }, { length: 1, wrap: true }, 'flex-start')
};

bouton delete

Html.svg
<ks-modal-gallery [buttonsConfig]="customButtonsConfig"
                  (buttonBeforeHook)="onButtonBeforeHook($event)"></ks-modal-gallery>
Ts.svg
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)

Html.svg
<!-- 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

Bash.svg
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

Bash.svg
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;
Bash.svg
# configuration dans angular.json → configurations
ng serve -c production
ng build --prod

Installation

Bash.svg
# 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

Ps.svg
# 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)

Html.svg
<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

Ts.svg
import { NgZone } from '@angular/core';

this.http.get<Class[]>(`/api/class`).subscribe(data => {
  this.ngZone.run(() => {
    this.bindedValue = data;
  });
});