Skip to content

Instantly share code, notes, and snippets.

@enricosoft
Last active October 23, 2017 16:29
Show Gist options
  • Save enricosoft/e8515247622ecf590a72be491c67c6ce to your computer and use it in GitHub Desktop.
Save enricosoft/e8515247622ecf590a72be491c67c6ce to your computer and use it in GitHub Desktop.
Angular2 - Tutorial Avanzato

** ANIMATION **

Per poter utilizzare le animazioni è necessario importare il modulo "BrowserAnimationsModule" in "app.module.ts"

import { BrowserAnimationsModule } from '@angular/platform-browser/animations';

e poi le relative parti necessarie nel component:

import {
  trigger,
  state,
  style,
  animate,
  transition
} from '@angular/animations';

Con questi puoi definire un "animation trigger".

Facciamo un esempio: in questo esempio gestiremo 2 stati: "active" e "inactive" associati al nostro oggetto tramite una variabile "heroState" Quando hero è active l'elemento sarà leggermente più largo e di un colore più chiaro.

@Component({
  selector: 'hero-list-basic',
  template: `
    <ul>
      <li *ngFor="let hero of heroes"
          [@heroState]="hero.state"
          (click)="hero.toggleState()">
        {{hero.name}}
      </li>
    </ul>
  `,
  styleUrls: ['./hero-list.component.css'],
  animations: [
    trigger('heroState', [
      state('inactive', style({
        backgroundColor: '#eee',
        transform: 'scale(1)'
      })),
      state('active',   style({
        backgroundColor: '#cfd8dc',
        transform: 'scale(1.1)'
      })),
      transition('inactive => active', animate('100ms ease-in')),
      transition('active => inactive', animate('100ms ease-out'))
    ])
  ]
})

** DIRECTIVE **

Di seguito un esempio dove andiamo a fare l'highlight del testo quando il mouse è IN e viceversa:

  1. CODICE HTML DA AGGIUNGERE IN PAGINA

Highlight me!

  1. CREARE UN FILE highlight.directive.ts
import { Directive, ElementRef, HostListener, Input } from '@angular/core';  // HostListener è appunto il modulo che mi consente di gestire il mouse In e Out

@Directive({ selector: '[myHighlight]' })
export class HighlightDirective {
    constructor(private el: ElementRef) {}
	
	@HostListener('mouseenter') onMouseEnter() {
    this.highlight('yellow');
  }

  @HostListener('mouseleave') onMouseLeave() {
    this.highlight(null);
  }

  private highlight(color: string) {
    this.el.nativeElement.style.backgroundColor = color;
  }
}

** BROWSER SUPPORT **

IE9+ EDGE 13+ SAFARI 7+ IOS 7+ FIREFOX latest ANDROID 4.1+

In realtà Angular2 è sviluppato sui browser più moderni e quindi di base gira sulle versioni più recenti dei browser. Per abilitare anche quelli vecchi bisogna includere dei polyfills

<script src="node_modules/core-js/client/shim.min.js"></script>

** COMPONENT STYLES **

Selettori speciali:

  1. :host La pseudo-class :host serve per stilare l'elemento contenitore (host) del nostro componente (in pratica il suo parent).
:host(.active) {
  border-width: 3px;
}
  1. :host-context La pseudo class :host-context ha la stessa funzionalità di :host ma può essere utilizzata con altri selettori
:host-context(.theme-light) h2 {
  background-color: #eef;
}
  1. /deep/ Gli stili di un componente in genere vengono applicati solo l'html del componente stesso. E' possibile propagare gli stili anche a tutto l'albero dei figli
:host /deep/ h3 {
  font-style: italic;
}

** DEPLOYMENT **

  1. Strada Semplice La strada più facile è quella di copiare pari pari l'ambiente di sviluppo nel web server (non consigliata perchè nel codice di sviluppo ci sono un sacco di file non compressi e svincolati tra di loro e quindi anche le richieste al webserver sono tantissime. Se pensi a tanti utenti contemporanei potrebbero esserci problemi.) In questo caso il webserver eseguirà index.html che si troverà in www.mysite.com/my/app/ e quindi sarà necessario settare il base href a . Sarà necessario anche configurare il webserver per fare redirect di file non trovati sempre verso index.html. Per esempio:

