Sei sulla pagina 1di 34

 Formazione  About Me 

Angular: introduzione al framework, consigli, best practices

In questo articolo, descrivo alcune delle funzionalità principali incluse nella release 5 di Angular
(UPDATE: l’articolo è stato aggiornato alla versione 6), uno dei framework più utilizzati nella
creazione di Single-Page Applications (SPA).

Con il termine Single-page application si intende un’applicazione web o un sito


web che può essere usato o consultato su una singola pagina web con l’obiettivo di
fornire una esperienza utente più fluida e simile alle applicazioni desktop dei
sistemi operativi tradizionali. Wikipedia

Gli argomenti principali trattati nell’articolo sono i seguenti:

Creazione progetti Angular tramite l’utilizzo di angular-cli


Utilizzo delle direttive incluse nel framework
Dynamic Styling
Template driven form e validators
Creazione server mock API REST
Comunicazione con il server tramite API REST

Il risultato finale dell’applicazione sarà un elenco di smartphone con un form per la gestione di
tutte le operazioni CRUD:
lettura (GET), inserimento (POST), cancellazione (DELETE) e modifica degli elementi
(PUT/PATCH).

Revoca cookie
NodeJS e NPM

Per installare gli strumenti necessari a svolgere questo tutorial è indispensabile e ettuare il
download di NodeJS, installarlo e verificare la possibilità di utilizzare il comando npm (Node
Package Manager) da terminale, che è incluso nell’installer di Node.

TIP: Un’alternativa all’installazione “tradizionale” di Node è l’utilizzo di NVM (Node


Version Manager) grazie al quale è possibile utilizzare diverse versioni di Node nella
stessa macchina. Molto utile se avete un progetto che richiede una versione
precedente di Node rispetto a quella attuale o se volete creare ambienti separati in
cui installare versioni di erenti dei tool e/o provare diverse configurazioni.
Disponibile per Windows e Mac (anche tramite HomeBrew)

Angular: creazione progetto


Revoca cookie
Il modo più semplice per configurare un progetto Angular è senza dubbio l’utilizzo di angular-cli,
un’altro strumento utilizzabile da riga di comando.

1. npm install -g @angular/cli

Dopo aver atteso qualche secondo / minuto sarà quindi possibile generare un progetto Angular
utilizzando semplicemente il modulo ng, disponibile dopo l’installazione della CLI (Command
Line Interface).

In Windows è spesso necessario chiudere il terminale e riaprirlo a inché possiate


utilizzare gli strumenti installati. Nel caso non fossero disponibili verificare di avere
i permessi di amministrazione del sistema dato che questi strumenti modificando
le variabili di ambiente. In caso negativo, provate ad aprire il terminale da
amministratore e installate nuovamente il pacchetto

1. ng new my-project

L’operazione richiederà qualche minuto a seconda della velocità della propria


connessione internet

Il risultato sarà la creazione di una struttura simile alla seguente (potrebbe variare leggermente
in base alla versione di angular-cli che state utilizzando):

Revoca cookie
Una volta terminato il processo, il progetto potrà essere avviato utilizzando il comando npm
start

1. cd my-project

2. npm start #oppure ng serve

Aprendo il browser e visitando l’url http://localhost:4200 sarà visibile un’applicazione


demo con un semplice “hello world”.

Installare Bootstrap e Font-Awesome


Revoca cookie
Installiamo ora Bootstrap 4 (framework CSS) e Font-Awesome (set di icone) allo scopo di
migliorare il look&feel della nostra demo con estrema facilità.
Ci tengo a precisare che utilizzeremo solo il CSS di Bootstrap e non i componenti
Javascript/jQuery forniti da Bootstrap. Esiste infatti una versione di Bootstrap creata
appositamente per Angular ma questo argomento non sarà trattato in questo articolo.

Naturalmente potrete utilizzare qualunque altro framework CSS o creare il proprio


CSS da zero

Installiamo quindi i due pacchetti utilizzando npm:

1. npm install bootstrap@4.0.0 font-awesome@4.7.0 --save

L’opzione --save non è più necessaria dalla versione 5 di npm perché abilitata di
default

O la più concisa:

1. npm i bootstrap@4.0.0 font-awesome@4.7.0

Ho specificato esplicitamente la versione delle librerie da utilizzare (@x.x.x) per far


in modo che il tutorial sia utilizzabile anche in futuro, quando saranno disponibili
nuove versioni delle librerie.

Apriamo ora il file angular-cli.json, individuiamo il nodo styles e aggiungiamo i path ai


css delle due librerie appena installate in modo tale da renderle accessibili globalmente.

1 "styles": [
2 "../node_modules/bootstrap/dist/css/bootstrap.min.css",
3 "../node_modules/font-awesome/css/font-awesome.min.css",
Revoca cookie
4 "styles.css"
5 ],

UPDATE: In angular 6, il nome del file di configurazione della CLI è cambiato in


angular.json e il path alla cartella node_modules non necessità più del ../ ma
sarà semplicemente node_modules/bootstrap/ecc

