@MicheleStieven
export class CounterService {
counter = 0;
}
@Component({ ... })
export class MyComponent {
constructor(c: CounterService) {
console.log(c.counter)
}
}
@NgModule({
...
providers: [ CounterService, ... ]
})
export class AppModule { }
Estendere con librerie
Organizzare un'applicazione
ESTERNE
INTERNE
@NgModule({
imports: [ ...moduli ],
declarations: [ ...componenti/dir/pipe ],
providers: [ ...servizi ],
exports: [ ...moduli, c/d/p ],
bootstrap: [ ...componente ],
entryComponents: [ ...componenti ]
})
export class MyModule {}
AppModule
DashboardModule
ContactModule
Reducers
Reducers
Actions
Actions
ReduxModule
RootReducer
ContactModule
contact.module.ts
contact-routing.module.ts
views
components
store
reducers
actions
services
utils
import { CommonModule } from '@angular/core';
import { ContactRoutingModule }
from './contact-routing.module';
@NgModule({
imports: [
CommonModule,
ContactRoutingModule,
...
],
declarations: [ ... ]
})
export class ContactModule { }
AppModule
Feature Modules
BrowserModule
CommonModule
Componenti, direttive e pipe sono
I servizi dichiarati in un modulo sono
I servizi accessibili globalmente vanno dichiarati in AppModule!
O no?
Definire le rotte della nostra applicazione
Passaggio di dati da una rotta all'altra
Ottimizzare le performance
Restringere l'accesso ad alcune aree dell'applicazione
{
path: 'contacts',
loadChildren: () => import('./contact.module')
.then(m => m.ContactModule)
}
Possiamo usare loadChildren anche per moduli non lazy-loaded con una normale funzione!
function contactsEntrypoint() {
return ContactsModule;
}
...
{
path: 'contacts',
loadChildren: contactsEntrypoint
}
import { PreloadAllModules } from '@angular/router';
...
RouterModule.forRoot(
ROUTES,
{ preloadingStrategy: PreloadAllModules }
)
{
path: 'admin',
loadChildren: './admin.module#AdminModule',
data: {preload: true}
}
export class SelectivePreload implements PreloadingStrategy {
preload(route: Route, load: Function): Observable<any> {
return route.data && route.data.preload ? load() : of(null);
}
}
RouterModule.forRoot(
ROUTES,
{ preloadingStrategy: SelectivePreload }
)
{
path: 'admin',
loadChildren: './admin.module#AdminModule',
data: {whatever: true}
}
export class Pippo implements PreloadingStrategy {
preload(route: Route, load: Function): Observable<any> {
return route.data && route.data.whatever ? load() : of(null);
}
}
RouterModule.forRoot(
ROUTES,
{ preloadingStrategy: Pippo }
)
Utilizziamo CoreModule per fare il provide dei servizi globali: viene importato UNA sola volta SOLO da AppModule.
E se per sbaglio un altro modulo lo importasse?
constructor(@Optional() @SkipSelf() core: CoreModule) {
if(core) {
throw new Error('You shall not run!');
}
}
{
"compilerOptions": {
...,
"baseUrl": "src",
"paths": {
"@app/*": ["app/*"],
// oppure...
"@app/core/*": ["app/core/*"],
"@app/shared/*": ["app/shared/*"],
"@env/*": ["environments/*"]
}
}
}
tsconfig.json
Usiamo gli Shared Module per dichiarare componenti/direttive/pipe in un unico modulo, che verrà importato da altri moduli.
import { CustomButton } from '...';
@NgModule({
declarations: [ CustomButton ],
exports: [ CustomButton ]
})
export class SharedModule {}
Uno Shared Module non dovrebbe fare il provide di servizi!
I Lazy Module, importandolo, ne genererebbero un'altra istanza...
interface ModuleWithProviders {
ngModule: Type<any>
providers?: Provider[]
}
Con MWP possiamo prendere un modulo esistente ed "agganciarci" dei servizi!
@NgModule({
declarations: [...],
imports: [...],
exports: [...]
})
export class SharedModule {
static forRoot() {
return {
ngModule: SharedModule,
providers: [...]
}
}
}
RouterModule.forRoot()
RouterModule.forChild()
class RouterModule {
static forRoot(routes, config) {
return {
ngModule: RouterModule,
providers: [
{
provide: ROUTES,
multi: true,
useValue: routes
},
...RouterProviders
]
}
}
}
Possiamo definire dei servizi che verranno usati dal router per stabilire se una rotta può essere utilizzata
export class AuthGuard
implements CanActivate {
canActivate() {
return true;
}
}
{
path: 'admin',
component: AdminComponent,
canActivate: [ AuthGuard ]
}
{
path: '',
canActivateChild: [ AuthGuard ],
children: [ ... ]
}
{
path: 'admin',
loadChildren: () => import(...).then(...),
canLoad: [ AuthGuard ]
}
{
path: '',
component: MyComponent,
canDeactivate: [ DeactivateGuard ]
}
interface CanDeactivate<T> {
canDeactivate(
component: T,
currentRoute: ActivatedRouteSnapshot,
currentState: RouterStateSnapshot,
nextState?: RouterStateSnapshot)
: Observable<boolean>|Promise<boolean>|boolean
}
export class DeactivateGuard implements CanDeactivate<MyComponent>{
canDeactivate(component: MyComponent) {
return component.canDeactivate ? component.canDeactivate() : true;
}
}
@Component({...})
export class MyComponent {
canDeactivate() { return true; }
}
...
{
path: 'my-component',
canDeactivate: [ DeactivateGuard ],
component: MyComponent
}
export interface CanComponentDeactivate {
canDeactivate: () => Observable<boolean> | Promise<boolean> | boolean
}
export class DeactivateGuard implements CanDeactivate<CanComponentDeactivate>{
canDeactivate(component: CanComponentDeactivate) {
return component.canDeactivate ? component.canDeactivate() : true;
}
}
@Component({...})
export class MyComponent {
canDeactivate() { return true; }
}
...
{
path: 'my-component',
canDeactivate: [ DeactivateGuard ],
component: MyComponent
}
{
path: 'contact/:id',
component: ContactComponent
}
@Component({...})
class ContactComponent {
constructor(route: ActivatedRoute) {
route.paramMap.subscribe(params => {
// params['id']
});
}
}
Immaginiamo la rotta:
path: 'contact/:id',
component: ContactComponent
ContactComponent dovrebbe richiedere un servizio e chiedere i dati del contatto passandogli l'id.
Vogliamo passargli direttamente i dati, evitandogli la dipendenza dal servizio...
{
path: 'contact/:id',
component: ContactComponent,
resolve: {
contact: ContactResolver
}
}
@Injectable()
class ContactResolver implements Resolve<any> {
constructor(private svc: MyService) {}
resolve(route: ActivatedRouteSnapshot) {
return this.svc.getContact(route.params.id);
}
}
@Component()
export class ContactComponent {
constructor(route: ActivatedRoute) {
route.data.subscribe(...)
}
}
- Servizi globali, per snellire AppModule
- Importato SOLO da AppModule
- Raggruppare componenti/direttive/pipe
- Servono servizi? Utilizziamo forRoot()
- Possono importare SharedModule
- Possono essere Lazy Loaded
- ...Possono avere i propri servizi!
SLIDES.COM/MICHELESTIEVEN/ANGULAR-ARCHITECTURES