IIS (web.config)

<system.webServer>
  <rewrite>
    <rules>
      <rule name="Angular Routes" stopProcessing="true">
        <match url=".*" />
        <conditions logicalGrouping="MatchAll">
          <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
          <add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
        </conditions>
        <action type="Rewrite" url="/src/" />
      </rule>
    </rules>
  </rewrite>
</system.webServer>

APACHE (.htaccess)

RewriteEngine On
# If an existing asset or directory is requested go to it as it is
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} -f [OR]
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} -d
RewriteRule ^ - [L]

# If the requested resource doesn't exist, use index.html
RewriteRule ^ /index.html

NGINX (try_files)

try_files $uri $uri/ /index.html;
  1. Stada Complessa Una maniera sicuramente più efficiente\ottimizzata è utilizzare WebPack.js (https://webpack.js.org/) che in pratica lavora gli assets (js, css, images) e li trasforma in bundles. WebPack.js contiene anche un plugin per la compilazione AOT che precompila tutto (e non JIT, che compila sul momento). Tutta la configurazione in genere la trovi nel file "webpack.config.js".

Le ottimizziazioni che propone webpack.js sono le seguenti:

  1. Ahead-of-Time (AOT) Compilation: precompila tutto il codice.
  2. Bundling: concatena i moduli in un singolo file (bundle).
  3. Inlining: porta i vari template html esterni e relativi css dentro i componenti.
  4. Minification: rimuove spazi bianchi in eccesso, commenti ed altre cose inutili.
  5. Uglification: riscrive il codice cercando di minificare il tutto e riscrivendo nomi variabili e funzioni.
  6. Dead code elimination: rimuove i moduli non referenziati e codice non utilizzato.
  7. Pruned libraries: rimuove le librerie non utilizzate dagli import.
  8. Performance measurement: focalizzato sulle ottimizzazioni che possono fare la differenza a livello di performance.

Ovviamente puoi usare qualsiasi build system diverso da webpack (per esempio Grunt.js). L'unica indicazione che danno è che il processo di build deve essere fatto eseguendo un'unica azione.

Nel caso di webpack.js basterà richiamare "ng build"


** REACTIVE FORMS **

Reactive forms è una tecnica Angular per creare dei form in stile reactiveJs.

INTRO Angular offre 2 tipi di tecnologie per costruire form: "reactive forms" e "template-driven forms" (tramite la libreria @angular/forms che contiene 2 moduli: "ReactiveFormsModule" e "FormsModule" ).

REACTIVE FORMS Reactive style si basa su un "non-UI data model" (tipicamente recuperato dal server) e una "UI-oriented form model" che visualizza gli stati ed i valori sullo schermo. La differenza principale tra ReactForm e "Template-driven" è che il primo è SINCRONO mentre i secondi sono ASINCRONI. Con ReactForm il componente garantisce che il data-model non cambi (lo tratta come se fossero dati statici stampati nell'html). Invece di aggiornare i dati direttamente, il componente estrae le modifiche fatte dell'utente e le spara ad un servizio esterno che le spara al server che poi ritornerà un nuovo data-model da far visualizzare al nostro "ReactForm". In pratica a livello di funzionamento è simible ai WebForms di ASP.NET, dove i dati vengono manipolati solo server side.

TEMPLATE-DRIVEN FORMS Questo è il caso classico di form dove metti dei form controls (come <input> o <select>) nel template html del componente e fai il bind al data-model tramite la direttiva ngModel. Non devi fare push\pull dei dati perchè è Angular a gestire il tutto in automatico tramite il ngModel. Angular aggiorna il mutable data-model con le modifiche fatte dall'utente.

QUAL E' IL MIGLIORE? Sono 2 tecniche diverse di sviluppare una stessa cosa, ognuna con vantaggi\svantaggi. Diciamo che dipende dal caso specifico.


** LIFECYCLE HOOKS **

Constructor

ngOnChanges --> it detects changes to input properties of the component

ngOnInit --> Initialize the directive/component after Angular first displays the data-bound properties and sets the directive/component's input properties.

ngDoCheck --> detect and act upon changes that Angular doesn't catch on its own

// QUESTI CICLANO DI CONTINUO FINO A CHE NON VIENE RICHIAMATO L' ONDESTROY
ngAfterContentInit --> Respond after Angular projects external content into the component's view.
ngAfterContentChecked --> Respond after Angular checks the content projected into the component.
ngAfterViewinit --> Respond after Angular initializes the component's views and child views.
ngAfterViewChecked --> espond after Angular checks the component's views and child views.

ngOnDestroy --> Cleanup just before Angular destroys the directive/component.

Per verificare quando l'elemento è inizializzato o distrutto bisogna mettersi in ascolto su questi 2 eventi: OnInit and OnDestroy.

L'evento ngOnInit() è il posto giusto per fare il fetch iniziale dei dati (invece di farlo nel constructor che non è consigliato).

Qual è l'evento giusto che corrisponde al DOM-READY di jQuery? (in pratica quando il render della UI è completato)

Alcuni dicono che l'evento giusto sia "ngOnInit" altri invece "ngAfterViewInit" ma alla fine dei giochi sembra che la seconda sia quella giusta. Attenzione però perchè l'evento viene richiamato varie volte e non one-time!


** NGMODULES **

Gli NgModules ti aiutano a organizzare le applicazioni in blocchi suddivisi per funzionalità, per esempio molte libreria Angular non sono altro che moduli (FormsModule, HttpModule, RouterModule, ecc). Un modulo è una classe typescript che utilizza il decoratore @NgModule. In ogni applicazione Angular c'è un modulo "root" che per convenzione viene chiamato "AppModule".


** PIPES **

Di seguito un esempio su come parametrizzare un pipe:

<p>The hero's birthday is {{ birthday | date:"MM/dd/yy" }} </p>

oppure:

<p>The hero's birthday is {{ birthday | date:format }}</p>
<button (click)="toggleFormat()">Toggle Format</button>

export class HeroBirthday2Component {
  birthday = new Date(1988, 3, 15); // April 15, 1988
  toggle = true; // start with true == shortDate

  get format()   { return this.toggle ? 'shortDate' : 'fullDate'; }
  toggleFormat() { this.toggle = !this.toggle; }
}

Si può fare anche il chaining di più pipes:

{{ birthday | date | uppercase}}

CREARE UN CUSTOM PIPES

import { Pipe, PipeTransform } from '@angular/core';

@Pipe({name: 'exponentialStrength'})

export class ExponentialStrengthPipe implements PipeTransform {
  transform(value: number, exponent: string): number {
    let exp = parseFloat(exponent);
    return Math.pow(value, isNaN(exp) ? 1 : exp);
  }
}

// Esempio di utilizzo:  {{ 2 |  exponentialStrength:10}}

ELENCO DI PIPES GIA' ESISTENTI IN NG2:

  • CurrencyPipe {{a | currency:'USD':true:'4.2-2'}} //currencyCode : symbolDisplay : digitInfo

  • JsonPipe {{object | json}}

  • LowerCasePipe {{value | lowercase}}

  • PercentPipe {{b | percent:'4.3-5'}}

  • SlicePipe {{collection | slice:1:3}} // array_or_string_expression | slice:start[:end]

  • DatePipe {{ dateObj | date:'shortTime' }} // date_expression | date[:format]

  • I18nPluralPipe

  • TitleCasePipe

  • DecimalPipe {{pi | number:'3.5-5'}} // number_expression | number[:digitInfo]

  • UpperCasePipe


** SECURITY: SANITIZE HTML **

  1. Sanitization Sanitization si occupa di fare l'inspect di codice considerato non sicuro (per esempio dell'html con degli script), trasformandolo in un codice sicuro pronto per essere inserito nel DOM. A questo codice viene fatto l'escape dell'html e quindi tutti i vari simboli dei tag vengono considerati come testo e non come html\script\css.

Esempio:

<p class="e2e-inner-html-bound" [innerHTML]="htmlSnippet"></p>

Per marcare un codice come "fidati", devi fare l'inject del "DomSanitizer" e chiamare uno dei seguenti metodi:

  • bypassSecurityTrustHtml
  • bypassSecurityTrustScript
  • bypassSecurityTrustStyle
  • bypassSecurityTrustUrl
  • bypassSecurityTrustResourceUrl

** NG DIRECTIVE DI BASE **

  1. *ngIf --> *ngIf="hero" (se utilizzato in una direttiva "template" va scritto così: [ngIf]="hero")

  2. *ngFor --> *ngFor="let hero of heroes"

  3. [ngSwitch] -->

<div [ngSwitch]="hero?.emotion">
  <happy-hero    *ngSwitchCase="'happy'"    [hero]="hero"></happy-hero>
  <sad-hero      *ngSwitchCase="'sad'"      [hero]="hero"></sad-hero>
  <confused-hero *ngSwitchCase="'confused'" [hero]="hero"></confused-hero>
  <unknown-hero  *ngSwitchDefault           [hero]="hero"></unknown-hero>
</div>

** TEST **

Puoi scrivere ed eseguire gli Angular Tests tramite vari tools:

  • Jasmine (The Jasmine test framework provides everything needed to write basic tests. It ships with an HTML test runner that executes tests in the browser.)

  • Karma (The karma test runner is ideal for writing and running unit tests while developing the application. It can be an integral part of the project's development and continuous integration processes.)

  • Protractor (Use protractor to write and run end-to-end (e2e) tests. End-to-end tests explore the application as users experience it. In e2e testing, one process runs the real application and a second process runs protractor tests that simulate user behavior and assert that the application respond in the browser as expected.)

** Karma Example **

  1. Configura Karma come prefersci (karma.conf.js)

  2. Crea un nuovo file "1st.spec.ts" nel percorso src/app/

  3. Scrivi il test:

describe('1st tests', () => {
  it('true is true', () => expect(true).toBe(true));
});
  1. npm test

Per testare un componente specifico invece basterà:

  1. Crea un nuovo file "banner-inline.component.spec.ts" nel percorso src/app/

  2. import { ComponentFixture, TestBed } from '@angular/core/testing';

  3. Utilizzare "TestBed" per simulare la creazione del componente Esempio:

describe('BannerComponent (inline template)', () => {

  let comp:    BannerComponent;
  let fixture: ComponentFixture<BannerComponent>;
  let de:      DebugElement;
  let el:      HTMLElement;

  beforeEach(() => {
    TestBed.configureTestingModule({
      declarations: [ BannerComponent ], // declare the test component
    });

    fixture = TestBed.createComponent(BannerComponent);

    comp = fixture.componentInstance; // BannerComponent test instance

    // query for the title <h1> by CSS element selector
    de = fixture.debugElement.query(By.css('h1'));
    el = de.nativeElement;
  });
});

TestBed è un utility di Angular che si occupa di creare un ambiente di test (configurabile tramite "configureTestingModule") per far girare un componente. In effetti lui fa il detach del componente che vogliamo testare dal suo modulo reale, esegue i test e poi la il re-attach una volta finiti i test.


** TYPESCRIPT CONFIGURATION **

TypeScript è il linguaggio primario per lo sviluppo in Angular2. Si tratta di un superset di Javascript orientato agli Oggetti (OOP) che ci da notevoli vantaggi a livello di ordine del codice, individuazione di bug. Typescript non viene eseguito dal browser ma va compilato (tramite " tsc compiler") ed alla fine risulterà javascript.

In un progetto Angular2 troveremo 2 file principali:

  1. tsconfig.json ---> TypeScript compiler configuration (qui trovi dei settings)

  2. typings ----> TypeScript declaration files. Molte librerie javascript (come jQuery, Jasmine, ecc) sono praticamente delle estensioni dello stesso Javascript. Il compiler Typescript non le conosce ovviamente e l'unico modo per indicare al compiler che esistono e che non deve emettere errori è aggiungerle ai "typings". In pratica nel file "lib.d.ts" andrai ad includere tutte queste librerie.


** ANGULAR CLI **

npm install -g @angular/cli npm uninstall -g angular-cli

ng help

ng new PROJECT-NAME

ng serve --host 0.0.0.0 --port 4201

ng g component|directive|pipe|service|class|guard|interface|enum|module itsFileName

ng build -w (fa la build con il watch)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment