[]

  1. Inizializzazione ambiente
  2. Versioni
  3. Funzionamento
  4. Local vs Global
  5. Typescript
  6. Binding
  7. Angular Fundamentals
  8. Component API
  9. Form
  10. Consuming HTTP service
  11. Routing
  12. Autenticazione e autorizzazione
  13. Deployment
  14. API end-point lato-client
  15. Risorse

Inizializzazione ambiente

  • Installare node.js (https://nodejs.org/it)
  • Installare librerie di terze parti tramite NPM (Node Package Manager) 
    • -- Per sapere quali librerie sono già installate in node.js
      npm [-g] ls --depth=0
      ng --version
      
      -- Aggiornare tutte le librerie di terzi referenziate nel nodo "dependencies" nel file <package.json>
      -- Se si indica la versione della dipendenza con ^ o ~ , npm potrebbe non usare l'esatta versione indicata
      -- Può così modificare il file package-lock.json se ci sono dei cambiamenti nelle versione o nuove librerie 
      npm install
      
      -- Elimina i file in node_modules per assicurare uno stato pulito
      -- Guarda alle dipendenza in package-lock.json e installe tutte le dipendenza con l'esatta versione
      -- Non modifica il file package-lock.json
      npm ci
      -- Installare Angular CLI (globalmente)
      npm install -g @angular/cli@latest
      
      -- Installare Angular CLI (in locale)
      npm install --save-dev @angular/cli@latest
      
      -- Installare bootstrap
      npm install boot --save  (--save aggiunge la dipendenza bootstrap nel file /package.json)
      
      -- aprire il file <style.css> e fare l'import della libreria css di bootstrap 
      @import "~bootstrap/dist/bootstrap.css
      
  • Creo Cartella nuovo progetto
    • //con la shell mi posiziono nella cartella scelta per il nuovo progetto e lancio il comando:
      ng new <project-name>
      ng new --help
      ng new <project-name> --minimal --skip-git --style css --strict --routing false --directory <folder_name>
      
      //con la shell mi posiziono nella cartella scelta e apro 'Visual Studio Code'
      code .
      
      //nel folder dell'applicazione lancio il seguente comando attivando Angular Live Development Server
      //l'applicazione è raggiungibile a http://localhost:4200/
      ng serve                [dietro le quinte viene chiamato anche il compiler typescript]
      
      //eseguo l'applicazione
      ng start
      
      
      //cartella generica progetto Angular
      /e2e                    [contiene test end-to-end (simulano attività degli utenti) per testare l'appli] 
      /node_modules           [risiedono tutte le librerie di terze parti, le dipendenze veramente usate verranno 
                               messe in un bundle e deployate sul server di produzione compilando l'applicazione]
      /src                    [contiene l'applicazione vera e propria]
         /app                 [contiene moduli + componenti]
         /assets              [contiene gli asset static (immagini, icone, file)]
         /environment         [contiene le configurazioni]
       index.html             [pagine iniziale]
       main.ts                [punto di partenza dell'applicazione (=startup per console windows application)]   
       polyfills.ts           [fa il download di alcuni metodi javascript necessari ad Angular ma che 
                               non tutti i browser supportano]
       style.css              [global css style setting]
       test.ts                [setto configurazione per i test]
      .editorconfig           [è importante che i membri di un team abbiano gli stessi settaggi per l'editor]
      .gitignore
      karma.conf.js           [è un test runner per javascript code. Permett di gestire test]
      package.json            [file usato da ogni app nodejs, contiene info generali sull'app. Le dipendenze
                               alle librerie di 3° parti usate in prod e le dipendenze usate durante lo sviluppo]
      tsconfig.json           [settaggi per il typescript compiler]
      

 

Versioni

  • AngularJs => Scritto in javascript nel 2010
  • Angular => Scritto in typescript a partire dal 2016

 

Funzionamento

  • Angulare è costituito di alcune librerie che possono essere scaricate tramite NPM
    • @angular/core
    • @angular/compiler
    • @angular/http
    • @angular/router
  • webpack =>
    • Quando modifichiamo la nostra applicazione viene, l’applicazione viene compilata automaticamente e un tool di bundling aggrega i file js e css di cui l’applicazione ha bisogno in un solo file di bundle e lo rende disponibile sul server.
    • HTM (Hot module reloading) => Permette di vedere i cambiamenti apportati alla pagina senza nemmeno fare il refresh del browser.
    • Anche i file css vengono compilati in un bundle javascript

 

Local vs Global

  • Local => le librerie installate in locale (nella root del progetto) possono essere riferenziate all’interno del progetto stesso tramite using import { ‘whatever’}
  • Global = le librerie installate globalmente possono essere richiamate tramite nella shell  (i binari finiscono nella variabile globale PATH)

 

Typescript

  • Superset di javascript
    • Strong typing
      • //dichiarazione di variabili
        let a:number;
        let b:string;
        let c:boolean;
        let d:number[] = [1,2,3];
        let e:any = [1,true, 'a'];
        
        //enum
        enum Color { Red = 0, Green = 1, Blue = 2 };
        let background = Color.Red;
    • OOP, class, interfaces..
      • //interfaccia
        interface Point {
         x:number,
         y:number,
         draw: () => void
        }
        
        //classi
        //usare (?) permette di rendere i parametri opzionali
        //public = default
        class Point {
           constructor(private _x?:number, private _y?:number){ .. }
           
           //proprietà X
           get x() {
             return this._x;
           }
        
           set x(value){
              if(value < 0) throw new Error('Value is not correct');
              this._x = value;
           }
        
           draw() { 
              console.log('X: ' + this._x + ', Y: ' + this._y);
           } 
        
           getDistance() { ... }
        }
        let point = new Point(1, 2);
        let x = point.X;
        point.X = 10;
        point.draw();
    • Catch compile-time errors
  • Ogni volta che si compila un’applicazione typescript viene “traspilato” in javascript (I browser non interpretano direttamente typescript.
  • Il compilatore typescript può decidere di compilare i file js rispettando le specifiche ES5 (ignoroando eventuali errori dovuti a ES6).
  • Installare globalmente typescript
    • npm install -g typescript
      
      //visualizzo la version del compilatore typescript
      tsc --version
      
      //eseguo il file javascript
      node <my-file.js>
  • Type assertion: è possibile indicare al compiler quale tipo ha la mia variabile, Senza che questo sia modificato a runtime 
    • let message;
      let endsWithC = (<string>message).endsWith('c');
      let endsWithC = (message as string).endsWith('c');
  • Arrow function (equivale a lamda expression in C#)
    • //normal syntax
      let log = function(msg) {
         console.log(msg);
      }  
      
      //arrow function syntax
      let log => (message) => {
        console.log(msg);
      }
  • Moduli typescript
    • Invece di scrivere tutto il codice typescript in un unico file, creo molti file ts
    • Un modulo è un file con la parola chiave export o import.
    • Un modulo può definito in un file e importato e utilizzato in un altro file typescript della mia applicazione
      • //nel file shapes.ts definisco 2 classi e le esporto
        export class Point  { .. }
        export class Square { .. }
        
        //nel file main.ts importo le due classi dal file square.ts e le uso
        //se importo moduli definiti nell'applicazione in from utilizzerò il path relativo
        import { Point, Square } from './square
        let point
        
        

Binding

  • Property binding & String interpolation
    • Quando un <component> viene compilato, Angular è capace di legare (bind) le prorietà degli elementi del DOM alle proprietà del <component>. Per forzare il bind è sufficiente mettere tra parentesi quadre tale proprietà (vedi esempio sotto)
    • //E' possibile mischiare del codice HTML con delle variabili definite nel <component> tramite  
      
      -- string interpolation syntax  
      <img src={{ imageUrl }} /> 
      
      oppure
      
      -- property binding syntax  (component -> view)
      <img [src]="imageUrl" />
      
      -- component
      export class MyComponent { 
         imageUrl = <img_path_url>;
         isActive = true; 
      }
      
      
      -----------------------------------------------------------------------
      Regola generale 
      - per i testi (in <p>, <span>, <div>) è più succinta la sintassi string interpolation 
      - per settare le proprietà degli elemnti del DOM è più succinta la sintassi property binding
  • Attribute binding
    • Normalmente esiste un mapping 1 a 1 tra gli attributi degli elementi HTML e gli attributi degli elementi del DOM.
    • Esistono invece alcuni casi dove invece un attributo presente nell’HTML non ha corrispondenza nel DOM.
    • In questo caso, è necessario usare una sintassi leggermente diversa per poter utilizzare il binding  
      • <tr>
           <td [attr.colspan]="colspan"></td>
        </tr>
  • Class and style binding
    • //Se la variabile <isActive> del nostro <compoment> è = true allora aggiungo 
      //all'elemento <button> la classe <active> altrimenti se <isActive>=false non aggiungo niente
      <button class="btn btn-primary" 
              [class.active]="isActive"
              [style.backgroundColor]="isActive ? 'blue' : 'red'">Press!</button>
      
      --component
      export class MyComponent {
         isActive = true | false;
      }
  • Event binding / Event filtering / Template variables
    • <button (click)="onSave($event)">Press!<button>
      
      //Event filtering and template variables
      <input #email type="text" (keyup.enter)="onKeyUp(email)" />
      export class MyComponent {
         onSave($event:any) { 
            ..
      
            //evita l'event bubbling. Interrompe cioè la propagazione dell'evento agli elementi superiori del DOM
            $event.stopPropagation(); 
         }
      
         onKeyUp(email:any) { 
            console.log(email); 
         }
        
      }
  • Two-way binding
    • Normalmente  il property binding è dal <component> => <view>. E’ possibile abilitare il binding bidirezionale. Per vedere anche nel <component> le modifiche fatte nel <view>.
    • <input [(ngModel)]="email" type="text" (keyup.enter)="onKeyUp" />
      
      export class MyComponent {
         email = "indicare una mail valida!";
      
         onKeyUp(){
            console.log(this.email);
         }
      }
      
      
      //app.module.ts => E' necessario importare esplicitamente gli angular FormsModule 
      import { FormsModule } from '@angular/forms';
      
      @NgModule({
         imports: [
            FormsModule
         ]
      })

 

Angular Fundamentals

    • Components
      • Definiscono le parti i cui è stata suddifica l’interfaccia utente.
      • Suddividere il mio codice in più componenti mi permette di scrivere del codice più leggibile e riusabile.
      • Un’applicazione Angular è essenzialmente un tree di componenti che partonto dal componente App (root)
      • Ogni component contiene:
        • Data
        • HTML template
        • Logic
      • Moduli => un modulo angular contiene un gruppo di componenti correlati tra loro.
        • Ogni applicazione Angular ha almeno un modulo root chiamato App module.
        • Ogni applicazione Angular ha almeno un component root chiamato App component.
      • Aggiungere un componente manualmente
        • Crearlo (courses.component.ts)
          • //courses.component.ts => creo il componente CoursesComponent
            //E' una classe con la dichiarazione di un @Component 
            //e nel cui nome per convenzione si sua la parole Component
            
             
            import { Component } from '@angular/core';
            
            @Component({
                selector: 'courses',   //definisco il tag <courses> da usare 
                                       //per poter aggiungere questo componente al nostro HTML
                template: '<h2>{{ 'Title: ' + title }}</h2>'
                [oppure templateUrl: './app.component.html']
            })
            
            export class CoursesComponent {
               title = 'my title';
            }
        • Registrarlo in un modulo
          • import { CoursesComponent } from './course.component';
            
            //apro app.module.ts
            //aggiungo il componente CoursesComponent a quelli già dichiarati in @NgModule
            @NgModule({
              declarations: [
                AppComponent,
                CoursesComponent 
              ],
              ...
            })
        • Aggiungerlo in un HTML markup
          • //Nel file app.component.html (la home page)
            <courses></courses>
      • Aggiungere un componente tramite CLI
        • //g=generate
          //c=component
          ng g c <component-name> (es: courses, senza la parola Component)
          -- creando una cartella
          ng g c vehicle-form --module ../app  (src/app/vehicle-form/vehicle-form.component.ts)
          //tramite il comando sopra Angular esegue
          create src/app/course/<component-name>.component.css
          create src/app/course/<component-name>.component.html
          create src/app/course/<component-name>.component.spec.ts
          create src/app/course/<component-name>.component.ts
          update src/app/app.module.ts
          
          Una volta creato il component è necessario aggiungere un nuovo root in app.module.ts
          per mappare l'URL al componente
    • Directives (comandi speciali per manipolare il DOM)
      • Structural => modificano la struttura del DOM
      • Attribute  => modificano gli attributi del DOM
      • -- Angulare traduce l'asterisco (*) iniziale presente nelle direttive con <ng-template ..>
        -- change detection (Angular di default si accorge dei cambiamenti in memoria delle variabili 
                             e rilancia automaticamente il rendering dell'elemento cambiato) 
        -- trackBy (per migliorare le performance è possibile passare da trackBy:memoria a trackBy:id) 
        -- exported values
        
        1- ngFor 
        
        @Component({ 
           selector: 'courses', 
           template: `
             <h2>{{ title }}</h2>
             <ul>
                <li *ngFor="let course of courses; 
                            index as i;
                            first;
                            last;
                            even;
                            odd">
                   {{i}} - {{ course }}
                </li>
             </ul>` 
        })
        
        export class CoursesComponent {
           title = "List of courses";
           courses = [];
        
           //dependency injection
           constructor(service:CoursesService) {
             this.courses = service.getCourses();
           }
        }
        
        2- ngIf
        <div *ngIf="courses.length > 0">Lista dei corsi</div>
        <div *ngIf="courses.length == 0">Nessun corso</div>
        
        oppure
        
        <div *ngIf="courses.lenght > 0; then coursesList; else noCourses"></div>
        <ng-template #coursesList>List of courses!</ng-template>
        <ng-template #noCourses>No courses!</ng-template>
        
        oppure (per piccoli tree, perchè nasconde senza eliminare dal DOM)
        
        <div [hidden]="courses.lenght == 0">List of courses!</div>
        <div [hidden]="courses.lenght > 0">No courses!</div>
        
        3- ngSwitchCase
        
        <div [ngSwitch]="courses.lenght">
           <div *ngSwitchCase="'0'">Zero corsi</div>
           <div *ngSwitchCase="'1'">Un corsi</div>
           <div *ngSwitchDefault>Molti corsi</div>
        </div>
        
        4- ngClass (attribute directive)
        
        <span [class.glyphicon-star]="isSelected"
              [class.glyphicon-star-empty]="!isSelected"></span>
        oppure
        <span [ngClass]="{
            'class.glyphicon-star': isSelected,
            'class.glyphicon-star-empty': !isSelected
        }"></span>
        
        5- ngStyle (attribute directive)
        
        <button [ngStyle]="{
           'backgroundColor': canSave ? 'blue' : 'gray',
           'color': canSave ? 'white' : 'black',
        }"></button>
    • Custom directives
      • -- creo un nuovo custom-directive
        ng g d <directive-name>
        
        -- app.component.html
        <input type='text' [<directive-name>]="'uppercase'" />
        
        -- <directive-name>.directive.ts
        import { HostListener, ElementRef, Input } from '@angular/core';
        
        @Directive({
          selector: '[appInputFormat]'
        })
        
        export class InputFormatDirective {
           @Input('appInputFormat') format:any;
           constructor(private el: ElementRef){ .. }   
        
           //sull'evento onBlur faccio diventare lowercase il contenuto del campo
           @HostListener('blur') onBlur() {
             let value:string = this.el.nativeElement.value;  
             
             if(this.format == 'uppercase')
                this.el.nativeElement.value = value.toLowerCase();
             else 
                this.el.nativeElement.value = value.toLowerCase();
           }
        }
    • Services
      • Esternalizzare la logica (per recuperare i dati da visualizzare nel <component>) in un altro tipo di classi chiamato <service> permette di =>
        • Evita che la presentazione (<components>) e la logica di recupero dati (<services>) siano fortemente accoppiati
        • Da’ la possibilità di riutilizzare il codice scritto in un <service>
      • //courses.service.ts
        //E' una semplice plain text class con la convezione di avere la parola Service nel proprio nome
        
        //Decoratore @Injectable: è necessario inserire questo decorator solo 
        //se il nostro servizio ha bisogno di dipendenze nel suo costruttore
        //Non è invece da esplicitare nel caso di un <component> avendo utilizzato 
        //già il decoratore @Component che lo include già 
        [
        @Injectable({
          providedIn: 'root'
        })
        ]
        
        export class CoursesService {
            constructor(){ .. }
            getCourses() {
                return ["course1", "course2", "course3"];
            }
        }
        
      • Aggiungere un componente tramite CLI
        • //g=generate 
          //s=service 
          ng g s <service-name>  (es: courses, senza la parola Service)
          
          //tramite tale comando Angular esegue:
          create src/app/<service-name>.service.spec.ts
          create src/app/<service-name>.service.ts
  • Dependency injection (DI)
    • Per usare un <service> in un <component> si utilizza un componente presente in Angular che implementa il meccanismo della DI.
    • Bisogna registrare le varie dipendenze di tutti i componenti di un modulo 
      • //app.module.ts
        @NgModule({
          providers: [
             CoursesService,
             //E' possibile dire a Angular di usare una classe (AppErrorHandler) al posto di un altra (ErrorHandler)
             { provide: ErrorHandler, useClass: AppErrorHandler }
          ]
        })
      • Quando Angular vede che il <component> ha delle dipendenze registrate, prima di crearne un’instanza crea le istanze di tali dipendenze.   
    • Singleton: quando si registra una dipendenza come <provider> in un <module>, Angular crea un’unica istanza di tale dipendenza da “iniettare” in tutti i componenti del modulo che la richiedono. 
  • Formatting data
    • Build-in pipes
      • <h2>{{ fistName | uppercase }}</h2>
        <h2>{{ rating   | number:'5.2-2' }}</h2>  (5 interi, 2 decimali) 
        <h2>{{ price    | currency:'EUR':true:'3.2-2' }}</h2> (3 interi, 2 decimali) 
        <h2>{{ date     | date:'shortDate'}}</h2>
        
        
        export class MyComponent() {
           firstName;
        }
        
        
    • Custom pipes
      • 1- creare il nuovo file => <custom-pipe-name>.pipe.ts
        import {Pipe, PipeTransform } from '@angular/core';
        
        @Pipe({
           name: summary
        })
        
        export class <custom-pipe-name>Pipe implements PipeTransform {
             transform(value:any, args?:any) {
                 return ..
             }
        }
        
        2- modificare il file => app.module.ts
        @NgModule({
          declarations: [
              <custom-pipe-name>Pipe
          ]
        })
        
        3- usare il nostro pipe customizzato in un componente
        {{ longText | <custom-pipe-name>Pipe }}
        
        
        0- generare Pipe con Angular CLI
        ng g p <custom-pipe-name>

 

Component API

  • Component input properties
    • -- file app.component.html
      //il componente <app.component> padre passare un valore 
      / alla proprietà isFavorite del componente <favorite>
      <favorite isFavorite="post.favorite"></favorite>
      
      -- file app.component.ts
      export class AppComponent {
        post = {
           title: "Title",
           isFavorite = false
        }
      }
      
      -- <component> favorite  --
      // METODO 1 (uso direttiva @Input) 
      import { Input } from '@angular/core';
      
      export class FavoriteComponent {
          -- espongo la proprietà isFavorire
          @Input() isFavorite;
      }
      
      // METODO 2 (uso @Component) 
      @Component({
         inputs: ['isFavorite']
      })
      export class FavoriteComponent {
         isFavorite;
      }
    • Alias Input properties
      • E' possibile dare un alias alle proprietà di un componente che si decide di rendere visibili all'esterno
        Nel nostro esempio la proprietà isFavorite del componente <favorite> diventa 'is-favorite' all'esterno 
        
        -- file app.component.html
        <favorite [is-favorite]="post.favorite"></favorite> 
        
        -- <component> favorite
        @Input('is-favorite') isFavorite
  • Component output properties
    • -- file app.component.html
      <favorite (change)="onFavoriteChanged($event)"><favorite>
      
      -- file app.compoment.ts
      import { FavoriteChangedEventArgs } from /.favorite/favorite.component';
      export class AppComponent {
         onFavoriteChanged(eventArgs:FavoriteChangedEventArgs ) {
            console.log("Favorite has changed!" + eventArgs.newValue);
         }
      }
      
      -- component <favorite>
      import {Output, EventEmitter } from '@angular/core';
      
      export class FavoriteComponent {
         isFavorite: boolean = false;
         @Output() change = new EventEmitter();
      
         onClick() {
            this.isFavorite = !this.isFavorite;
            this.change.emit({newValue:this.isFavorite});
         }
      }
      export interface FavoriteChangedEventArgs {
         newValue:boolean;
      }
    • Alias output properties
      • E' possibile dare un alias alle proprietà di un componente che si decide di rendere visibili all'esterno 
        Nel nostro esempio la proprietà isFavorite del componente diventa 'is-favorite' all'esterno 
        
        -- file app.component.html  
        <favorite (my-change)="onFavoriteChanged($event)"></favorite>
        
        -- <component> favorite 
        @Ouptup('my-change') change = new EventEmitter();
        
        onClick() {
           this.change.emit();
        }
  • ViewEncapsulation
    • Emiulated => Angular cerca di emulare il comportamento del Shadow DOM. Un stile applicato ad un componente sarà valido solo in quel componente. Non viene propagato (non fa ombra) sugli altri elementi della pagina esterni a quel componente
    • Native => Angular non emulare il comportamento del Shadow DOM ma lo lascia gestire al broser stesso. (Solo i browser moderni lo fanno).
    • None =>
      • @Component({
           encapsulation: ViewEncapsulation.Emulated [default] | Native | None
        })
  • ngContent / ngContainer
    • -- app.content.html
      <my-panel>
         <ng-container class='heading'>Titolo</ng-container>
         <ng-container class='body'>Ipse dixit, Ipse dixit..</ng-container>
      
         oppure
      
         <ng-container id='heading'>Titolo</ng-container>
         <ng-container id='body'>Ipse dixit, Ipse dixit..</ng-container>
      </my-panel>
      
      -- <my-panel>.component.html
      //Nel componente <my-panel> definisco due placeholder richiamabili dall'elemento subscriber 
      //come fosse delle classi o degli id in css 
      <div class='panel panel-default'>
        <div class='panel-heading'>
           <ng-content select='.heading'></ng-content>
           
           oppure
           
           <ng-content select='#heading'></ng-content>
        </div>
        <div class='panel-body'>
           <ng-content select='.body'></ng-content>
           
           oppure
           
           <ng-content select='#body'></ng-content>
        </div>
      </div>
      
      

 

Form

  • Form Template-driven (Angular crea automaticamente le cose in background)
    • Quando usarli
      • Per form più semplici
      • Con validazione semplice
      • Più facili da creare
    • ngForm  / ngSubmit =>
      • Angular applica automaticamente al <form> la direttiva ngForm che crea la classe formGroup (rappresenta tutti i campi)
      • Espone la proprietà ngSubmit che serve per catturare il contenuto del form
    • ngModelGroup =>
      • Se il form è molto grosso è possibilie opzionalmente suddividerlo applicando delle direttive ngModelGroup  (rappresenta un gruppo di campi)
    • ngModel =>
      • Applicando la direttiva ngModel al campo del form, Angular gli attacca automanticamente formControl (rappresenta un campo)
      • @import { FormsModule } from '@angular/forms';
        
        //Applicando ngModel a l'attributo name ad un campo del nostro form Angular crea automaticamente 
        //un'instanza della classe controlForm e la associa a tale campo
        <input ngModel name='firstName' ...>
    • Validation / ngValue =>
      • -- contact-form.component.html
        //Esempio di validazione del campo 'firstName'
        <form #f="ngForm" (ngSubmit)="submit(f)">
           <div ngModelGroup="contact" #contact="ngModelGroup">
           <div #ngIf="!contact.valid">...</div> <!-- messaggio d'errore per il gruppo di campi -->
              <div class="form-group">
                 
                 <!-- input text -->
                 <label for="firstName">First Name</label>
                 <input required minlength="3" pattern="banana" ngModel name="firstName" id="firstName" 
                    type="text" class="form-control" #firstName="ngModel" (change)="log(firstName)" />
                  
                 <!-- validation messages -->
                 <div class="alert alert-danger" *ngIf="firstName.touched && !firstName.valid">
                     <div *ngIf="firstName.errors?.['required']">First name is required</div>
                     <div *ngIf="firstName.errors?.['minlength']">First name should be 
                        {{ firstName.errors?.['minlength']['requiredLength'] }} minimum charactes</div>
                     <div *ngIf="firstName.errors?.['pattern']">First name doesn't match the pattern</div>
                  </div>
              
             </div>
           </div>
        
           <!-- checkbox -->
           <div class="checkbox">
             <label>
                <input type="checkbox" ngModel name="isSubscribe" /> Subscribe to mailing list
             </label>
           </div>
        
           <!-- dropdown list -->
           <div class="form-group">
              <label for="contactMethod">Contact Method</label>
              <select ngModel name="contactMethod" id="contactMethod" class="form-control">
                   <option *ngFor="let m of contactMethods" [value]="m.id">{{ m.name }}</option>
              </select>
           </div>
        
           <!-- radio button -->
           <div *ngFor="let m of contactMethods" class="radio">
             <label>
                <input ngModel type="radio" name="contactMethod" [value]="m.id" /> {{ m.name }}
             </label>
           </div>
        
           <!-- radio button sulla stessa linea -->
           <label for="radio1" class="radio-inline"> 
              <input type="radio" id="radio1" name="group1"  /> 
           </label>
           <label for="radio2" name="group1" class="radio-inline"> 
              <input type="radio" id="radio2" name="group1" /> 
           </label>
        
           <!-- per debug -->
           <p>{{ f.value | json }</p>
        
           <!-- submit button -->
           <button class="btn btn-primary" [disabled]="f.valid">Submit</button>
        </form>
        
        
        -- contact-form.component.ts
        export class ContactFormComponent {
           contactMethods = [
             { id:1, name: 'email' },
             { id:2, name: 'phone'},
           ];
        
           submit(f:any) { .. }   
        }
         
        -- CSS
        .form-control.ng-touched.ng-invalid {
          border:2px solid red;
        }
  • Form Reactive (anche Model-driven, lo sviluppatore ha piena libertà e sviluppa tutto ad hoc)
    • Quando usarli
      • Per form più complessi
      • Validazione può essere customizzata a piacimento
      • Unit testable
    • AbstractControl => E’ la classe base per le classi FormControl, FormGroup, and FormArray
    • E’ necessario esplicitare la struttura nel nostro <form> nel componente. (usare formBuilder)
    • E’ possibile usare 3 direttive per collegare gli elementi del DOM con il componente
      • formGroup
        • collega l'elemento <form> con la variabile form dichiarata nel componente 
          <form [formGroup]="form">
      • formGroupName
        • <div formGroupName="contact">
            <div formControlName="email">
            <div formControlName="address">
          </div>
      • formControlName
        • <input formControleName="username" />
      • formArray
        • <li *ngFor="let item of topics.controls">
          
          --
          form;
          
          constructor(fb:FormBuilder){
            this.form = fb.group({
               topics: fb.array([])
            })
          }
          
          get topics(){
             return this.form.get('topics') as FormArray;
          }
    • FormControl
      • -- app.module.ts
        //aggiungere il modulo ReactiveFormsModule nella sezione imports
        @NgModule({
           imports: [
              ReactiveFormsModule
           ]
        })
        
        -- signup-form-component.html
        -- al <form> bisogna agggiungere il binding alla variabile form definita nel component
        -- ad ogni campo bisogna aggiungere formControlName
        <form [formGroup]="form">
           <div class="form-group">
              <label for="username">Username</label>
              <input formControlName="username" id="username" type="text" class="form-control" />
              <div *ngIf="u.touched && u.errors.['required']" class="alert alert-danger">Username is required</div>
              <div *ngIf="u.touched && u.errors.['minlength']" class="alert alert-danger">Greater than 3</div>
              <div *ngIf="u.touched && u.errors.['maxlength']" class="alert alert-danger">Less than 10</div>
           </div>
           <div class="form-group">
              <label for="password">Password</label>
              <input formControlName="password" id="password" type="text" class="form-control"/>
              <div *ngIf="p?.touched && p?.invalid" class="alert alert -danger">
                  Password must be greater then 6 letters
              </div>
           </div>
           <button class="btn btn-primary" type="submit">Sign Up</button>
        </form>
        
        -- signup-form-component.ts
        import { FormGroup, FormControl, Validators } from '@angular/forms';
        
        export class SignupFormComponent {
          //creo variabile form da bindare nel tag <form> nella pagina html
          form = new FormGroup({
            username: new FormControl('[optional <initial_value>]', [ 
              Validators.required,
              Validators.minLenght(3),
              Validators.maxLenght(10)
            ]),
            password: new FormControl('', Validators.required)
        
            oppure (javascript accetta le due sintassi)
        
            "username": new FormControl(),
            "password":  new FormControl()
          });
        
          get u() {
             return this.form.get('username');
          }
        
          get p() {
             return this.form.get('password');
          }
        }
        
    • Custom validator / Asyncronous validator
      • //creo file dove metto le mie classi che definisco le mie validazioni personalizzate
        -- username.validators.ts
        import { AbstractControl, ValidationErrors } from "@angular/forms";
        
        export class UsernameValidators {
            //verifico che il campo non contiene spazi
            static cannotContainSpace(control:AbstractControl): ValidationErrors | null {
                if((control.value as string).indexOf(' ') >= 0)
                   return { cannotContainSpace: true };
                
                return null;  
            }
        
            //asyncronous validation
            //verifico che la username inserita non sia 'mario.rossi'
            static shouldBeUnique(control:AbstractControl): Promise {
                return new Promise((resolve, reject) => {
                  //uso setTimeout per simulare una chiamata asincrona  
                  setTimeout( () => {
                        if(control.value === 'mario.rossi')
                           resolve({shouldBeUnique:true });
                        else
                           resolve(null);   
                  }, 2000);       
                });
            }
            
        }
        
        //nel file del mio form aggiungo la validazione personalizzata appena creata
        -- signup-form-components.ts
        form = new FormGroup({
           username: new FormControl('', 
             //syncronous validators  [UsernameValidators.cannotContainSpace],
             //asyncronous validators [UsernameValidators.shouldBeUnique]
           )
        });
        
        
        --signup-form-component.html
        //aggiungo un messaggio che gestisce la validazione asincrona
        //mi avvisa che sto verificando l'unicità della username inserita
        //sfrutto l'attributo pending dell'oggetto form.get('username')
        <div *ngIf="u?.pending">Checking for unique username</div>
            
        
    • Validation onSubmit
      • --signup-form-component.html
        <form [formGroup]="form" (ngSubmit)="login()">
          <div *ngIf="form.errors" class="alert alert-danger">Username or password invalid</div>
        </form>
        
        --signup-form-component.ts
        login() {
           //interrogo il server e setto l'errore alla variabile form
           // o alla variabile che rappresenta il mio campo
           this.form.setErrors({ invalidLogin:true });  //errore generico nel form
           oppure
           this.u.setErrors({ invalidLogin:true });  //errore di un campo specifico
        }
        
        
    • FromGroup (raggruppare i campi del form in differenti sotto gruppi)
      • //E' possibile raggruppare i campi nel form in sottogruppi e usare la classe FormGroup per gestire
        //i vari gruppi di campi
        //creo un gruppo di campi chiamato account con 2 campi (username, password)
        
        -- signup-form-component.html
        <form [formGroup]="form">
         <div fromGroupName='account'>
            <div formControlName='username'></div>
            <div formControlName='password'></div>
         </div>
        </form>
        
        -- signup-form-component.ts
        form = new FormGroup({
            account: new FormGroup({
                username: new FormControl('', Validators.required),
                password: new FormControl('', Validators.required)
            })
        });
        
        get u() {
           return this.form.get('account.username');
        }
    • FormArray
      • -- form-array-component.html
        <form>
           <input type="text" class="form-control" (keyup.enter)="addTopic(topic)" #topic />
           <ul class="list-group">
             <li *ngFor="let item of topics.controls" (click)="removeTopic($any(topic))" class="list-group-item">
                {{ item.value }}
             </li>
           </ul>
        </form>
        
        --form-array-component.ts
        import { FormArray, FormControl, FormGroup } from '@angular/forms';
        
        export class FormArrayComponent {
           form = new FormGroup({
              topics: new FormArray([])
           });
        
           addTopic(topic: HTMLInputElement) {
             this.topics.push(new FormControl(topic.value));
             topic.value ='';
           }
        
           removeTopic(topic: FormControl) {
            let index = this.topics.controls.indexOf(topic);
            this.topics.removeAt(index);
          }
        
           get topics() {
             return this.form.get('topics') as FormArray;
           };
        }
        
        
    • FormBuilder
      • -- form-builder-component.html
        
        -- form-builder-component.ts
        import { FormArray, FormBuilder, FormControl, FormGroup } from '@angular/forms';
        
        export class FormBuilderComponent {
           form;
           constructor(fb:FormBuilder){
               this.form = fb.group({
                   topics: fb.array([]),
                   username: ['', Validators.required],
                   contact: fb.group({
                      adress: [],
                      zipcod: []
                   })
               })
           }
        }

 

Consuming HTTP service

  • CRUD
    • Leggere dati (GET)
      • -- app.module.ts
        //importare il modulo per la gestione delle comunicazioni HTTP
        //così facendo tutte le classi definite al suo interno sono automaticamente
        //utilizzabili nei construttori dei miei componenti, in quando all'interno di HttpClientModule 
        //è definita una propria derittiva @NgModule (come in tutti i moduli) dove è stato
        //già inizializzato l'array dei servizi forniti (providers:[])
        import { HttpClientModule } from '@angular/common/http';
        
        @NgModule({
           imports: [
              HttpClientModule
           ]
        })
        
        
        --post.service.ts
        import { Injectable } from '@angular/core';
        import { HttpClient } from '@angular/common/http';
        
        @Injectable({
          providedIn: 'root'
        })
        export class PostService {
          //private url = "https://jsonplaceholder.typicode.com/posts";
        
          constructor(private http: HttpClient){
          }
        
          getPosts() {
            //return this.http.get(this.url);
            return this.http.get('/makes');
          }
        }
        
        
        --posts-component.ts
        import { PostService} form '../../../services/post.service';
        
        export class PostsComponent implements OnInit { 
           posts:any;
           
           constructor(private service: PostService) { .. }
        
           ngOnInit(): void {
            this.service.getPosts()
              .subscribe({
                 next: (data) => { this.posts = data },
                 error: (e) => {
                    alert('An unexpedcted error occured.');
                    console.log(e);
                 }
             });
          }
          
        }
        
    • Creare nuovi dati (POST)
      • -- post.service.ts
        createPost(post: any) {
          return this.http.post<{id:number}>(this.url, JSON.stringify(post));
        }
        
        -- posts.component.ts
        createPost(input: HTMLInputElement) {
            let post:any = { title: input.value }; 
            input.value = '';
        
            this.service.createPost(post)
              .subscribe({
                  next: (data) => {
                     post.id = data.id;
                     this.posts.splice(0, 0, post);
                  }, 
                  error: (e) => {
                    alert('An unexpected error occured.');
                    console.log(e);
                  } 
              });
        }
        
    • Modificare dati (PUT/PATCH)
      • updatePost(post:any){
           //E' possibile usare PATCH per modificare solo un sottoinsieme delle proprietà dell'oggetto
           //In tal caso si passa solo le proprietà modificare (es: title) in JSON
           //Se invece è stato modificato il maggior numero di proprietà dell'oggetto meglio usare PUT
           //In tal caso si passo tutto l'oggetto in JSON
           this.http.patch(this.url + '/' + post.id, JSON.stringify({title:post.title})
                     [.put(this.url + '/' + post.id, JSON.stringify(post)]
                    .subscribe(data => {
                        console.log(data);
                    }   
        }
    • Eliminare dati (DELETE)
      • -- post.service.ts
        deletePost(postId: number) {
          return this.http.delete(this.url + '/' + post.id);
        }
        
        -- posts.component.ts
        deletePost(post:any){
            this.service.deletePost(post.id)
                .subscribe({
                   next: (data) => {
                      let index = this.posts.indexOf(post);
                      this.posts.splice(index, 1);
                   }, 
                   error: (e) => {
                      if(e.status === 404)
                        alert('This post has already been delete!);
                      else {
                        alert('An unexpected error occured.');
                        console.log(e);
                      }
                   }
                });
        }
        
  • Global error handling
    • -- app.module.ts
      //Sostituire la classe di Angulare che gestisce gli errori normalmente con la classe custmo AppErrorHandler
      providers: [
          { provide: ErrorHandler, useClass: AppErrorHandler}
        ],
      
      -- /common/app-error-handler.ts
      import { ErrorHandler } from "@angular/core";
      
      export class AppErrorHandler implements ErrorHandler {
          handleError(error: any): void {
              -- some code here! --
          }
      }
      
  • Pessimistic vs Ottimistic approach
    • Quando si implementano le operazioni CRUD, è possibile seguire 2 approcci
      • Pessimistic approach => Modifico l’interfaccia solo dopo aver ricevuto la risposta positiva dal servizio HTTP che esegue l’operazione richiesta.
      • Optimisti approach => Modifico l’interfaccia subito, senza aspettare la risposta del servizio. Nel caso la risposta sia effettivamente negativa, inserisco la logica per annullare la modifica apportata all’interfaccia.
  • Observables vs Promies
    • Observables =>
      • lazy (un servizio observable non viene eseguito finché non viene definito il suo metodo subscribe) .
      • Permettono reactive programming
      • E’ possibile convertire un observable verso un promise => promise = observable.ToPromise()
    • Promies => eager  (un servizio asincrono viene eseguito anche se non è stato definita una funzione di callback (then o catch).
  • Lifecycle hooks
    • OnInit => ngOnInit (usato per inizializzare variabili nei <component>)
      OnChanges => ngOnChanges
      DoCheck => ngDoCheck
      AfterContentInit => ngAfterContentInit

 

Routing

  • Configurare il routing
    • //Definisco gli accoppiamenti tra l'URL visitato dall'utente e il componente da visualizzare 
      //Deve andare dal più specifico al più generico 
      //'**' è una wildcard che significa tutti i path
      
      -- app.module.ts 
      import { RouterModule } from '@angular/router';
      
      
      imports: [
        RouterModule.forRoot([
            {
              path: '', 
              component: HomeComponent 
            },
            {
              path: 'followers/:username', 
              component: GithubProfileComponent
            },
            { 
              path: 'followers', 
              component: GithubFollowersComponent 
            },
            { 
              path: 'posts', 
              component: PostsComponent },
            {
              path: '**', 
              component: NotFoundComponent
            }
          ])
      ]
  • Aggiungere <router-outlet>
    • Affinche il meccanismo di routing funzioni così come settato nel  <RouteModule> è necessario definire almeno un <Router-Outlet>. 
    • Un <Router-Outlet> agisce come un placeholder che Angular riempie dinamicamente in base alla corrente impostazione del browser.
    • -- app.component.html
      <navbar></navbar>
      <router-outlet></router-outlet>
  • Aggiungere <routerLink>
    • 1- Link senza parametri 
      --navbar.component.html
      <a routerLink="/followers">Followers</a>
      <a routerLink="/posts">Posts</a>
      
      
      2- Link con passaggio di parametri
      --followers-component.html 
      <a [routerLink]="['/followers', f.id]">Vedi dettaglio del follower</a>
      //cliccando, l'utente navigherà alla pagina => /followers/1234 (dove f.id=1234) 
      3- Settare il link attivo
      <li routerLinkActive="active current">
       <a [routerLink]="['/followers', f.id]">Vedi dettaglio del follower</a>
      </li>
      
      4- Leggere i parametri passati
      --follower-detail-component.ts
      import { ActivatedRoute } from '@angular/router';
      
      export class FollowerDetailComponent implements OnInit {
        constructor(private route: ActivatedRoute){ .. }
        id: any;
        ngOnInit() {
          //se il componente può essere richiamato da se stesso allora
          //devo passare per la versione observable di route.paramMap
          this.route.paramMap
             .subscribe(params => {
                this.id = params.get('id'); 
             })
      
          //altrimenti posso usare la versione semplice non asincrona
          //infatti se l'utente arriva da questo componente da un altro
          //allora abbiamo la certezza che OnInit verrà sempre eseguito
          //non abbiamo bisogno di fare il subscribe della versione observable di route.paramMap
          this.id = this.route.snapshot.paramMap.get(id');
      
        }
      }
      
      Angular quando naviga ad un nuovo componente, distrugge il precedente e aggiunge al DOM il corrente.
      Quando navigo da un componente allo stesso (ad es: visualizzo un log e dopo il seguente) questo meccanismo non avviene.
      Angular si accorge che è inutile distruggere e creare lo stesso componente.
      Questa è la ragione per cui si usa un observable per leggere i parametri del componentente ActivatedRoute
      
      
      5- Link con parametri in queryString (QS)
      //scrivo link con parametri in QS
      -- navbar.component.html
      <a routerLink="/followers"
        [queryParams]="{ page: 1, order: 'newest' }">Go to followers</a>
      
      //leggo parametri in QS
      --followers.component.ts
      //caso in cui il componente possa autochiamarsi
      //devo usare la versione asincrona del metodo che mi permette di leggere i parametri in QS
      this.page = this.route.queryParamMap.subscribe(params => {
                    this.page =  params.get('page');
                  });
      
      //caso in cui il componente è acceduto sempre e sicuramente da un altro componente
      //posso non uso versione asincrona 
      this.page = this.route.snapshot.queryParamMap.get('page');
          
      
      6- Navigare via codice
      import { Router } from '@angular/router';
      
      export class GithubProfileComponent {
       constructor(private route:Router) { .. }
       this.route.navigate(['/followers/1'], {
              queryParams: { page:1 , order: 'newest' }
            });
      }
  • Combining observables
    • //E' possibile unire più observables e fare un unico subscribe complessivo.
      //In altre parole è possibile combinare più eventi asincroni e sottoscrivere un unico metodo che li attende entreambi
      import { combineLatest } from 'rxjs'
      
      export class GithubFollowersComponent implements OnInit {
        followers?: any[];
        
        constructor(private route: ActivatedRoute, private service:GithubFollowersService) { }
      
        ngOnInit(): void {
          const paramMap = this.route.paramMap;
          const queryParamMap = this.route.queryParamMap;
         
          //caso1 => leggo i parametri passati nell'URL in modo asincrono
          //combino 2 observables insieme
          combineLatest([
            paramMap, // error here
            queryParamMap
          ])
          .pipe(
            //leggo gli observables e mappo l'uscita come un array di followers
            switchMap(([val1, val2]) => {
               let id = val1.get('id'); 
               this.page = val2.get('page');
               return this.service.getAll();
            })
          )
          .subscribe(followers => {
            this.followers = followers;
          });
          
          //caso2 => leggo i parametri passati nell'URL in modo sincrono
          //this.route.snapshot.paramMap.get('id');
          //this.page = this.route.snapshot.queryParamMap.get('page');
          //this.service.getAll()
          //  .subscribe({
            //   next: followers => this.followers = followers
          // });
          
        }
      
      }
      
      
      

 

Autenticazione e autorizzazione

  • Bearer authentication => In Angular l’autenticazione/autorizzazione è implementata tramite i JSON web tokens (JWT)
    • Un JWT ha 
      • header
      • payload => è la sezione di un JWT che contiene i dati che vogliamo passare dal client al server.
      • digital signature => creata in rapporto al contenuto dell’header e del payload. E’ generata sulla base di una chiave secret conosciuto solo dal server.
    • Sul sito jwt.io
      • si trovano le librerie da usare sia lato client che lato server per implementare l’autenticazione/autorizzazione tramite i JSON Web tocked
      • un degubber per generare al volo un token (in rapporto al contenuto delle sue 3 sezione e della chiave secret) e usarlo nella nostra applicazione per debuggarla.
  • NPM: angular2-jwt (vecchie versione  / HttpInterceptor (nuove versioni)
  • Usare *ngIf per mostrare o nasconde alcune 
  • Proteggere gli indirizzi (routes) => Usare i servizi guards
    • E’ possibile proteggere l’accesso ad alcune pagine del nostro sito implementando dei servizi Guard e usarli nella definizione del RouterModule in app.module.ts
    • -- app.module.ts
      //Abbiamo protesso l'accesso al component AdminComponent con il guard AdminAuthGuard
      RouterModule.forRoot([
            { path: '', component: HomeComponent },
            { path: 'admin', component: AdminComponent, canActivate: [AdminAuthGuard] }
          ])
      
      -- admin.auth-guard.service.ts
      //Se l'utente è un admin = true, else = false
      //Questo servizio eredita da AuthGuard che è un altro Guard che verifica se l'utente è loggato o no
      import { AuthGuard } from './auth-guard.service';
      import { Injectable } from '@angular/core';
      
      @Injectable()
      export class AdminAuthGuard extends AuthGuard {
      
        canActivate() {
          let isAuthenticated = super.canActivate();
          if (!isAuthenticated)
            return false; 
      
          if (this.authService.currentUser.admin)
            return true; 
      
          this.router.navigate(['/no-access']);
          return false;
        }
      }
      
      -- auth-guard.service.ts
      import { AuthService } from './services/auth.service';
      import { Injectable } from '@angular/core';
      import { Router, CanActivate } from '@angular/router';
      
      @Injectable()
      export class AuthGuard implements CanActivate {
      
      constructor(protected router: Router, protected authService: AuthService) { }
       
        canActivate() {
          if (this.authService.isLoggedIn()) return true;
      
          this.router.navigate(['/login']);
          return false;
        }
      }
      
  • Chiamare API endpoints protetti (accessibili solo ad utenti loggati) => usare AuthHttp
    • -- order.service.ts
      import { Injectable } from '@angular/core';
      import { AuthHttp } from "angular2-jwt/angular2-jwt";
      
      @Injectable()
      export class OrderService {
      
        //Per chiamare un API protetta è sufficiente sostituire HTTP con authHTTP
        //AuthHTTP al suo interno aggiunge automaticamente un header con il campo 'Authorization' 
        //senza doverlo fare automaticamente (come invece bisogna fare se si usa http)
        constructor(private authHttp: AuthHttp) {
        }
      
        getOrders() { 
          //si possono saltare tutte le seguenti linee di codice
          //let headers = new Headers();
          //let token = localStorage.getItem('token');
          //headers.append('Authorization', 'Bearer ' + token
          //let options = new RequestOptions({ headers: headers }); 
          //return this.http.get('/api/orders', options)
          return this.authHttp.get('/api/orders')
            .map(response => response.json());
        }
      }

 

Deployment

  • Tecniche di ottimizzazione pre-deploy
    • Minification =>  Lanciare un tool per minificare il nostro codice
    • Uglification => Lanciare un tool per ririscrivere nomi lunghi e descrittivi in nomi corti e senza un significato per gli umani.
    • Bundling =>
      • Combinare più file js e css insieme e fare il deploy di 1 solo file
      • Il bundling permette al client che utilizzerà la nostra applicazione di eseguire meno http request possibili verso il server per scaricare le librerie utilizzate nell’applicazione stessa.
        • il client di carica più velocemente.
        • Il server può essere a disposizione per più client alla volta.
    • Dead code elimination => Eliminare tutto il codice non utilizzato.
    • Ahead-of-time compilation (AOT)
    • //Angular compila l'applicazione specificatamente per un ambiente di prod
      //quindi applica tutte le tecniche di ottimizzazione
      ng build [--configuration=production]
      
      ng build [--configuration=development]
  • AOT vs JIT compilation
    • Just-in-time compilation
      • Angular compiler è eseguito per ogni utente
      • Compila interamente l’applicazione, quindi in caso di grosse applicazione, risulta un processo lento
      • Più adatto all’ambiente di sviluppo
      • Il compiler va deployato insieme all’applicazione.
    • AOT
      • L’applicazione parte più velocemente
      • Non dobbiamo deploiare il compilatore
      • Possono essere intercettati alcuni errori prima del deploy stesso 
      • Più sicuro => meno possibilità di injection attacks
  • Angular compiler
    • -- /package.json
      "dependencies": {
        "@angular/compiler: "^4.0.0"
      }
      
      -- eseguire il compiler
      //creo nella cartella (es: ./src/dist/out-tsc) indicata nel file tsconfig.json
      //il file .map per ogni componente definito nell'applicazione  
      > ./node_modules/ .bin/ngc -p ./src
      
      -- compilo 
      //nella cartella configurata in tsconfig.json Angular mette i file compilati
      //E' possibile indicare quale file delle variabili d'ambiente considerare 
      ng build --env=prod | --env=dev
      
      
  • Aggiungere enviroment
    • 1- creare un nuovo file nella cartella /src/app/enviroment
      environment.staging.ts
      
      2- ./angular.json  [prima < Angular 6.0 era angular-cli.json]
      Nel nodo "project/architect/build/configurations" aggiungere il nodo "staging" copiando quelli presenti
      e indicando in "fileRemplacements" il file creato al punto 1
      "configuration" : {
          "staging":
             "fileReplacements": [
               {
               "replace": "src/environments/environment.ts",
                "with": "src/environments/environment.staging.ts"
               }
            ]
       }
      
       
      3- ./angular.json
      Nel nodo "serve" aggiungere la proprietà "broserTarger"
      "serve":
         "configurations" : {
            "staging": : {
                    "browserTarget": "exercice-blog:build:staging"
                  }
         }
  • Linter
    • tslint => E’ un pacchetto da importare nel progetto che permette di sincronizzare alcuni elementi della sintassi cosi da armonizzare il codice tra diversi collaboratori.
    • //Visualizza un elenco di punti del codice dove sono state infrante le regole definite nel file
      //di configurazione tsling.json
      ng lint
      
      //Permetti a lint di sistemare gli errori che trova
      ng lint --fix
      
      
      2- esling
      //simile a tsling c'è eslint (utile anche per javascript)
      //il file di configurazione è .eslintrc.json
      
      3- VSCode
      //Esiste un'estensione che può essere installata in Visual Studio Code
      
      
  • Fare il deploy verso Github Pages(Ospita solo pagine statiche (html, css, js))
    • 1- Creare un nuovo repository in Github
      2- Collega il git locale con il repo appena creato  
      git remote add origin https://github.com/<github-username>/<repository-name>.git
      
      3- Sincronizza la cartella locale con la cartella remota
      git push -u origin main
      
      4- Installare la CLI Github Pages nel progetto angular
      npm i -g angular-cli-ghpages
      
      5- Fai il build dell'applicazione mettendo come ulr base l'indirizzo delle 'pages' del repo Github
      ng build [--prod] --base-href="https://<github-username>.github.io/<repository-name>/"
      
      6- Lanciare l' Angular-cli-ghpages 
      ngh --dir dist/<angular-app-name>
      
      7- Attiva le 'pages' nel repo Github sul ramo 'gh-pages'
      
      8- L'applicazione è ora disponibile
      https://<github-username>.github.io/<repository-name>/
      
      9- Creare uno custom script in package.json
      "scripts": {
         "deploy:gh": "ng build --base-href='https://leperegoriot1977.github.io/angular-blog/' && ngh --dir dist/exercice-blog"
      }
      
      10- Ogni volta che si vuole deployare su Github Pages 
      npm run deploy:gh
  • Firebase
    • 1- Creare una nuova applicazione in Firebase
      
      2- Installare nella cartella del progetto locale le estenzioni di Firebase per Angular
      npm i -g firebase-tools
      
      3- Loggarsi a Firebase tramite la Angular-CLI e scegliere le opzioni proposte
      firebase login
      
      4- Inizializzare Firebase nel folder del nostro progetto locale => si crea un nuovo file firebase.json
      firebase init
      
      5- Aprire il nuovo file firebase.json e verificare che sia ben impostata la cartella dove verrà fatto il build
      dell'applicazione angular
      -- firebase.json
      {
        "hosting": {
          "public": "dist/exercice-blog",
          "ignore": [
            "firebase.json",
            "**/.*",
            "**/node_modules/**"
          ],
          "rewrites": [
            {
              "source": "**",
              "destination": "/index.html"
            }
          ]
        }
      }
      
      
      6- Build l'applicazione
      ng build
      
      7- Fare il deploy su Firebase
      firebase deploy
      
      8- L'applicazione è deploiata
      https://<my-firebase-app>.web.app/
      
      9- Creare uno custom script in package.json 
      "scripts": { 
         "deploy:firebase": "ng build  && firebase --deploy" 
      } 
      
      10- Ogni volta che si vuole deployare su Github Pages 
      npm run deploy:firebase
      

 

API end-point lato-client

  • Configurare proxy
    • 1- Creare il proxy.conf.js
      -- proxy.conf.js
      const PROXY_CONFIG = [
         //aggiungere una voce per ogni controller con API che si vogliono raggiungere tramite URL
         contest: [
           "/api"
           "/weatherforecast",
           "/makes",
        ]
      ]
      
      oppure
      
      1B - Creare il file proxy.conf.json
      -- proxy.conf.json
      {
          "/api": {
              "target": "https://localhost:7147/",
              "secure": false
          }
      }
      
      2- Fare in modo che l'applicazione prenda in considerazione il file proxy.conf.js (oppure proxy.conf.json)
      -- angular.json
      "projects":
        "Vega":
          "serve":
           "development":
             "proxyConfig: "proxy.conf.js" (oppure "proxy.conf.json")
      oppure
      ng serve --proxy-config proxy.conf.js (oppure "proxy.conf.json")
      
  •  src/app/services
    • //usare @Inject('BASE_URL') per l'url di base (es: https://localhost:7147) 
      import { Injectable, Inject } from '@angular/core';
      import { HttpClient } from '@angular/common/http';
      import { MakeResource } from '../resources/make-resource';
      
      @Injectable({
        providedIn: 'root'
      })
      export class MakeService {
        public makes: MakeResource[] = [];
      
        constructor(private http: HttpClient,  @Inject('BASE_URL') private baseUrl: string) { 
        }
      
        getMakes() {
          //GET https://localhost:7147/makes
          return this.http.get<MakeResource[]>(this.baseUrl + 'makes');
        }
      }

 

Risorse

  • Bootstrap componens
  • C# To TypeScript
    • In Visual Studio Code installare l’estensione aggiuntiva ‘C# To TypeScript’
    • Prendere le classi C# che si voglio copiare in formato TypeScript (per la nostra applicazione Angular)
    • Aprire il componente in Angular
    • Fare F1 => cercare ‘C# to TypeScript (Past As)’  => dare l’ok, le classi verranno copiate come desisderato 
  • Simulare la presenza di servizi lato server
  • JSon web tockens
  • Firebase
  • StackBlitz
    • Boot a fresh environment in a few seconds
  • https://www.npmjs.com/
    • Search packages