Per far in modo che la modifica abbia e etto è necessario “killare” il processo npm start
avviato in precedenza (utilizzando ad esempio CTRL + C o CMD + C) e riavviarlo con npm start.

TIP: Angular-cli utilizza WebPack dietro le quinte, un mix tra un automation tool e
un module bundler, allo scopo di compilare il progetto, generare le build, gestire
CSS/SASS e molto altro. Il file angular-cli.json permette di configurare alcune
funzionalità di WebPack senza tuttavia dover necessariamente conoscere lo
strumento. Maggiori informazioni sul repository GitHub di angular-cli

Creare un server Mock per le API REST

Esistono diversi strumenti in grado di creare un set di servizi (mock) REST in pochi minuti ma uno
dei più apprezzati è senza dubbio json-server.

Il funzionamento è molto semplice. Si crea un file .json, si avvia json-server da riga di comando e
in pochi secondi saranno disponibili delle API REST per la manipolazione del JSON:

GET: per recuperare le informazioni dal JSON


POST: per inserire contenuti
PUT / PATCH: per aggiornare parzialmente o in toto un elemento
DELETE: per rimuovere un elemento

Sarà quindi possibile installare json-server globalmente utilizzando la riga di comando:

1. npm install -g json-server

oppure come dipendenza di un eventuale progetto che state sviluppando:


Revoca cookie
1. npm install --save-dev json-server

TIP: utilizzo -save-dev a inché il pacchetto sia disponibile tra le


devDependencies e non sia quindi inserito nel bundle finale del progetto (ovvero
la versione da distribuire)

Dopo aver installato il pacchetto sarà su iciente creare un file, ad esempio db.json, e inserire
all’interno di questo file una struttura JSON. Per il nostro esempio creeremo una lista di devices:

1 {
2 "devices": [
3 {
4 "label": "One Plus 8",
5 "os": "android",
6 "price": 750,
7 "rate": 3,
8 "memory": 7400,
9 "desc": "One Plus 5 is a flagship phone...",
10 "id": 2
11 },
12 {
13 "label": "IPhone 7 +",
14 "os": "ios",
15 "price": 700,
16 "rate": 3,
17 "memory": 3000,
18 "desc": "",
19 "id": 24
20 }
21 ],
22 "login": {
23 "token": "efoiwejo32r32-fake-token"
24 }
25 }

Per avviare il server sarà quindi su iciente posizionarsi da terminale nella cartella in cui si è
creato il file db.json e avviare digitare la seguente istruzione:

1. json-server --watch db.json

Revoca cookie
Ora avremo a disposizione un set di API per manipolare la nostra collezione di devices.

Sarà infatti possibile e ettuare operazioni in POST, GET, PUT, PATCH e DELETE semplicemente
invocando l’endpoint generato dal server, in questo caso:

1. http://localhost:3000/devices

2. http://localhost:3000/login

Angular: comunicazione con il server utilizzando HttpClient

Carichiamo ora una collezione dati dal server mock creato con json-server.

La cartella /src contiene il file app.component.ts che rappresenterà il nostro entry-point,


ovvero il componente principale della nostra applicazione.

Apriamo il file e modifichiamo il contenuto come segue:

1 // app.component.ts
2 import { Component } from '@angular/core';
3 import { HttpClient } from '@angular/common/http';
4
5 @Component({
6 selector: 'app-root',
7 templateUrl: './app.component.html',
8 styleUrls: ['./app.component.css']
9 })
10 export class AppComponent {
11
12 constructor(private http: HttpClient) {
13 this.getAll();
14 }
15
Revoca cookie
16 getAll() {
17 this.http.get('http://localhost:3000/devices')
18 .subscribe(result => console.log(result));
19 }
20 }

A questo punto avremo due problemi:

1) Se non avete avviate il server JSON, come descritto in precedenza, non avrete a disposizione
l’end-point dal quale recuperare la collezione dati.
Il mio consiglio è quello di creare una cartella server nella root del progetto (quindi fuori dalla
cartella /src) e di avviare il comando da terminale : json-server --watch
server/db.json.

TIP: è possibile aggiungere un azione al nodo scripts all’interno del file


package.json per automatizzare il processo a avviarlo semplicemente con
l’istruzione: npm run server
"server": "json-server --watch server/db.json"

2) Il secondo problema è rappresentato dall’utilizzo di HttpClient, un servizio incluso in


Angular per la comunicazione con il server, senza aver tuttavia importato il relativo modulo.

TIP: Angular è suddiviso in di erenti moduli, ovvero delle collezioni di componenti,


direttive, servizi (e molto altro) “pre-confezionati” che potete utilizzare nelle vostre
applicazioni: FormsModule per i form, HttpClient per la comunicazione con il
server, BrowserModule per le direttive base e così via

E’ quindi necessario aprire il file src/app.module e importare HttpClientModule. Visto che


ci siamo, importiamo anche FormsModule che ci servirà in seguito per la gestione dei form 😉

