ng serve --configuration=local-dev
A workspace contains the files for one or more projects
ng new angular-tour-of-heroes
It also creates the following workspace and starter project files:
- A new workspace, with a root folder named angular-tour-of-heroes.
- An initial skeleton app project in the src/app subfolder.
- Related configuration files.
cd angular-tour-of-heroes
ng serve --open
ng generate component heroes
The ngOnInit() is a lifecycle hook. Angular calls ngOnInit() shortly after creating a component. It's a good place to put initialization logic.
[(ngModel)] is Angular's two-way data binding syntax.
Every component must be declared in exactly one NgModule.
Create a new service
ng generate service hero
If a property should be bind to the template, ie the messageService, the property must be public because you're going to bind to it in the template.
In Angular, the best practice is to load and configure the router in a separate, top-level module that is dedicated to routing and imported by the root AppModule. By convention, the module class name is AppRoutingModule and it belongs in the app-routing.module.ts in the src/app folder.
ng generate module app-routing --flat --module=app
A typical Angular Route has two properties:
- path: a string that matches the URL in the browser address bar.
- component: the component that the router should create when navigating to this route.
The following line adds the RouterModule to the AppRoutingModule imports array and configures it with the routes in one step by calling RouterModule.forRoot():
imports: [ RouterModule.forRoot(routes) ],
Next, AppRoutingModule exports RouterModule so it will be available throughout the application.
exports: [ RouterModule ]
The <router-outlet>
tells the router where to display routed views.
The RouterOutlet is one of the router directives that became available to the AppComponent because AppModule imports AppRoutingModule which exported RouterModule.
The ng generate command you ran at the start of this tutorial added this import because of the --module=app
flag.
<a routerLink="/heroes">Heroes</a>
A routerLink attribute is set to "/heroes", the string that the router matches to the route to HeroesComponent. The routerLink is the selector for the RouterLink directive that turns user clicks into router navigations. It's another of the public directives in the RouterModule.
The ActivatedRoute holds information about the route to this instance of the HeroDetailComponent. This component is interested in the route's parameters extracted from the URL. The "id" parameter is the id of the hero to display.
The location is an Angular service for interacting with the browser. You'll use it later to navigate back to the view that navigated here.
const id = Number(this.route.snapshot.paramMap.get('id'));
The route.snapshot is a static image of the route information shortly after the component was created.
The paramMap is a dictionary of route parameter values extracted from the URL. The "id" key returns the id of the hero to fetch.
Route parameters are always strings. The JavaScript Number function converts the string to a number, which is what a hero id should be.
In general, an observable can return multiple values over time. An observable from HttpClient always emits a single value and then completes, never to emit again. This particular HttpClient.get() call returns an Observable<Hero[]>; that is, "an observable of hero arrays". In practice, it will only return a single hero array.
Attenzione che i Subscribe vanno poi fatti unsubscribe in ngOnDestroy a meno che non uso async
come pipe che lo fa in automatico
{{data$ | async | json}}
fare http.get e poi .pipe( switchMap(seconda chiamata), switchMap(terza chiamata dopo la seconda e posso girare il result della chiamata prec)). Potrebbe essere meglio il mergeMap o concatMap (avendo anche certezza dell'ordine) poiché altrimenti se le chiamate sono veloci posso essere cancellate alcune
SwitchMap prende un array ed emette un observable per ognuno, quindi potrei considerando users
come un array di User
così posso usare map prendendo una property del singolo User (mentre senza lo switchMap non potrei perché è un array)
SwitchMap(users => users)
.map(user => user.name)
shareReplay(1)
o share()
converte da cold a hot Observable quindi diventa Multicast (singleton e quindi eventi condivisi senza ripartire dall'inizio)
Subject
Observable speciale ed è hot. in subscriber prende i valori dal prossimo next.
BehaviorSubject
Observable simile a Subject ma prende anche un val di default. i subscriber prendo il valore attuale e i successivi o anche il def se non ha ancora emesso valori. Di solito si usa questo (e non Subject). ha anche il metodo getValue (ma da usare con parsimonia)
ReplaySubject
recupera lo storico dei valori nella subject, prende anche un paramentro ed è un numero che mi dice gli ultimi N valori da prendere
OT : chrome extension Lighthouse che da report su SEO, accessibilità, performance
npm install -g json-server
meglio però come dev dependencies npm install -D json-server
ng g m features/login --route login --module app
modulo e lazy login già utilizzabile
Se creo un modulo di routing per poter poi usare il tag router-outlet occorre che il modulo esporti il RouterModule, in questo modo tutti quelli che importeranno AppRoutingModule avranno accesso anche al RouterModule
Creiamo moduli anche per poter usare il lazy loading (usando il loadChildren)
Non si possono caricare lo stesso componente in più moduli (se c'è importazione doppia) -> allora devo wrapparlo in un modulo e utilizzare il modulo. Anche Material ha ogni componente in un suo modulo (così posso importare il singolo modulo e componente che mi serve).
Per fare in modo che la dependencies injection carichi implementazioni diverse occorre esplicitare nei providers
del modulo o componente:
{ provide: ConfigService, useClass: ConfigServiceAlternative }
il default:
{ provide: ConfigService, useClass: ConfigService }
che sarebbe equivalente a:
ConfigService
C'è una gerarchia degli injector e non è tutto su root
@HostBinding('className')
consente di mettere classe nella root tag del component, oppure @HostBinding class = 'nomeClasse'
, ma posso anche cambiare il contenuto @HostBinding innerText = 'new content'
HostListener
mi consente, se sono in una direttiva posso ad esempio mettermi in ascolto di un evento, tipo click:
@HostListener('click')
clickMe() {
consol.log('clickme')
}
Renderer2
mi da accesso a metodi di basso livello simili a jQuery tipo setStyle
, appendChild
Direttive strutturali (con *
davanti) hanno anche il vantaggio (mettendo l'asterisco, anche su nostre direttive) è che vengano generati wrap tag html (non rederizzati)