Angular Universal: Real Application Issues

Angular Universal is an open source project that extends @ angular / platform-server functionality. It makes Server Side Rendering possible in Angular.





Angular Universal supports multiple backends:





  1. Express .





  2. ASP.NET Core .





  3. hapi .





Socket Engine β€” -, SSR- . , Angular Universal Express, .





Angular Universal

Angular DOM node.js β€” domino. GET- domino , .





Angular . , DOM, . DOM . HTML GET-. Angular- .





SSR Angular

1.

. , . , .





, Angular SSR. , , Zone.js ApplicationRef.





Zone.js β€” , . Angular . Angular .





ApplicationRef β€” (docs). ApplicationRef#isStable. Observable, boolean. isStable true , Angular , false β€” .





, β€” , Angular





, Angular . .





, , . setInterval, rxjs.interval , Angular, . HTTP- . .





, timeout:





import { timeout, catchError } from 'rxjs/operators';
import { of } from 'rxjs/observable/of';

http.get('https://example.com')
	.pipe(
		timeout(2000),
		catchError(e => of(null))
	).subscribe()
      
      



, .





:





  • ;





  • timeout .





NgxSsrTimeoutModule @ngx-ssr/timeout. . AppServerModule, HTTP- .





import { NgModule } from '@angular/core';
import {
	ServerModule,
} from '@angular/platform-server';
import { AppModule } from './app.module';
import { AppComponent } from './app.component';
import { NgxSsrTimeoutModule } from '@ngx-ssr/timeout';

@NgModule({
	imports: [
		AppModule,
		ServerModule,
		NgxSsrTimeoutModule.forRoot({ timeout: 500 }),
	],
	bootstrap: [AppComponent],
})
export class AppServerModule {}
      
      



Angular NgZone.





import { Injectable, NgZone } from "@angular/core";

@Injectable()
export class SomeService {
	constructor(private ngZone: NgZone){
		this.ngZone.runOutsideAngular(() => {
			interval(1).subscribe(() => {
				// somo code
			})
		});
	}
}
      
      



tuiZonefree @taiga-ui/cdk:





import { Injectable, NgZone } from "@angular/core";
import { tuiZonefree } from "@taiga-ui/cdk";

@Injectable()
export class SomeService {
	constructor(private ngZone: NgZone){
		interval(1).pipe(tuiZonefree(ngZone)).subscribe()
	}
}

      
      



. , (. β„– 7). , .





2. Β« Β»

. , 2 . . 2 , .





, , , , , html . html, .





. : in-memory cache .





. . :





Cache-Control: max-age=31536000
      
      



.





.





In-memory cache. In-memory cache , API- . @ngx-ssr/cache



.





API- , NgxSsrCacheModule AppModule.





maxSize . 50 , 50 GET-, .





maxAge , .





import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppComponent } from './app.component';
import { NgxSsrCacheModule } from '@ngx-ssr/cache';
import { environment } from '../environments/environment';

@NgModule({
	declarations: [AppComponent],
	imports: [
		BrowserModule,
		NgxSsrCacheModule.configLruCache({ maxAge: 10 * 60_000, maxSize: 50 }),
	],
	bootstrap: [AppComponent],
})
export class AppModule {}
      
      



html.





, @ngx-ssr/cache @ngx-ssr/cache/express. withCache. .





import { ngExpressEngine } from '@nguniversal/express-engine';
import { LRUCache } from '@ngx-ssr/cache';
import { withCache } from '@ngx-ssr/cache/express';

server.engine(
	'html',
	withCache(
		new LRUCache({ maxAge: 10 * 60_000, maxSize: 100 }),
		ngExpressEngine({
			bootstrap: AppServerModule,
		})
	)
);
      
      



3. ReferenceError: localStorage is not defined

localStorage . . : ReferenceError: localStorage is not defined.





Angular- API. , node.js document. DOCUMENT DI.





API . DI. DI .





Web APIs for Angular.





:





import {Component, Inject, NgModule} from '@angular/core';
import {LOCAL_STORAGE} from '@ng-web-apis/common';

@Component({...})
export class SomeComponent {
	constructor(@Inject(LOCAL_STORAGE) localStorage: Storage) {
		localStorage.getItem('key');
	}
}
      
      



LOCAL_STORAGE @ng-web-apis/common. . UNIVERSAL_LOCAL_STORAGE @ng-web-apis/universal AppServerModule β€” LOCAL_STORAGE localStorage .





import { NgModule } from '@angular/core';
import {
	ServerModule,
} from '@angular/platform-server';
import { AppModule } from './app.module';
import { AppComponent } from './app.component';
import { UNIVERSAL_LOCAL_STORAGE } from '@ngx-ssr/timeout';

@NgModule({
	imports: [
		AppModule,
		ServerModule,
	],
	providers: [UNIVERSAL_LOCAL_STORAGE],
	bootstrap: [AppComponent],
})
export class AppServerModule {}
      
      



4.

, :





@Component({
	selector: 'ram-root',
	template: '<some-omp *ngIf="isServer"></some-omp>',
	styleUrls: ['./app.component.less'],
})
export class AppComponent {
	isServer = isPlatformServer(this.platformId);
	
	constructor(@Inject(PLATFORM_ID) private platformId: Object){}
}
      
      



PLATFORM_ID, . ngIf.





C DI .





.





export const IS_SERVER_PLATFORM = new InjectionToken<boolean>('Is server?', {
	factory() {
		return isPlatformServer(inject(PLATFORM_ID));
	},
});
      
      



IS_SERVER_PLATFORM : .





@Directive({
	selector: '[ifIsServer]',
})
export class IfIsServerDirective {
	constructor(
		@Inject(IS_SERVER_PLATFORM) isServer: boolean,
		templateRef: TemplateRef<any>,
		viewContainer: ViewContainerRef
	) {
		if (isServer) {
			viewContainer.createEmbeddedView(templateRef);
		}
	}
}
      
      



IfIsBowser.





.





@Component({
	selector: 'ram-root',
	template: '<some-omp *ifIsServer"></some-omp>',
	styleUrls: ['./app.component.less'],
})
export class AppComponent {}
      
      



. . . , @ngx-ssr/platform.





5. Memory leak

interval .





import { Injectable, NgZone } from "@angular/core";
import { interval } from "rxjs";

@Injectable()
export class LocationService {
	constructor(ngZone: NgZone) {
		ngZone.runOutsideAngular(() => interval(1000).subscribe(() => {
		...
		}));
	}
}
      
      



, , subscribe, . . .





ngOnDestoroy. , . . , :





import { Injectable, NgZone, OnDestroy } from "@angular/core";
import { interval, Subscription } from "rxjs";

@Injectable()
export class LocationService implements OnDestroy {
	private subscription: Subscription;

	constructor(ngZone: NgZone) {
		this.subscription = ngZone.runOutsideAngular(() =>
			interval(1000).subscribe(() => {})
		);
	}

	ngOnDestroy(): void {
		this.subscription.unsubscribe();
	}
}
      
      



6.

. . index.html.





. . β„– 1. , ? ?





.





7.

, , , .





Angular , . html .





. , . roadmap Angular Universal Β«Full client rehydration strategy that reuses DOM elements/CSS rendered on the serverΒ».





Angular Universal β€” . .





, - Angular Universal , . , , - .








All Articles