1 // app.module.ts
2 import { BrowserModule } from '@angular/platform-browser';
3 import { NgModule } from '@angular/core';
4
5 import { AppComponent } from './app.component';
6 import { HttpClientModule } from '@angular/common/http';
7 import { FormsModule } from '@angular/forms';
8 Revoca cookie
9 @NgModule({
10 declarations: [
11 AppComponent
12 ],
13 imports: [
14 BrowserModule, HttpClientModule, FormsModule
15 ],
16 providers: [],
17 bootstrap: [AppComponent]
18 })
19 export class AppModule { }

Salviamo tutti i files, avviate il browser e navigate all’indirizzo http://localhost:4200. Aprite i Dev
Tools (se usate Chrome: F12 su windows oppure CMD + OPTIONS + i su Mac) e dovreste vedere il
risultato nella console dei dev tools:

Model e tipizzazione

Per sfruttare le potenzialità del linguaggio Typescript, alla base del framework Angular, possiamo
creare un custom type che rappresenti i nostri device.
Sarà utile per tutta una serie di motivi: abilitare intellisense e autocompletamento negli
editor/IDE, ricevere errori dal compilatore qualora utilizzassimo delle proprietà errate,
documentare il codice, solo per citarne alcuni.

Creiamo il file device.ts in una nuova cartella src/model/

1 // src/model/device.ts
2 export interface Device {
3 id?: number;
4 label?: string;
5 os?: string;
6 price?: number;
7 memory?: number;
8 rate?: number;
9 desc?: string;
10 }
Revoca cookie
TIP: il punto di domanda (?) indica che la proprietà sarà facoltativa. Sarebbe
preferibile evitarlo in un contesto reale ma, ai fini didattici, se ipotizziamo di non
gestire tutte le proprietà di un device, come in questo scenario, è preferibile
utilizzare questo approccio per evitare errori del compiler.

Modifichiamo ora il file app.component.ts allo scopo di:

1) utilizzare il nuovo tipo Device


2) salvare l’intera collezione dei dati ricevuti dal server in un array di Device

1 // src/app.component.ts
2 import { Component } from '@angular/core';
3 import { HttpClient } from '@angular/common/http';
4 import { Device } from './model/device';
5
6 @Component({
7 selector: 'app-root',
8 templateUrl: './app.component.html',
9 styleUrls: ['./app.component.css']
10 })
11 export class AppComponent {
12 devices: Device[];
13
14 constructor(private http: HttpClient) {
15 this.getAll();
16 }
17
18 getAll() {
19 this.http.get<Device[]>('http://localhost:3000/devices')
20 .subscribe(result => this.devices = result);
21 }
22 }

Tramite l’istruzione this.http.get< Device[] > specifichiamo che il risultato


ottenuto dal server sarà di tipo Device: una sorta di casting del risultato. Non è
utile in questo scenario ma in contesti reali è pressoché indispensabile se vogliamo
evitare errori del compilatore qualora provassimo ad accedere al contenuto di una
proprietà utilizzando ad esempio l’istruzione devices[0].anyProperty.

Template: visualizzare una collezione di dati Revoca cookie


Per visualizzare un elenco di elementi, possiamo utilizzare la direttiva ngFor fornita da Angular
(modulo BrowserModule).
Questa direttiva permette di e ettuare un ciclo su una collezione dati e di renderizzare ogni
elemento tramite un template HTML.

TIP: le direttive sono dei “componenti speciali” che possono essere applicati al
DOM o ad altri componenti. Angular include un set di direttive molto utili per
manipolare il dom (ngIf, ngClass, ecc.) ma potete chiaramente creare direttive
personalizzate

Apriamo quindi il file src/app.component.html, cancelliamo il contenuto hello world e


inseriamo il seguente codice:

1 <li *ngFor="let device of devices">{{device.label}}</li>

che produrrà il seguente risultato:

TIP: si utilizzano le parentesi gra e, come in {{device.label}}, per eseguire e


processare istruzioni Javascript all’interno del template HTML. In questo caso
visualizziamo la label di un singolo device. Nel caso inserissimo al loro interno, ad
esempio, un’operazione matematica, ad es. {{1+1}}, avremmo come output il
risultato: 2

Possiamo ora completare il template visualizzando altre proprietà del device e utilizzando
alcune delle classi CSS messe a disposizione da Bootstrap e FontAwesome.

1 <div class="card bg-dark text-white mb-3">


2
3 <!--Devices List -->
4 <div *ngFor="let device of devices"
5 class="list-group-item list-group-item-action"> Revoca cookie
6
7 <!--os icon-->
8 <i class="fa"
9 [ngClass]="{
10 'fa-android': device.os === 'android',
11 'fa-apple' : device.os === 'ios',
12 'fa-tablet' : device.os === 'others'
13 }"
14 ></i>
15
16
17 <!--label-->
18 <i class="fa fa-in"></i>
19 <span>{{device?.label}}</span>
20
21 <!--display rate-->
22 <!--...missing...-->
23
24 <div class="pull-right">
25 <!--price -->
26 <strong *ngIf="device.price"
27 [style.color]="device.price > 500 ? 'red' : null">
28 € {{device.price | number: '1.2-2'}}
29 </strong>
30
31 <!--trash icon-->
32 <i class="fa fa-trash icon"></i>
33 </div>
34 </div>
35 </div>

