Gérer la récupération des fichiers avec Angular4 peut être un vrai casse-tête : entre les problèmes de compatibilité lié au navigateur et l'API de service qui ne répond pas facilement à ce besoin, j'ai trouvé cette petite librairie bien pratique.
On l'appelle de cette manière :
import { Component } from '@angular/core';
import { ZipService} from '../services/ZipService';
import { saveAs as importedSaveAs } from "file-saver";
@Component({
selector: 'zip-zip',
templateUrl: './zip-component.html'
})
export class ZipComponent {
private zipService: ZipService;
constructor(zipService: ZipService) {
this.zipService= zipService;
}
public getZipFile(): any {
this.zipService.downloadZipFile().subscribe(
(data: any) => {
importedSaveAs(new Blob([data], {type: 'application/zip'}), 'filename.zip');
},
error => this.doSomething(),
() => this.doSomething()
);
}
}
Angular est un framework orienté Aspect qui n'empêche pas... (attention, roulement de tambour...) : D'écrire des objets !
Du coup, on peut avoir besoin de mettre en place de la programmation orientée aspect.
De mémoire, j'avais essayé ce framework : https://www.npmjs.com/package/aspect.js-angular
Mes petites options de build pour la prod : ng build --prod --env=prod --aot
La compilation AOT d'angular permet d'améliorer la vitesse d'exécution de l'application en prod.
Faudra que je creuse le sujet, mais c'est en place chez moi et c'est efficace (sauf lors du build, là c'est hyper long).
Bon, personnellement, je ne suis pas fan du ng-cli, mais reconstruire le build angular à la main, c'est juste la croix et la bannière.
Heureusement, il y a le .angular-cli.json, qui permet de configurer à minima le tin-touin. C'est notamment utile lorsque l'on souhaite changer l'icône du site ou rajouter des fichiers css perso, voire des fonts.
Exemple :
{
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"project": {
"name": "Mon application"
},
"apps": [
{
"root": "src",
"outDir": "dist",
"assets": [
"assets",
"logo-brand.png" <-------- nouvelle icone qui apparaitra dans l'onglet navigateur (à configurer ensuite dans le index.html)
],
"index": "index.html",
"main": "main.ts",
"polyfills": "polyfills.ts",
"test": "test/test.ts",
"tsconfig": "tsconfig.app.json",
"testTsconfig": "tsconfig.spec.json",
"prefix": "app", <------ On peut changer le nom du composant de base
"styles": [
"assets/css/bulma.css", <------ On peut rajouter ici des css perso
"assets/css/fontawesome-all.min.css",
"styles.css"
],
"scripts": [],
"environmentSource": "environments/environment.ts",
"environments": {
"dev": "environments/environment.ts",
"prod": "environments/environment.prod.ts"
}
}
],
"lint": [
{
"project": "src/tsconfig.app.json"
},
{
"project": "src/tsconfig.spec.json"
}
],
"test": {
"karma": {
"config": "./karma.conf.js"
}
},
"defaults": {
"styleExt": "css",
"component": {}
}
}
Je recommande l'usage de ce navigateur sans GUI pour les tests angular. Il est léger, rapide et ne bugge pas.
On l'installe via le package.json :
$ npm install karma-slimerjs-launcher --save-dev
Puis, on le rajoute dans la conf karma :
karma.conf.js
module.exports = function (config) {
config.set({
basePath: '',
frameworks: ['jasmine', '@angular/cli'],
plugins: [
require('karma-jasmine'),
require('karma-slimerjs-launcher'),
require('karma-coverage-istanbul-reporter'),
require('@angular/cli/plugins/karma')
],
...
browsers: ['SlimerJS'],
...
Les différents binding de key event définis dans Angular4.
Angular propose une petite feature bien pratique pour le format des données affichées à l'écran : la définition des pipes.
On définit un pipe comme ça :
import {DomSanitizer} from '@angular/platform-browser';
import {PipeTransform, Pipe} from "@angular/core";
@Pipe({ name: 'safeHtml'}) <------ C'est avec ce nom que vous appelerez votre pipeTransformer dans les vues
export class SafeHtmlPipe implements PipeTransform {
constructor(private sanitized: DomSanitizer) {}
transform(value: any) {
return this.sanitized.bypassSecurityTrustHtml(value);
}
}
On déclare ensuite le Pipe dans le NgModule via les déclarations :
import { SafeHtmlPipe } from './pipe/SafeHtmlPipe';
@NgModule({
declarations: [ SafeHtmlPipe ]
})
Et on peut s'en servir dans le modèle :
import { DatePipe } from '@angular/common';
@Component({
...,
providers: [DatePipe]
})
export class MyComponent {
private datePipe: DatePipe;
constructor(datePipe: DatePipe) {
this.datePipe = datePipe;
}
doSomething() {
let filename = 'filename'+this.datePipe.transform(new Date(), 'yyyyMMyy-hhmm')+'.jpg';
}
}
Ou dans les vues :
<label>Date</label>
<label>The hero's birthday is {{ birthday | date }}</label>
Le DatePipe a pour nom "date" : https://github.com/angular/angular/blob/5.2.3/packages/common/src/pipes/date_pipe.ts#L15-L174
Certains codes de retour HTTP sont gérés directement par le framework Angular et ne peuvent pas être récupérés au niveau de la requête. C'est notamment le cas pour le code 401, très souvent utilisé puisqu'il informe que l'utilisateur n'est pas loggué en session.
Une manip existante consiste à surcharger le comportement natif du composant HTTP en spécifiant le comportement sur le code erreur de votre choix.
Voici comment rajouter la gestion des codes 401 à une application Angular 4 :
app.module.ts
import { HttpModule, Http } from '@angular/http';
import { AppComponent } from './app.component';
import { AuthenticatedHttpService } from './services/AuthenticatedHttpService';
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
FormsModule,
ReactiveFormsModule,
HttpModule
],
providers: [ { provide: Http, useClass: AuthenticatedHttpService } ],
bootstrap: [ AppComponent ]
})
authenticatedHttpService.ts
import { Injectable } from '@angular/core';
import { Request, XHRBackend, RequestOptions, Response, Http, RequestOptionsArgs, Headers } from '@angular/http';
import { Router, NavigationEnd, Event } from '@angular/router';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/catch';
import 'rxjs/add/observable/throw';
/**
* This class handles generically the error on authentication (code 401).
*/
@Injectable()
export class AuthenticatedHttpService extends Http {
private router: Router;
constructor(backend: XHRBackend, defaultOptions: RequestOptions, router: Router) {
super(backend, defaultOptions);
this.router = router;
}
request(url: string | Request, options?: RequestOptionsArgs): Observable<Response> {
return super.request(url, options).catch((error: Response) => {
if ((error.status === 401 || error.status === 403) && (window.location.href.match(/\?/g) || []).length < 2) {
//DO SOMETHING HERE
//exemple :
if(window.location.href != '/login') {
this.router.navigate(['/login']);
}
}
return Observable.throw("request authentication");
});
}
}
Le routage sous Angular4 se fait avec des liens complets plutôt qu'avec des ancres (fonctionnement Aurelia), ce qui pose des problèmes avec les serveurs comme SpringBoot (redirection compliquée). Toutefois, il est possible de forcer l'usage des ancres sous Angular4 :
fichier app.module.ts
import { RouterModule, Routes } from '@angular/router';
import { HashLocationStrategy, LocationStrategy } from '@angular/common';
import { AppComponent } from './app.component';
import { ComponentOne } from './one/one.component';
const appRoutes: Routes = [
{ path: 'home', component: Home },
{ path: 'login', component: Login },
{ path: 'one', component: ComponentOne},
{ path: '', redirectTo: '/home', pathMatch: 'full' },
{ path: '**', redirectTo: '/home' }
];
@NgModule({
declarations: [
Home, Login, ComponentOne
],
imports: [
BrowserModule,
FormsModule,
ReactiveFormsModule,
HttpModule,
RouterModule.forRoot(appRoutes) <----- L'ajout de la configuration de routing au module
],
providers: [ { provide: LocationStrategy, useClass: HashLocationStrategy } ], <----- Impose l'usage des ancres pour la navigation
bootstrap: [ AppComponent ]
})
export class AppModule { }