Revoca cookie
Cancellazione e selezione di un elemento

Innanzitutto aggiungiamo i metodi setActive e delete al nostro componente.


Il primo sarà invocato al click di ogni elemento della lista, allo scopo di selezionarlo.
Il secondo, invece, al click dell’icona “trash”, il cestino.

1 // src/app.component.ts
2 import { Component } from '@angular/core';
3 import { HttpClient } from '@angular/common/http';
4 import { Device } from './model/device';
5
6 @Component({
7 selector: 'app-root',
8 templateUrl: './app.component.html',
9 styleUrls: ['./app.component.css']
10 })
11 export class AppComponent {
12 devices: Device[];
13 active: Device = {};
14
15 constructor(private http: HttpClient) {
16 // console.log ('environment:', env);
17 this.getAll();
18 }
19
20 getAll() {
21 this.http.get<Device[]>('http://localhost:3000/devices')
22 .subscribe(result => this.devices = result);
23 }
24
25 setActive(device: Device) {
26 this.active = device;
27 }
28
29
30 delete(event: MouseEvent, device: Device) {
31 event.stopPropagation()
32 this.http.delete<any>(`http://localhost:3000/devices/${device.i
33 .subscribe(
34 () => {
35 const index = this.devices.indexOf(device)
36 this.devices.splice(index, 1);
37 }
38 );
39 }
40
41 }
Revoca cookie
Il metodo setActive salva una reference dell’elemento in una proprietà active
che sarà successivamente utilizzata per popolare il form ed e ettuare la modifica
delle proprietà del device selezionato

Modifichiamo quindi il template per invocare i due metodi appena creati e per visualizzare
l’elemento selezionato proprio sopra la lista (in cui successivamente posizioneremo il form).
Utilizziamo inoltre la direttiva ngClass per evidenziare l’elemento selezionato.

1 <pre>{{active | json}}</pre>
2
3 <div class="card bg-dark text-white mb-3">
4
5 <!--Devices List -->
6 <div *ngFor="let device of devices"
7 class="list-group-item list-group-item-action"
8 [ngClass]="{'bg-warning text-dark': device.id === active?.id
9 (click)="setActive(device)">
10
11 <!--os icon-->
12 <i class="fa"
13 [ngClass]="{
14 'fa-android': device.os === 'android',
15 'fa-apple' : device.os === 'ios',
16 'fa-tablet' : device.os === 'others'
17 }"
18 ></i>
19
20
21 <!--label-->
22 <i class="fa fa-in"></i>
23 <span>{{device?.label}}</span>
24
25 <!--display rate-->
26 <!--...missing...-->
27
28 <div class="pull-right">
29 <!--price -->
30 <strong *ngIf="device.price"
31 [style.color]="device.price > 500 ? 'red' : null">
32 € {{device.price | number: '1.2-2'}}
33 </strong>
34
35 <!--trash icon-->
36 <i class="fa fa-trash icon"
37 (click)="delete($event, device)"></i>
38 </div> Revoca cookie
39 </div>
40 </div>

TIP: l’istruzione json utilizzata in {{active | json}} permette di visualizzare il


contenuto di una collezione dati all’interno di un template ed è chiamata “pipe”
(ex filter in AngularJS)

Il risultato sarà il seguente:

Come potete notare notato che al metodo delete passo la proprietà $event,
ovvero l’evento del mouse generato dal click dell’utente. In questo modo è
possibile invocare l’istruzione event.stopPropagation() per evitare che anche
il metodo setActive sia invocato al click sull’icona “trash”. Maggiori info sulla
documentazione MDN

Revoca cookie
Angular Form

Una delle funzionalità più amate di Angular è la gestione dei form, superiore per quantità di
feature e potenzialità a qualunque altra libreria o framework attualmente disponibile sul
mercato (e cito ad es. React o Vue).

Angular fornisce due soluzioni per la gestione dei form:

Template driven form: utilizzati in questo articolo


Reactive form: un approccio che sfrutta il paradigma della programmazione funzionale,
davvero molto potente e flessibile (utilizzando RxJS, incluso come dipendenza del
framework)

L’argomento “Form” è davvero molto ampio e in questo articolo analizzeremo velocemente solo
una piccola parte delle funzionalità dei template-driven form.

Iniziamo creando un semplice form proprio sopra la lista:

1 <div class="card bg-dark text-white mb-3">


2 <!--edit / add form-->
3 <form novalidate
4 (submit)="save(f)"
5 #f="ngForm"
6 class="card-body">
7
8 <input type="text"
9 [ngModel]="active?.label"
10 name="label"
11 required
12 class="form-control bg-dark text-white"
13 placeholder="Phone model *"
14 >
15
16 <div class="btn-group btn-group-sm m-1">
17 <button class="btn btn-warning"
18 type="submit"
19 [disabled]="f.invalid">
20 {{active?.id ? 'SAVE' : 'ADD'}}
21 </button>
22 <button
23 class="btn btn-light"
24 type="button"
25 *ngIf="active?.id"
26 (click)="reset()">
27 ADD NEW
Revoca cookie
28 </button>
29 </div>
30 </form>
31 </div>

Salvando il file e provando il form riceveremo degli errori perché non abbiamo ancora
implementato i metodi save() e reset().

Il form non è ancora completo ma di seguito descrivo alcune delle funzionalità utilizzate finora:

Il form sarà utilizzato sia per gestire l’inserimento di nuovi elementi, che la modifica di
quelli esistenti
Al submit del form (tramite click o pressione del pulsante INVIO della tastiera) sarà
invocato il metodo submit(f) tramite il quale passiamo anche una reference del form

L’istruzione #f viene definita “template reference variable” e rappresenta una


reference al nostro form. Tramite questa proprietà potremo conoscere in ogni
momento lo stato del form: se è valido (f.valid o f.invalid), se è già stato
utilizzato / sporcato (f.dirty), possiamo recuperare le informazioni inserite in
tutti i campi del form tramite la proprietà “value” (f.value) e molto altro. Lo
stesso meccanismo può essere utilizzato anche sul singolo campo. Si potrà quindi
sapere se un campo è valid, invalid, dirty, touched, errors e molto altro. Davvero
potente!

La direttiva ngModel permettere di sincronizzare una proprietà della classe con il


campo di input tramite l’utilizzo di un binding 1-way (al contrario di AngularJS che
utilizza il binding bidirezionale). Sostanzialmente, quando il valore della proprietà
active muta, anche il campo di input sarà aggiornato con il relativo valore. Quindi
ogni qualvolta l’utente selezionerà un elemento della lista, il contenuto della proprietà
active.label sarà visualizzata all’interno del campo di input.

TIP: è possibile abilitare il binding bidirezionale utilizzando le doppie parentesi


[(ngModel)] ma è preferibile utilizzare un approccio 1-way. I motivi sono
molteplici ma, per citarne uno, l’applicazione risulterà più semplice da mantenere
evitando di perdere il controllo del flusso dati. In questo esempio potrebbe
sembrare inutile ma nel momento in cui l’applicazione dovrà scalare, la UI sarà
Revoca cookie
suddivisa in centinaia di componenti, si gestirà la business logic in servizi o si
utilizzeranno pattern architetturali come Redux o Mobx State Tree, sarà di
fondamentale importanza. In questo tutorial non a ronteremo nessuno di questi
argomenti ma vi consiglio di abituarvi a lavorare nel modo corretto fin da subito

Il pulsante per il submit sarà disabilito fino a che tutti i campi di input del form non saranno
validi. In questo esempio abbiamo specificato che la label è un elemento required perciò
il form non sarà valido fino a che quel campo (ed eventuali altri) non sarà valido
La label del pulsante submit assumerà il valore SAVE, nel momento in cui abbiamo un
elemento selezionato, oppure ADD quando invece ne inseriremo uno nuovo
Il pulsante reset invece si occuperà di deselezionare l’elemento attivo, per permettere la
creazione di un nuovo elemento

Modifica e Inserimento elementi

Revoca cookie
Completiamo la demo aggiungendo i metodi per la gestione dell’inserimento e modifica di
elementi e integrando, nel template HTML, i campi di input e una select per la gestione di alcune
delle proprietà dei device (prezzo e sistema operativo)

app.component.js

1 import { Component } from '@angular/core';


2 import { HttpClient } from '@angular/common/http';
3 import { Device } from './model/device';
4 import { NgForm } from '@angular/forms';
5
6 const INITIAL_STATE = { label: null, os: null };
7
8 @Component({
9 selector: 'app-root',
10 templateUrl: './app.component.html',
11 styleUrls: ['./app.component.css']
12 })
13 export class AppComponent {
14 devices: Device[];
15 active: Device = INITIAL_STATE;
16
17 constructor(private http: HttpClient) {
18 // console.log ('environment:', env);
19 this.getAll();
20 }
21
22 getAll() {
23 this.http.get<Device[]>('http://localhost:3000/devices')
24 .subscribe(result => this.devices = result);
25 }
26
27 setActive(device: Device) {
28 console.log( device )
29 this.active = device;
30 }
31
32
33 delete(event: MouseEvent, device: Device) {
34 this.http.delete<any>(`http://localhost:3000/devices/${device.i
35 .subscribe(
36 () => {
37 const index = this.devices.indexOf(device)
38 this.devices.splice(index, 1);
39 }
40 );
41 }
Revoca cookie
42
43 save(form: NgForm) {
44 if (this.active.id) {
45 this.edit(form.value);
46 } else {
47 this.add(form.value);
48 form.reset();
49 }
50 }
51
52 add(device: Device) {
53 this.http.post<Device>(`http://localhost:3000/devices`, device)
54 .subscribe(res => {
55 this.devices.push(res)
56 this.reset();
57 })
58 }
59
60 edit(device: Device) {
61 const newDevice = Object.assign(
62 {},
63 this.active,
64 device
65 );
66
67 this.http.patch<Device>(`http://localhost:3000/devices/${newDev
68 .subscribe(
69 res => {
70 const index = this.devices.findIndex(device => device.id
71 this.devices[index] = newDevice;
72 }
73 );
74
75 }
76
77 reset() {
78 this.active = INITIAL_STATE;
79 }
80
81 }

app.component.html

1 <div class="card bg-dark text-white mb-3">


2 <!--edit / add form-->
3 <form novalidate
4 (submit)="save(f)"
5 #f="ngForm" Revoca cookie
6 class="card-body">
7
8 <input type="text"
9 [ngModel]="active?.label"
10 name="label"
11 required
12 class="form-control bg-dark text-white m-1"
13 placeholder="Phone model *">
14
15
16 <input type="number"
17 [ngModel]="active?.price"
18 name="price"
19 class="form-control bg-dark text-white m-1"
20 placeholder="Price *">
21
22 <select [ngModel]="active?.os"
23 name="os"
24 required
25 class="form-control bg-dark text-white m-1">
26 <option value="null">Select OS *</option>
27 <option value="ios">ios</option>
28 <option value="android">android</option>
29 <option value="others">others</option>
30 </select>
31
32
33 <div class="btn-group btn-group-sm m-1">
34 <button class="btn btn-warning"
35 type="submit"
36 [disabled]="f.invalid">
37 {{active?.id ? 'SAVE' : 'ADD'}}
38 </button>
39 <button
40 class="btn btn-light"
41 type="button"
42 *ngIf="active?.id"
43 (click)="reset()">
44 ADD NEW
45 </button>
46
47 </div>
48 </form>
49 </div>
50
51
52 <div class="card bg-dark text-white mb-3">
53
54 <!--Devices List -->
55 <div *ngFor="let device of devices"
Revoca cookie
56 class="list-group-item list-group-item-action"
57 [ngClass]="{'bg-warning text-dark': device.id === active?.id
58 (click)="setActive(device)">
59
60 <!--os icon-->
61 <i class="fa"
62 [ngClass]="{
63 'fa-android': device.os === 'android',
64 'fa-apple' : device.os === 'ios',
65 'fa-tablet' : device.os === 'others'
66 }"
67 ></i>
68
69
70 <!--label-->
71 <i class="fa fa-in"></i>
72 <span>{{device?.label}}</span>
73
74 <!--display rate-->
75 <!--...missing...-->
76
77 <div class="pull-right">
78 <!--price -->
79 <strong *ngIf="device.price"
80 [style.color]="device.price > 500 ? 'red' : null">
81 € {{device.price | number: '1.2-2'}}
82 </strong>
83
84 <!--trash icon-->
85 <i class="fa fa-trash icon"
86 (click)="delete($event, device)"></i>
87 </div>
88 </div>
89 </div>

Risultato:

In fase di editing:

Revoca cookie
In fase di inserimento:

Revoca cookie
Next Step

Questo articolo introduce solo una minima parte delle funzionalità del framework e non
vengono applicate tutta una serie di best practices e metodologie indispensabili per rendere
l’applicazione più scalabile, manutenibile e testabile.
Il framework include, infattim moltissimi altri strumenti tra i quali:

La possibilità di suddividere il codice in componenti e creare quindi custom HTML tags (ad
es. )
Suddividere la business logic in servizi utilizzando il motore di dependency injection
integrato
Organizzare il progetto in diverse route e in moduli custom
Creare custom directives e custom pipes
Gestire autenticazione (ad es. JWT o OAuth), integrare interceptor ecc. Revoca cookie
Utilizzare facilmente pattern architetturali per la gestione dello stato applicativo come
Redux o MobxState Tree, già citati in precedenza
e molto molto altro

Ad esempio, l’esercizio precedente, in un contesto reale, potrebbe essere scritto come segue, e
questo è solo uno dei possibili approcci che non utilizza neppure uno dei pattern architetturali
menzionati sopra:

1 @Component({
2 selector: 'devices-view',
3 template: `
4
5 <toggable [title]="Device Form">
6 <add-edit-form
7 [active]="store.active"
8 (reset)="actions.reset()"
9 (save)="actions.save($event)">
10 </add-edit-form>
11 </toggable>
12
13 <toggable title="DEVICES" [closable]="false">
14
15 <list-filter
16 class="header-content"
17 [filters]="filters"
18 (update)="this.filters = $event"></list-filter>
19
20 <devices-list
21 [devices]="store.devices"
22 [active]="store.active"
23 [filters]="filters"
24 (selectRow)="setActive($event)"
25 (delete)="actions.delete($event)"></devices-list>
26 </toggable>
27 `
28 })
29 export class DevicesViewComponent {
30 constructor(
31 public store: DeviceStore,
32 public actions: DeviceService,
33 ) {
34 // Load devices
35 this.actions.getAll();
36 }
37
38 }

Revoca cookie
Lo stesso discorso vale per la struttura del progetto che, al crescere della complessità,
naturalmente dovrà essere organizzato diversamente. Un esempio:

DEMO

Di seguito una demo live. Tieni presente che il server Node utilizzato è un hosting gratuito.
Potrebbe non essere velocissimo (soprattutto in fase di avvio) e non mi ritengo responsabile dei
contenuti inseriti dagli altri utenti 😉

Revoca cookie
devices.component.ts × https:/…
1 import { Component, ElementRe
2 import { Device } from '../..
3 import { HttpClient } from '@a
4 import { NgForm } from '@angul
Phone model *
5
6 const INITIAL_STATE = { label
7 Price *
8 @Component({
9 selector: 'devices-view', Select OS *
10 template: `
11 <br>
12 <div class="card bg-dark t Rate: 1 2
13 <!--edit / add form-->
14 <form novalidate
15 (submit)="save(f) description (not requir
16 #f="ngForm"
17 class="card-body">
18
ADD
19 <input type="text"
20 [ngModel]="acti
21 name="label"
22 required
23 class="form-con 1 1
Console
24 placeholder="Ph
angular5-bootstrap4-crud-device-list-simple Editor Preview Both

Visualizza lo script su StackBlitz


(nella demo ho gestito anche il “rate” del device, la descrizione e viene utilizzato inoltre Angular
Router per la creazione di applicazioni multi-view)

CONCLUSIONE

Spero che questa breve overview ti sia piaciuta.


Feedback, critiche o consigli sono ben accetti 😉

gennaio 28th, 2018 | Angular, Articoli, Javascript

Share on...     

19 Comments

Enrico 3 febbraio 2018 at 10:49 - Reply


Revoca cookie
Complimenti, veramente un ottimo tutorial per chi si sta muovendo i primi passi su
Angular. 🙂

fabiobiondi 3 febbraio 2018 at 15:51 - Reply

Grazie mille. Nel mio canale YouTube trovi anche delle playlist di video con altri
argomenti (custom components, providers, form, custom validators, ecc. ) ma non
ne vado molto fiero perché registrati in fretta, oltre al fatto che produrre video non è
il mio forte 🙂

Giannifed 3 febbraio 2018 at 14:44 - Reply

Grazie dell’articolo sempre molto utili.


Solo per segnalare che in un listato in app.component.html è presente

*ngFor=”let d of devices”

mentre dovrebbe essere *ngFor=”let device of devices” come negli altri listati.
Ringrazio

Giannifed 3 febbraio 2018 at 15:02 - Reply

anche nel listato finale

(click)=”setActive(d)

dovrebbe essere

(click)=”setActive(device)

in quanto ngFor è :

*ngFor=”let device of devices”

Ringrazio.

fabiobiondi 3 febbraio 2018 at 15:53 - Reply

Grazie mille per i due suggerimenti Gianni. Hai perfettamente ragione e ho corretto
gli errori.
Revoca cookie
Sono errori di battitura in quanto nel mio esempio live ho usato la “d” per essere
più conciso (pur essendo una bad practice) ma durante la stesura dell’articolo l’ho
sostituito con “device” dimenticando i due esempi da te citati. Gentilissimo 🙂

Alessandro Aprile 18 febbraio 2018 at 23:25 - Reply

npm e angular mi stanno dando qualche grattacapo. con angular attuale (`


npm -g list [AT]angular/cli
/usr/local/lib
└── [AT]angular/cli[AT]1.6.3
`) ho

“`
npm i bootstrap[AT]4.0.0 font-awesome[AT]4.7.0
npm WARN [AT]angular-devkit/schematics[AT]0.0.52 requires a peer of [AT]angular-
devkit/core[AT]0.0.29 but none is installed. You must install peer dependencies yourself.
npm WARN [AT]schematics/angular[AT]0.1.17 requires a peer of [AT]angular-
devkit/core[AT]0.0.29 but none is installed. You must install peer dependencies yourself.
npm WARN ajv-keywords[AT]3.1.0 requires a peer of ajv[AT]^6.0.0 but none is installed.
You must install peer dependencies yourself.
npm WARN bootstrap[AT]4.0.0 requires a peer of jquery[AT]1.9.1 – 3 but none is
installed. You must install peer dependencies yourself.
npm WARN bootstrap[AT]4.0.0 requires a peer of popper.js[AT]^1.12.9 but none is
installed. You must install peer dependencies yourself.
“`
Oltre a questo, aggiornate le dipendenze come da errore,
a `npm start`
ho
““
ERROR in ./node_modules/css-loader?
{“sourceMap”:false,”importLoaders”:1}!./node_modules/postcss-loader/lib?
{“ident”:”postcss”,”sourceMap”:false}!./node_modules/bootstrap/dist/css/bootstrap.min.css
Module build failed: BrowserslistError: Unknown browser major
“`
per risolvere ho aggiornato a `angular-cli[AT]1.7.x` come da questa issue
https://github.com/angular/angular-cli/issues/9288#issuecomment-360430754.

(NB: carattere AT sostituito per il filtro antispam) Revoca cookie


fabiobiondi 18 febbraio 2018 at 23:35 - Reply

Grazie per il feedback Alessandro.


Anch’io ho notato che ci sono dei problemi con le versioni di angular-cli 1.6.x e
Bootstrap 4, sia beta che final.
Personalmente, ho avuto anche dei problemi in fase di build.
Nel caso dovessi averli, una possibile soluzione (temporanea) è quella di usare la
versione SASS di Bootstrap 4 come segue:

– Rinominare il file styles.css in styles.scss


– In angular-cli.json importa styles.scss invece di styles.css
– in styles.scss importa la versione SASS di Bootstrap : @import
‘~bootstrap/scss/bootstrap.scss’;

Andre 22 febbraio 2018 at 11:37 - Reply

Ciao, dove posso trovare le slide che sono state mostrate all’evento angular best
practises?

fabiobiondi 15 aprile 2018 at 1:18 - Reply

Le slide sono disponibili su SlideShare.


Tuttavia, il codice, come sai, è stato scritto dal vivo e non lo troverai nelle slide.

Francesco 20 aprile 2018 at 0:21 - Reply

Fabio sei veramente forte..complimenti davvero. Grazie mille

Alessandro 1 giugno 2018 at 11:59 - Reply

Ciao Fabio, ho notato che prendi i dati nel costruttore piuttosto che all’interno della
funzione ngOnInit. A cosa è dovuta questa scelta ? Grazie 🙂

fabiobiondi 1 giugno 2018 at 22:45 - Reply

Ciao Alessandro. Per due motivi:


1) Non aggiungere ulteriori concetti all’articolo che è già abbastanza lungo 🙂
Revoca cookie
2) Non è necessario. ngOnInit viene invocato quando le proprietà in Input del
componente sono disponibili. Ma in questo specifico esempio non abbiamo
componenti, se non il componente di root, che non ha alcuna proprietà in input.
Quindi sarebbe inutile 🙂

Alessandro 7 giugno 2018 at 15:05 - Reply

Perfetto, grazie ancora. 🙂

Saldimic 21 agosto 2018 at 9:58 - Reply

Complimenti un articolo abbastanza esaustivo.

Chiara 29 agosto 2018 at 14:55 - Reply

Fantastico articolo. Molto utile!

marco 21 settembre 2018 at 8:38 - Reply

Grazie! Tutorial fatto benissimo per i primi passi con Angular. Molto utile!

Emanuele 4 ottobre 2018 at 13:07 - Reply

Fabio da poco sto iniziando ad interessarmi al mondo angular, node.js, non mi è chiara
una cosa:
con php pubblicavo su altervista i miei siti di test in modo gratuito , in questo caso si
trovano hosting gratuiti dove pubblicare i miei test?
se si potresti indicarmene almeno uno?
grazie la guida è chiara e precisa

fabiobiondi 4 ottobre 2018 at 13:37 - Reply

Ciao Emanuele, a meno che non usi Server Side Rendering in Angular (e
inizialmente ne dubito), le tue applicazioni Angular / React utilizzeranno
esclusivamente di API RESTful.
In questo specifico contesto (il più comune) è su iciente che tu faccia una build
(npm run build), verrà creata una versione statica del sito (HTML + JS ES5) e il
risultato potrà quindi girare su un qualunque webserver (Apache, IIS, NGinx,Revoca
ecc). cookie
Lato server, le tuoi API le puoi mettere anche su un’altro server e fruirle dalla tua
app (purché sia abilitato CORS sul server).
Se hai altri dubbi o domande ti consiglio di frequentare il nostro gruppo Facebook
Angular Developer Italiani

giuseppe 20 ottobre 2018 at 12:59 - Reply

BRAVO

Leave A Comment

Comment...

Name (required) Email (required) Website

POST COMMENT

ITALIAN COMMUNITIES LATEST ARTICLES CONTACTS

 Live streams su front-end Trieste (North-East Italy)


development: Javascript, Email: hello@fabiobiondi.io
Angular, React, RxJS, state Web: fabiobiondi.io
managers…

LINK UTILI
 Deploy di un’applicazione
NodeJS su Azure con
 Corsi di Formazione
Visual Studio Code
 About Me
 Deploy di un sito statico su
Azure (storage) con Visual  Iscriviti alla Newsletter
Studio Code Revoca cookie
 Typescript 3.8 & ESNext
private fields
(this.#myProp)

 Creare Web Components


con Stencil.JS

 Lazy components and


modules in Angular 9 & IVY

2019 Fabio Biondi - All Rights Reserved - P.I. 01116230317 - Privacy Policy      

Revoca cookie

Potrebbero piacerti anche