SlideShare a Scribd company logo
Summer. Sea. JavaScript.
Angular Universal
A medicine for the SEO/CDN issues
Maciej
Treder
@maciejtreder
Outline
• SPA pitfall
• Server-side rendering
• Server vs. Browser
• API optimization
• Deployment
• Prerendering & Summary
SPA pitfall SSR
Server vs.
Browser
APIs Deploy Summary
SPA pitfall
ng build
• Ahead of Time compilation
—prod flag
• Ahead of Time compilation
• Minified
• Tree-shaked
SPA pitfall
ng build vs. —prod
SPA pitfall
SPA problem
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteRule ^index.html$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . index.html [L]
</IfModule>
.htaccess
SPA pitfall
SPA Problem
GET /
GET /anotherPage
index.html
GET
/subpage
GET
/contact
GET /home
SPA pitfall
SPA Problem
GET / GET /anotherPage
SPA pitfall
SPA Problem
SPA pitfall
Server Side Rendering
SSR
Server Side Rendering
GET /
GET /anotherPage
SSR
Is it worth?
curl localhost:8080
<!DOCTYPE html><html lang="en"><head>
<meta charset="utf-8">
<title>SomeProject</title>
<base href="/">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" type="image/x-icon" href="favicon.ico">
<link rel="stylesheet" href="styles.3ff695c00d717f2d2a11.css"><style ng-transition="app-root">
/*# sourceMappingURL=data:application/
json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IiIsImZpbGUiOiJzcmMvYXBwL2FwcC5jb21wb25lbnQuY3NzIn0= */</
style></head>
<body>
<script type="text/javascript" src="runtime.26209474bfa8dc87a77c.js"></script><script type="text/javascript" src="es2015-
polyfills.c5dd28b362270c767b34.js" nomodule=""></script><script type="text/javascript" src="polyfills.8bbb231b43165d65d357.js"></
script><script type="text/javascript" src="main.8a9128130a3a38dd7ee5.js"></script>
<script id="app-root-state" type="application/json">{}</script></body></html>
<app-root _nghost-sc0="" ng-version="7.2.9"><div _ngcontent-sc0="" style="text-align:center"><h1 _ngcontent-sc0=""> Welcome
to someProject! </h1><img _ngcontent-sc0="" alt="Angular Logo" src="data:image/
svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTAgMjUwIj4KICAgIDxwYXRoIGZpbGw9IiNERD
AwMzEiIGQ9Ik0xMjUgMzBMMzEuOSA2My4ybDE0LjIgMTIzLjFMMTI1IDIzMGw3OC45LTQzLjcgMTQuMi0xMjMuMXoiIC8+CiAgICA8cGF0aCBmaWxsPSIjQzMwMDJ
GIiBkPSJNMTI1IDMwdjIyLjItLjFWMjMwbDc4LjktNDMuNyAxNC4yLTEyMy4xTDEyNSAzMHoiIC8+CiAgICA8cGF0aCAgZmlsbD0iI0ZGRkZGRiIgZD0iTTEyNSA1
Mi4xTDY2LjggMTgyLjZoMjEuN2wxMS43LTI5LjJoNDkuNGwxMS43IDI5LjJIMTgzTDEyNSA1Mi4xem0xNyA4My4zaC0zNGwxNy00MC45IDE3IDQwLjl6IiAvPgogI
Dwvc3ZnPg==" width="300"></div><h2 _ngcontent-sc0="">Here are some links to help you start: </h2><ul _ngcontent-sc0=""><li
_ngcontent-sc0=""><h2 _ngcontent-sc0=""><a _ngcontent-sc0="" href="https://angular.io/tutorial" rel="noopener"
target="_blank">Tour of Heroes</a></h2></li><li _ngcontent-sc0=""><h2 _ngcontent-sc0=""><a _ngcontent-sc0="" href="https://
angular.io/cli" rel="noopener" target="_blank">CLI Documentation</a></h2></li><li _ngcontent-sc0=""><h2 _ngcontent-sc0=""><a
_ngcontent-sc0="" href="https://blog.angular.io/" rel="noopener" target="_blank">Angular blog</a></h2></li></ul></app-root>
SSR
SSR
—prod vs. universal
SSR
—prod vs. universal
Load HTML Bootstrap
Load HTML Bootstrap
SSR
NO SSR First meaningful paint
First meaningful paint
SSR
How to start?
Official guide
https://angular.io/guide/universal
ng-toolkit
https://github.com/maciejtreder/ng-toolkit
SSR
ng add @nguniversal/express-engine
CREATE src/main.server.ts (220 bytes)
CREATE src/app/app.server.module.ts (318 bytes)
CREATE src/tsconfig.server.json (219 bytes)
CREATE webpack.server.config.js (1360 bytes)
CREATE server.ts (1500 bytes)
UPDATE package.json (1876 bytes)
UPDATE angular.json (4411 bytes)
UPDATE src/main.ts (432 bytes)
UPDATE src/app/app.module.ts (359 bytes)
SSR
Adjust your modules
app.module.tsapp.server.module.ts
@NgModule({
bootstrap: [AppComponent],
imports: [
BrowserModule.withServerTransition({appId: 'my-app'}),
//other imports
],
})
export class AppModule {}
import {NgModule} from '@angular/core';
import {ServerModule} from '@angular/platform-server';
import {ModuleMapLoaderModule} from
‘@nguniversal/module-map-ngfactory-loader';
import {AppModule} from './app.module';
import {AppComponent} from './app.component';
@NgModule({
imports: [
AppModule,
ServerModule,
ModuleMapLoaderModule
],
bootstrap: [AppComponent],
})
export class AppServerModule {}
SSR
Adjust your modules
Official guide
app.module.tsapp.server.module.ts
@NgModule({
declarations: [AppComponent],
imports: [
//common imports
]
})
export class AppModule {}
import {NgModule} from '@angular/core';
import {ServerModule} from '@angular/platform-server';
import {ModuleMapLoaderModule} from
‘@nguniversal/module-map-ngfactory-loader';
import {AppModule} from './app.module';
import {AppComponent} from './app.component';
@NgModule({
imports: [
AppModule,
ServerModule,
ModuleMapLoaderModule,
//server specific imports
],
bootstrap: [AppComponent],
})
export class AppServerModule {}
app.browser.module.ts
@NgModule({
bootstrap: [AppComponent],
imports: [
AppModule,
BrowserModule.withServerTransition({appId: 'my-app'}),
//browser specific imports
]
})
export class AppModule {}
//browser specific imports
//server specific imports
SSR
ng add @ng-toolkit/universal
CREATE local.js (248 bytes)
CREATE server.ts (1546 bytes)
CREATE webpack.server.config.js (1214 bytes)
CREATE src/main.server.ts (249 bytes)
CREATE src/tsconfig.server.json (485 bytes)
CREATE src/app/app.browser.module.ts (395 bytes)
CREATE src/app/app.server.module.ts (788 bytes)
CREATE ng-toolkit.json (95 bytes)
UPDATE package.json (1840 bytes)
UPDATE angular.json (4022 bytes)
UPDATE src/app/app.module.ts (417 bytes)
UPDATE src/main.ts (447 bytes)
SSR
And let’s go!
• npm run build:prod
• npm run server
Date: 2018-11-21T13:04:33.302Z
Hash: 1a82cb687d2e22b5d12b
Time: 10752ms
chunk {0} runtime.ec2944dd8b20ec099bf3.js (runtime) 1.41 kB [entry] [rendered]
chunk {1} main.09093ffa4ad7f66bc6ff.js (main) 169 kB [initial] [rendered]
chunk {2} polyfills.c6871e56cb80756a5498.js (polyfills) 37.5 kB [initial] [rendered]
chunk {3} styles.3bb2a9d4949b7dc120a9.css (styles) 0 bytes [initial] [rendered]
> my-app@0.0.0 server /Users/mtreder/myApp
> node local.js
Listening on: http://localhost:8080
SSR
Under the hood
export const app = express();
app.use(compression());
app.use(cors());
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
const {AppServerModuleNgFactory, LAZY_MODULE_MAP} = require('./dist/server/main');
app.engine('html', ngExpressEngine({
bootstrap: AppServerModuleNgFactory,
providers: [
provideModuleMap(LAZY_MODULE_MAP)
]
}));
server.ts
SSR
Under the hood
app.get('/*', (req, res) => {
res.render('index', {req, res}, (err, html) => {
if (html) {
res.send(html);
} else {
console.error(err);
res.send(err);
}
});
});
server.ts
SSR
Under the hood
app.set('view engine', 'html');
app.set('views', './dist/browser');
app.get('*.*', express.static('./dist/browser', {
maxAge: '1y'
}));
server.ts
SSR
Server Side Rendering
GET /
GET /anotherPage
SSR
ng-toolkit
SSR
Browser vs. Server
• document
• window
• navigator
• file system
• request
Server vs.
Browser
Browser vs. Server
public ngOnInit(): void {
console.log(window.navigator.language);
}
Listening on: http://localhost:8080
ERROR ReferenceError: window is not defined
at AppComponent.module.exports../src/app/app.component.ts.AppComponent.ngOnInit
(/Users/mtreder/myApp/dist/server.js:118857:21)
at checkAndUpdateDirectiveInline (/Users/mtreder/myApp/dist/server.js:19504:19)
at checkAndUpdateNodeInline (/Users/mtreder/myApp/dist/server.js:20768:20)
at checkAndUpdateNode (/Users/mtreder/myApp/dist/server.js:20730:16)
at prodCheckAndUpdateNode (/Users/mtreder/myApp/dist/server.js:21271:5)
at Object.updateDirectives (/Users/mtreder/myApp/dist/server.js:118833:264)
at Object.updateDirectives (/Users/mtreder/myApp/dist/server.js:21059:72)
at Object.checkAndUpdateView (/Users/mtreder/myApp/dist/server.js:20712:14)
at ViewRef_.module.exports.ViewRef_.detectChanges (/Users/mtreder/myApp/dist/
server.js:19093:22)
at /Users/mtreder/myApp/dist/server.js:15755:63
Server vs.
Browser
server? browser?
import { Component, Inject, PLATFORM_ID, OnInit } from '@angular/core';
import { isPlatformBrowser, isPlatformServer } from '@angular/common';
@Component({
selector: 'home-view',
templateUrl: './home.component.html'
})
export class HomeComponent implements OnInit {
constructor( private platformId) {}
public ngOnInit(): void {
if ( ) {
console.log('I am executed in the browser!’);
// window.url can be reached here
}
if (isPlatformServer(this.platformId)) {
console.log('I am executed in the server!’);
// window.url CAN’T be reached here
}
}
}
Server vs.
Browser
Wrapper Service
• Determine if we are in the browser or server
• Retrieve window or request object
• Create ‘mock’ window based on request object if necessary
Server vs.
Browser
REQUEST
import { Component, OnInit, Inject, PLATFORM_ID, Optional } from ‘@angular/core’;
import { REQUEST } from '@nguniversal/express-engine/tokens';
import { isPlatformServer } from '@angular/common';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
})
export class AppComponent implements OnInit {
constructor(
@Inject(REQUEST) private request: any,
@Inject(PLATFORM_ID) private platformId: any) {}
public ngOnInit(): void {
if (isPlatformServer(this.platformId)) {
console.log(this.request.headers);
}
}
}
import { REQUEST } from '@nguniversal/express-engine/tokens';
@Optional @Inject(REQUEST) private request: any,
console.log(this.request.headers);
Server vs.
Browser
REQUEST
Listening on: http://localhost:8080
{ host: 'localhost:8080',
connection: 'keep-alive',
'cache-control': 'max-age=0',
'upgrade-insecure-requests': '1',
'user-agent':
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36
(KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36',
accept:
'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8',
'accept-encoding': 'gzip, deflate, br',
'accept-language': 'en-US,en;q=0.9,ru;q=0.8',
'if-none-match': 'W/"40e-JviTST4QyiABJz2Lg+QxzZtiXv8"' }
'accept-language': 'en-US,en;q=0.9,ru;q=0.8',
Server vs.
Browser
Wrapper Service
@Injectable()
export class WindowService {
private _window: Window;
constructor(@Inject(PLATFORM_ID) platformId: any, @Optional @Inject(REQUEST) private request: any ) {
if (isPlatformServer(platformId)) {
this._window = {
navigator: {
language: this.request.headers['accept-language']
},
URL: this.request.headers.host + '' + this.request.url
};
} else {
this._window = window;
}
}
get window(): any {
return this._window;
}
}
Server vs.
Browser
Wrapper Service
import { Component , OnInit, Inject} from '@angular/core';
import { WINDOW } from '@ng-toolkit/universal';
export class AppComponent implements OnInit {
constructor(@Inject(WINDOW) private window: Window) {}
public ngOnInit(): void {
console.log(window.navigator.language);
}
}
app.component.ts
console.log(this.window.navigator.language);
Server vs.
Browser
@ng-toolkit/universal
import { NgtUniversalModule } from '@ng-toolkit/universal';
import { NgModule } from '@angular/core';
@NgModule({
imports:[
NgtUniversalModule
]
})
export class AppModule { }
app.module.ts
Server vs.
Browser
Server/Browser modules
@ngx-translate
• i18n module
• multiple ways of usage
{{‘Welcome to' | translate}}
<div [innerHTML]="'HELLO' | translate"></div>
{
"Welcome to": "Ласкаво просимо в"
}
uk.json
Server vs.
Browser
Server/Browser modules
import { TranslateHttpLoader } from '@ngx-translate/http-loader';
import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
export function HttpLoaderFactory(http: HttpClient) {
return new TranslateHttpLoader(http);
}
@NgModule({
imports:[
TranslateModule.forRoot({
loader: {provide: TranslateLoader, useFactory: HttpLoaderFactory,
deps: [httpClient]}
})
]
})
export class AppBrowserModule {}
export function httpLoaderFactory(http: HttpClient): TranslateLoader {
return new TranslateHttpLoader(http);
}
HttpLoaderFactory,
app.browser.module.ts
Server vs.
Browser
Server/Browser modules
import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
import { Observable, Observer } from 'rxjs';
import * as fs from 'fs';
export function universalLoader(): TranslateLoader {
return {
getTranslation: (lang: string) => {
return Observable.create((observer: Observer<any>) => {
observer.next(JSON.parse(fs.readFileSync(`./dist/assets/i18n/${lang}.json`, 'utf8')));
observer.complete();
});
}
} as TranslateLoader;
}
@NgModule({
imports:[
TranslateModule.forRoot({
loader: {provide: TranslateLoader, useFactory: universalLoader}
})
]
})
export class AppServerModule {}
export function universalLoader(): TranslateLoader {
app.server.module.ts
universalLoader }
Server vs.
Browser
Server/Browser modules
import { Component, OnInit, Inject } from '@angular/core';
import { TranslateService } from '@ngx-translate/core';
import { WINDOW } from '@ng-toolkit/universal';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
})
export class AppComponent implements OnInit {
constructor(
@Inject(WINDOW) private window: Window,
private translateService: TranslateService
) {}
public ngOnInit(): void {
this.translateService.use(this.window.navigator.language);
}
}
app.component.ts
this.translateService.use(this.window.navigator.language);
Server vs.
Browser
i18n with Universal
https://www.twilio.com/blog/create-search-engine-friendly-
internationalized-web-apps-angular-universal-ngx-translate
Server vs.
Browser
Server vs.
Browser
API optimization
APIs
DRY(c)
Don’t repeat your calls
export class AppComponent implements OnInit {
public post: Observable<any>;
constructor(private httpClient: HttpClient) {}
public ngOnInit(): void {
this.post = this.httpClient.get('https://jsonplaceholder.typicode.com/posts/1');
}
}
APIs
2
1
3
4
5 6
external.api.com
APIs
external.api.com
my-website.com
1
2
3
APIs
HttpCacheModule
npm install @nguniversal/common
import { NgtUniversalModule } from '@ng-toolkit/universal';
import { CommonModule } from '@angular/common';
import { HttpClientModule } from '@angular/common/http';
import { TransferHttpCacheModule } from '@nguniversal/common';
import { NgModule } from '@angular/core';
import { AppComponent } from './app.component';
@NgModule({
declarations: [
AppComponent
],
imports:[
CommonModule,
NgtUniversalModule,
TransferHttpCacheModule,
HttpClientModule
]
})
export class AppModule { }
APIs
TransferState
• ServerTransferStateModule (@angular/platform-server)
• BrowserTransferStateModule (@angular/platform-browser)
• get(key, fallbackValue)
• set(key, value)
• has(key)
• remove(key)
APIs
HTTP_INTERCEPTOR
• Provided in the AppModule
• Every http request made with HttpClient goes threw it
• Used to transform request or response ie:
• Adding authentication headers
APIs
HTTP_INTERCEPTOR
@Injectable()
export class ServerStateInterceptor implements HttpInterceptor {
    constructor(private _transferState: TransferState) {}
    intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
        return next.handle(req).pipe(tap(event => {
            if (event instanceof HttpResponse) {
                this._transferState.set(makeStateKey(req.url), event.body);
            }
        }));
    }
}
APIs
HTTP_INTERCEPTOR
@Injectable()
export class BrowserStateInterceptor implements HttpInterceptor {
    constructor(private _transferState: TransferState) { }
    intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
        if (req.method !== 'GET') {
            return next.handle(req);
        }
        const storedResponse: string = this._transferState.get(makeStateKey(req.url), null);
        if (storedResponse) {
            const response = new HttpResponse({ body: storedResponse, status: 200 });
this._transferState.remove(makeStateKey(req.url));
            return of(response);
        }
        return next.handle(req);
    }
}
APIs
HTTP_INTERCEPTOR
import {HTTP_INTERCEPTORS } from '@angular/common/http';
providers: [
{
provide: HTTP_INTERCEPTORS,
useClass: BrowserStateInterceptor,
multi: true,
}
]
import {HTTP_INTERCEPTORS } from '@angular/common/http';
providers: [
{
provide: HTTP_INTERCEPTORS,
useClass: ServerStateInterceptor,
multi: true,
}
]
APIs
Performance
export class RouteResolverService implements Resolve<any> {
constructor(
private httpClient: HttpClient,
@Inject(PLATFORM_ID) private platformId: any
) {}
public resolve(): Observable<any> {
}
}
const watchdog: Observable<number> = timer(500);
if (isPlatformBrowser(this.platformId)) {
return this.httpClient.get<any>('https://jsonplaceholder.typicode.com/posts/1');
}
return Observable.create(subject => {
this.httpClient.get<any>('https://jsonplaceholder.typicode.com/posts/1')
.subscribe(response => {
subject.next(response);
subject.complete();
});
})
.pipe(takeUntil(watchdog))
watchdog.subscribe(() => {
subject.next('timeout');
subject.complete()
})
APIs
0.5sec
APIs
0.5sec
APIs
https://www.twilio.com/blog/faster-javascript-web-apps-
angular-universal-transferstate-api-watchdog
DRY(c) & Performance
APIs
APIs
Deployment
Deploy
Let’s go Serverless!
• Function as a Service
• Event-driven
• Scalable
• Pay for the up-time
Deploy
Let’s go Serverless!
https://www.twilio.com/blog/angular-universal-
javascript-node-js-aws-lambda
Deploy
Deploy
Prerender
• Generating HTML files at a build time
• Can be hosted from traditional hosting (ie. AWS S3)
• Doesn’t perform dynamic request
• https://github.com/maciejtreder/angular-ssr-prerender
• @ng-toolkit/universal + npm run build:prerender
Deploy
Summary
server-side renderingprerenderng build —prod
SEO
Performance
Difficulty
SEO + external calls
Additional back-end logic
Summary
@maciejtreder

More Related Content

What's hot

Quality assurance for javascript
Quality assurance for javascriptQuality assurance for javascript
Quality assurance for javascriptYuan Wang
 
Workshop 9: BackboneJS y patrones MVC
Workshop 9: BackboneJS y patrones MVCWorkshop 9: BackboneJS y patrones MVC
Workshop 9: BackboneJS y patrones MVC
Visual Engineering
 
Sub task in project
Sub task in projectSub task in project
Sub task in project
Celine George
 
Struts introduction
Struts introductionStruts introduction
Struts introduction
Muthukumaran Subramanian
 
Making Workflows Work for You
Making Workflows Work for YouMaking Workflows Work for You
Making Workflows Work for You
Stephan Richter
 
MV(C, mvvm) in iOS and ReactiveCocoa
MV(C, mvvm) in iOS and ReactiveCocoaMV(C, mvvm) in iOS and ReactiveCocoa
MV(C, mvvm) in iOS and ReactiveCocoa
Yi-Shou Chen
 
Struts presentation
Struts presentationStruts presentation
Struts presentation
Nicolaescu Petru
 
Model View Controller
Model View ControllerModel View Controller
Model View Controller
Madhukar Kumar
 
Introduction to Ember.js
Introduction to Ember.jsIntroduction to Ember.js
Introduction to Ember.js
Vinoth Kumar
 
MVC ppt presentation
MVC ppt presentationMVC ppt presentation
MVC ppt presentation
Bhavin Shah
 
MVC Pattern. Flex implementation of MVC
MVC Pattern. Flex implementation of MVCMVC Pattern. Flex implementation of MVC
MVC Pattern. Flex implementation of MVC
Anton Krasnoshchok
 
Hanselman lipton asp_connections_ams304_mvc
Hanselman lipton asp_connections_ams304_mvcHanselman lipton asp_connections_ams304_mvc
Hanselman lipton asp_connections_ams304_mvcdenemedeniz
 
Real-world Model-View-ViewModel for WPF
Real-world Model-View-ViewModel for WPFReal-world Model-View-ViewModel for WPF
Real-world Model-View-ViewModel for WPF
Paul Stovell
 
Feature Development with jQuery
Feature Development with jQueryFeature Development with jQuery
Feature Development with jQueryMichael Edwards
 
Struts(mrsurwar) ppt
Struts(mrsurwar) pptStruts(mrsurwar) ppt
Struts(mrsurwar) ppt
mrsurwar
 
Unit testing and MVVM in Silverlight
Unit testing and MVVM in SilverlightUnit testing and MVVM in Silverlight
Unit testing and MVVM in SilverlightDevnology
 
Introduction to Zend Framework 2
Introduction to Zend Framework 2Introduction to Zend Framework 2
Introduction to Zend Framework 2
John Coggeshall
 
Skroutz Android MVP and Adapter Delegates presentation
Skroutz Android MVP and Adapter Delegates  presentationSkroutz Android MVP and Adapter Delegates  presentation
Skroutz Android MVP and Adapter Delegates presentation
gmetal
 
Djangocon 09 Presentation - Pluggable Applications
Djangocon 09 Presentation - Pluggable ApplicationsDjangocon 09 Presentation - Pluggable Applications
Djangocon 09 Presentation - Pluggable Applications
Nowell Strite
 
MVC
MVCMVC

What's hot (20)

Quality assurance for javascript
Quality assurance for javascriptQuality assurance for javascript
Quality assurance for javascript
 
Workshop 9: BackboneJS y patrones MVC
Workshop 9: BackboneJS y patrones MVCWorkshop 9: BackboneJS y patrones MVC
Workshop 9: BackboneJS y patrones MVC
 
Sub task in project
Sub task in projectSub task in project
Sub task in project
 
Struts introduction
Struts introductionStruts introduction
Struts introduction
 
Making Workflows Work for You
Making Workflows Work for YouMaking Workflows Work for You
Making Workflows Work for You
 
MV(C, mvvm) in iOS and ReactiveCocoa
MV(C, mvvm) in iOS and ReactiveCocoaMV(C, mvvm) in iOS and ReactiveCocoa
MV(C, mvvm) in iOS and ReactiveCocoa
 
Struts presentation
Struts presentationStruts presentation
Struts presentation
 
Model View Controller
Model View ControllerModel View Controller
Model View Controller
 
Introduction to Ember.js
Introduction to Ember.jsIntroduction to Ember.js
Introduction to Ember.js
 
MVC ppt presentation
MVC ppt presentationMVC ppt presentation
MVC ppt presentation
 
MVC Pattern. Flex implementation of MVC
MVC Pattern. Flex implementation of MVCMVC Pattern. Flex implementation of MVC
MVC Pattern. Flex implementation of MVC
 
Hanselman lipton asp_connections_ams304_mvc
Hanselman lipton asp_connections_ams304_mvcHanselman lipton asp_connections_ams304_mvc
Hanselman lipton asp_connections_ams304_mvc
 
Real-world Model-View-ViewModel for WPF
Real-world Model-View-ViewModel for WPFReal-world Model-View-ViewModel for WPF
Real-world Model-View-ViewModel for WPF
 
Feature Development with jQuery
Feature Development with jQueryFeature Development with jQuery
Feature Development with jQuery
 
Struts(mrsurwar) ppt
Struts(mrsurwar) pptStruts(mrsurwar) ppt
Struts(mrsurwar) ppt
 
Unit testing and MVVM in Silverlight
Unit testing and MVVM in SilverlightUnit testing and MVVM in Silverlight
Unit testing and MVVM in Silverlight
 
Introduction to Zend Framework 2
Introduction to Zend Framework 2Introduction to Zend Framework 2
Introduction to Zend Framework 2
 
Skroutz Android MVP and Adapter Delegates presentation
Skroutz Android MVP and Adapter Delegates  presentationSkroutz Android MVP and Adapter Delegates  presentation
Skroutz Android MVP and Adapter Delegates presentation
 
Djangocon 09 Presentation - Pluggable Applications
Djangocon 09 Presentation - Pluggable ApplicationsDjangocon 09 Presentation - Pluggable Applications
Djangocon 09 Presentation - Pluggable Applications
 
MVC
MVCMVC
MVC
 

Similar to Maciej Treder ''Angular Universal - a medicine for the Angular + SEO/CDN issues''

Maciej Treder "Server-side rendering with Angular—be faster and more SEO, CDN...
Maciej Treder "Server-side rendering with Angular—be faster and more SEO, CDN...Maciej Treder "Server-side rendering with Angular—be faster and more SEO, CDN...
Maciej Treder "Server-side rendering with Angular—be faster and more SEO, CDN...
Fwdays
 
Angular server side rendering - Strategies & Technics
Angular server side rendering - Strategies & Technics Angular server side rendering - Strategies & Technics
Angular server side rendering - Strategies & Technics
Eliran Eliassy
 
Reactive application using meteor
Reactive application using meteorReactive application using meteor
Reactive application using meteor
Sapna Upreti
 
Reactive Application Using METEOR
Reactive Application Using METEORReactive Application Using METEOR
Reactive Application Using METEOR
NodeXperts
 
React loadable
React loadableReact loadable
React loadable
George Bukhanov
 
Rest web service_with_spring_hateoas
Rest web service_with_spring_hateoasRest web service_with_spring_hateoas
Rest web service_with_spring_hateoas
Zeid Hassan
 
A Story about AngularJS modularization development
A Story about AngularJS modularization developmentA Story about AngularJS modularization development
A Story about AngularJS modularization development
Johannes Weber
 
using Mithril.js + postgREST to build and consume API's
using Mithril.js + postgREST to build and consume API'susing Mithril.js + postgREST to build and consume API's
using Mithril.js + postgREST to build and consume API's
Antônio Roberto Silva
 
Introducing Rendr: Run your Backbone.js apps on the client and server
Introducing Rendr: Run your Backbone.js apps on the client and serverIntroducing Rendr: Run your Backbone.js apps on the client and server
Introducing Rendr: Run your Backbone.js apps on the client and serverSpike Brehm
 
Building an angular application -1 ( API: Golang, Database: Postgres) v1.0
Building an angular application -1 ( API: Golang, Database: Postgres) v1.0Building an angular application -1 ( API: Golang, Database: Postgres) v1.0
Building an angular application -1 ( API: Golang, Database: Postgres) v1.0
Frost
 
Front End Development for Back End Developers - UberConf 2017
Front End Development for Back End Developers - UberConf 2017Front End Development for Back End Developers - UberConf 2017
Front End Development for Back End Developers - UberConf 2017
Matt Raible
 
Building and deploying React applications
Building and deploying React applicationsBuilding and deploying React applications
Building and deploying React applications
Astrails
 
Node.js server-side rendering
Node.js server-side renderingNode.js server-side rendering
Node.js server-side rendering
The Software House
 
Building Web Apps with Express
Building Web Apps with ExpressBuilding Web Apps with Express
Building Web Apps with Express
Aaron Stannard
 
Birt Integration
Birt IntegrationBirt Integration
Birt Integration
micajblock
 
Introduction to ASP.NET
Introduction to ASP.NETIntroduction to ASP.NET
Introduction to ASP.NET
Peter Gfader
 
Front End Development for Back End Developers - vJUG24 2017
Front End Development for Back End Developers - vJUG24 2017Front End Development for Back End Developers - vJUG24 2017
Front End Development for Back End Developers - vJUG24 2017
Matt Raible
 
A Gentle Introduction to Angular Schematics - Angular SF 2019
A Gentle Introduction to Angular Schematics - Angular SF 2019A Gentle Introduction to Angular Schematics - Angular SF 2019
A Gentle Introduction to Angular Schematics - Angular SF 2019
Matt Raible
 
Google app engine by example
Google app engine by exampleGoogle app engine by example
Google app engine by example
Alexander Zamkovyi
 
Front End Development for Back End Java Developers - Jfokus 2020
Front End Development for Back End Java Developers - Jfokus 2020Front End Development for Back End Java Developers - Jfokus 2020
Front End Development for Back End Java Developers - Jfokus 2020
Matt Raible
 

Similar to Maciej Treder ''Angular Universal - a medicine for the Angular + SEO/CDN issues'' (20)

Maciej Treder "Server-side rendering with Angular—be faster and more SEO, CDN...
Maciej Treder "Server-side rendering with Angular—be faster and more SEO, CDN...Maciej Treder "Server-side rendering with Angular—be faster and more SEO, CDN...
Maciej Treder "Server-side rendering with Angular—be faster and more SEO, CDN...
 
Angular server side rendering - Strategies & Technics
Angular server side rendering - Strategies & Technics Angular server side rendering - Strategies & Technics
Angular server side rendering - Strategies & Technics
 
Reactive application using meteor
Reactive application using meteorReactive application using meteor
Reactive application using meteor
 
Reactive Application Using METEOR
Reactive Application Using METEORReactive Application Using METEOR
Reactive Application Using METEOR
 
React loadable
React loadableReact loadable
React loadable
 
Rest web service_with_spring_hateoas
Rest web service_with_spring_hateoasRest web service_with_spring_hateoas
Rest web service_with_spring_hateoas
 
A Story about AngularJS modularization development
A Story about AngularJS modularization developmentA Story about AngularJS modularization development
A Story about AngularJS modularization development
 
using Mithril.js + postgREST to build and consume API's
using Mithril.js + postgREST to build and consume API'susing Mithril.js + postgREST to build and consume API's
using Mithril.js + postgREST to build and consume API's
 
Introducing Rendr: Run your Backbone.js apps on the client and server
Introducing Rendr: Run your Backbone.js apps on the client and serverIntroducing Rendr: Run your Backbone.js apps on the client and server
Introducing Rendr: Run your Backbone.js apps on the client and server
 
Building an angular application -1 ( API: Golang, Database: Postgres) v1.0
Building an angular application -1 ( API: Golang, Database: Postgres) v1.0Building an angular application -1 ( API: Golang, Database: Postgres) v1.0
Building an angular application -1 ( API: Golang, Database: Postgres) v1.0
 
Front End Development for Back End Developers - UberConf 2017
Front End Development for Back End Developers - UberConf 2017Front End Development for Back End Developers - UberConf 2017
Front End Development for Back End Developers - UberConf 2017
 
Building and deploying React applications
Building and deploying React applicationsBuilding and deploying React applications
Building and deploying React applications
 
Node.js server-side rendering
Node.js server-side renderingNode.js server-side rendering
Node.js server-side rendering
 
Building Web Apps with Express
Building Web Apps with ExpressBuilding Web Apps with Express
Building Web Apps with Express
 
Birt Integration
Birt IntegrationBirt Integration
Birt Integration
 
Introduction to ASP.NET
Introduction to ASP.NETIntroduction to ASP.NET
Introduction to ASP.NET
 
Front End Development for Back End Developers - vJUG24 2017
Front End Development for Back End Developers - vJUG24 2017Front End Development for Back End Developers - vJUG24 2017
Front End Development for Back End Developers - vJUG24 2017
 
A Gentle Introduction to Angular Schematics - Angular SF 2019
A Gentle Introduction to Angular Schematics - Angular SF 2019A Gentle Introduction to Angular Schematics - Angular SF 2019
A Gentle Introduction to Angular Schematics - Angular SF 2019
 
Google app engine by example
Google app engine by exampleGoogle app engine by example
Google app engine by example
 
Front End Development for Back End Java Developers - Jfokus 2020
Front End Development for Back End Java Developers - Jfokus 2020Front End Development for Back End Java Developers - Jfokus 2020
Front End Development for Back End Java Developers - Jfokus 2020
 

More from OdessaJS Conf

'GraphQL Schema Design' by Borys Mohyla. OdessaJS'2021
'GraphQL Schema Design' by Borys Mohyla. OdessaJS'2021'GraphQL Schema Design' by Borys Mohyla. OdessaJS'2021
'GraphQL Schema Design' by Borys Mohyla. OdessaJS'2021
OdessaJS Conf
 
'How i came up with my talk' by Yurii Artiukh. OdessaJS'2021
'How i came up with my talk' by Yurii Artiukh. OdessaJS'2021'How i came up with my talk' by Yurii Artiukh. OdessaJS'2021
'How i came up with my talk' by Yurii Artiukh. OdessaJS'2021
OdessaJS Conf
 
"Is there life in react without redux" by Babich Sergiy. OdessaJS'2021
"Is there life in react without redux" by Babich Sergiy. OdessaJS'2021"Is there life in react without redux" by Babich Sergiy. OdessaJS'2021
"Is there life in react without redux" by Babich Sergiy. OdessaJS'2021
OdessaJS Conf
 
Олексій Павленко. CONTRACT PROTECTION ON THE FRONTEND SIDE: HOW TO ORGANIZE R...
Олексій Павленко. CONTRACT PROTECTION ON THE FRONTEND SIDE: HOW TO ORGANIZE R...Олексій Павленко. CONTRACT PROTECTION ON THE FRONTEND SIDE: HOW TO ORGANIZE R...
Олексій Павленко. CONTRACT PROTECTION ON THE FRONTEND SIDE: HOW TO ORGANIZE R...
OdessaJS Conf
 
Андрій Троян. Розробка мікросервісів з NestJS. OdessaJS'2021
Андрій Троян. Розробка мікросервісів з NestJS. OdessaJS'2021Андрій Троян. Розробка мікросервісів з NestJS. OdessaJS'2021
Андрій Троян. Розробка мікросервісів з NestJS. OdessaJS'2021
OdessaJS Conf
 
Олексій Гончар "Використання Electron в розробці корпоративної відео-мессeндж...
Олексій Гончар "Використання Electron в розробці корпоративної відео-мессeндж...Олексій Гончар "Використання Electron в розробці корпоративної відео-мессeндж...
Олексій Гончар "Використання Electron в розробці корпоративної відео-мессeндж...
OdessaJS Conf
 
Максим Климишин "Що такого особливого у пропозиції вартості шаблону Micro Fro...
Максим Климишин "Що такого особливого у пропозиції вартості шаблону Micro Fro...Максим Климишин "Що такого особливого у пропозиції вартості шаблону Micro Fro...
Максим Климишин "Що такого особливого у пропозиції вартості шаблону Micro Fro...
OdessaJS Conf
 
Павло Галушко. GOOD CODE MYTHS. OdessaJS'2021
Павло Галушко. GOOD CODE MYTHS. OdessaJS'2021Павло Галушко. GOOD CODE MYTHS. OdessaJS'2021
Павло Галушко. GOOD CODE MYTHS. OdessaJS'2021
OdessaJS Conf
 
"NODEJS & GRAPHQL COOKBOOK. LET’S TALK ABOUT MICRO-SERVICES" by Антон Чередні...
"NODEJS & GRAPHQL COOKBOOK. LET’S TALK ABOUT MICRO-SERVICES" by Антон Чередні..."NODEJS & GRAPHQL COOKBOOK. LET’S TALK ABOUT MICRO-SERVICES" by Антон Чередні...
"NODEJS & GRAPHQL COOKBOOK. LET’S TALK ABOUT MICRO-SERVICES" by Антон Чередні...
OdessaJS Conf
 
'BUILDING ANGULAR APPS WITH NX' by Anastasia Necheporenko
'BUILDING ANGULAR APPS WITH NX' by Anastasia Necheporenko'BUILDING ANGULAR APPS WITH NX' by Anastasia Necheporenko
'BUILDING ANGULAR APPS WITH NX' by Anastasia Necheporenko
OdessaJS Conf
 
'IS THERE JAVASCRIPT ON SWAGGER PLUGINS?' by Dmytro Gusev
'IS THERE JAVASCRIPT ON SWAGGER PLUGINS?' by  Dmytro Gusev'IS THERE JAVASCRIPT ON SWAGGER PLUGINS?' by  Dmytro Gusev
'IS THERE JAVASCRIPT ON SWAGGER PLUGINS?' by Dmytro Gusev
OdessaJS Conf
 
'ETHEREUM SMART CONTRACTS ON JS' by Yaroslav Dvorovenko
'ETHEREUM SMART CONTRACTS ON JS' by Yaroslav Dvorovenko'ETHEREUM SMART CONTRACTS ON JS' by Yaroslav Dvorovenko
'ETHEREUM SMART CONTRACTS ON JS' by Yaroslav Dvorovenko
OdessaJS Conf
 
'GOLANG USAGE IN DEVELOPMENT OF NODE.JS APPLICATIONS (NODE.JS: IN GO WE TRUST...
'GOLANG USAGE IN DEVELOPMENT OF NODE.JS APPLICATIONS (NODE.JS: IN GO WE TRUST...'GOLANG USAGE IN DEVELOPMENT OF NODE.JS APPLICATIONS (NODE.JS: IN GO WE TRUST...
'GOLANG USAGE IN DEVELOPMENT OF NODE.JS APPLICATIONS (NODE.JS: IN GO WE TRUST...
OdessaJS Conf
 
'MICROFRONTENDS WITH REACT' by Liliia Karpenko
 'MICROFRONTENDS WITH REACT' by Liliia Karpenko 'MICROFRONTENDS WITH REACT' by Liliia Karpenko
'MICROFRONTENDS WITH REACT' by Liliia Karpenko
OdessaJS Conf
 
'Web performance metrics' BY ROMAN SAVITSKYI at OdessaJS'2020
'Web performance metrics' BY ROMAN SAVITSKYI at OdessaJS'2020'Web performance metrics' BY ROMAN SAVITSKYI at OdessaJS'2020
'Web performance metrics' BY ROMAN SAVITSKYI at OdessaJS'2020
OdessaJS Conf
 
'STORY OF ANOTHER ANIMATION' by YURII ARTYUKH at OdessaJS'2020
'STORY OF ANOTHER ANIMATION' by  YURII ARTYUKH at OdessaJS'2020'STORY OF ANOTHER ANIMATION' by  YURII ARTYUKH at OdessaJS'2020
'STORY OF ANOTHER ANIMATION' by YURII ARTYUKH at OdessaJS'2020
OdessaJS Conf
 
'JavaScript was invented in Odessa' by DMITRIY GUSEV at OdessaJS'2020
'JavaScript was invented in Odessa' by DMITRIY GUSEV at OdessaJS'2020'JavaScript was invented in Odessa' by DMITRIY GUSEV at OdessaJS'2020
'JavaScript was invented in Odessa' by DMITRIY GUSEV at OdessaJS'2020
OdessaJS Conf
 
'Why svelte' by BORYS MOHYLA at OdessaJS'2020
'Why svelte' by BORYS MOHYLA at OdessaJS'2020'Why svelte' by BORYS MOHYLA at OdessaJS'2020
'Why svelte' by BORYS MOHYLA at OdessaJS'2020
OdessaJS Conf
 
'Effective node.js development' by Viktor Turskyi at OdessaJS'2020
'Effective node.js development' by Viktor Turskyi at OdessaJS'2020'Effective node.js development' by Viktor Turskyi at OdessaJS'2020
'Effective node.js development' by Viktor Turskyi at OdessaJS'2020
OdessaJS Conf
 
'Tensorflow.js in real life' by Pavlo Galushko at OdessaJS'2020
'Tensorflow.js in real life' by Pavlo Galushko at OdessaJS'2020'Tensorflow.js in real life' by Pavlo Galushko at OdessaJS'2020
'Tensorflow.js in real life' by Pavlo Galushko at OdessaJS'2020
OdessaJS Conf
 

More from OdessaJS Conf (20)

'GraphQL Schema Design' by Borys Mohyla. OdessaJS'2021
'GraphQL Schema Design' by Borys Mohyla. OdessaJS'2021'GraphQL Schema Design' by Borys Mohyla. OdessaJS'2021
'GraphQL Schema Design' by Borys Mohyla. OdessaJS'2021
 
'How i came up with my talk' by Yurii Artiukh. OdessaJS'2021
'How i came up with my talk' by Yurii Artiukh. OdessaJS'2021'How i came up with my talk' by Yurii Artiukh. OdessaJS'2021
'How i came up with my talk' by Yurii Artiukh. OdessaJS'2021
 
"Is there life in react without redux" by Babich Sergiy. OdessaJS'2021
"Is there life in react without redux" by Babich Sergiy. OdessaJS'2021"Is there life in react without redux" by Babich Sergiy. OdessaJS'2021
"Is there life in react without redux" by Babich Sergiy. OdessaJS'2021
 
Олексій Павленко. CONTRACT PROTECTION ON THE FRONTEND SIDE: HOW TO ORGANIZE R...
Олексій Павленко. CONTRACT PROTECTION ON THE FRONTEND SIDE: HOW TO ORGANIZE R...Олексій Павленко. CONTRACT PROTECTION ON THE FRONTEND SIDE: HOW TO ORGANIZE R...
Олексій Павленко. CONTRACT PROTECTION ON THE FRONTEND SIDE: HOW TO ORGANIZE R...
 
Андрій Троян. Розробка мікросервісів з NestJS. OdessaJS'2021
Андрій Троян. Розробка мікросервісів з NestJS. OdessaJS'2021Андрій Троян. Розробка мікросервісів з NestJS. OdessaJS'2021
Андрій Троян. Розробка мікросервісів з NestJS. OdessaJS'2021
 
Олексій Гончар "Використання Electron в розробці корпоративної відео-мессeндж...
Олексій Гончар "Використання Electron в розробці корпоративної відео-мессeндж...Олексій Гончар "Використання Electron в розробці корпоративної відео-мессeндж...
Олексій Гончар "Використання Electron в розробці корпоративної відео-мессeндж...
 
Максим Климишин "Що такого особливого у пропозиції вартості шаблону Micro Fro...
Максим Климишин "Що такого особливого у пропозиції вартості шаблону Micro Fro...Максим Климишин "Що такого особливого у пропозиції вартості шаблону Micro Fro...
Максим Климишин "Що такого особливого у пропозиції вартості шаблону Micro Fro...
 
Павло Галушко. GOOD CODE MYTHS. OdessaJS'2021
Павло Галушко. GOOD CODE MYTHS. OdessaJS'2021Павло Галушко. GOOD CODE MYTHS. OdessaJS'2021
Павло Галушко. GOOD CODE MYTHS. OdessaJS'2021
 
"NODEJS & GRAPHQL COOKBOOK. LET’S TALK ABOUT MICRO-SERVICES" by Антон Чередні...
"NODEJS & GRAPHQL COOKBOOK. LET’S TALK ABOUT MICRO-SERVICES" by Антон Чередні..."NODEJS & GRAPHQL COOKBOOK. LET’S TALK ABOUT MICRO-SERVICES" by Антон Чередні...
"NODEJS & GRAPHQL COOKBOOK. LET’S TALK ABOUT MICRO-SERVICES" by Антон Чередні...
 
'BUILDING ANGULAR APPS WITH NX' by Anastasia Necheporenko
'BUILDING ANGULAR APPS WITH NX' by Anastasia Necheporenko'BUILDING ANGULAR APPS WITH NX' by Anastasia Necheporenko
'BUILDING ANGULAR APPS WITH NX' by Anastasia Necheporenko
 
'IS THERE JAVASCRIPT ON SWAGGER PLUGINS?' by Dmytro Gusev
'IS THERE JAVASCRIPT ON SWAGGER PLUGINS?' by  Dmytro Gusev'IS THERE JAVASCRIPT ON SWAGGER PLUGINS?' by  Dmytro Gusev
'IS THERE JAVASCRIPT ON SWAGGER PLUGINS?' by Dmytro Gusev
 
'ETHEREUM SMART CONTRACTS ON JS' by Yaroslav Dvorovenko
'ETHEREUM SMART CONTRACTS ON JS' by Yaroslav Dvorovenko'ETHEREUM SMART CONTRACTS ON JS' by Yaroslav Dvorovenko
'ETHEREUM SMART CONTRACTS ON JS' by Yaroslav Dvorovenko
 
'GOLANG USAGE IN DEVELOPMENT OF NODE.JS APPLICATIONS (NODE.JS: IN GO WE TRUST...
'GOLANG USAGE IN DEVELOPMENT OF NODE.JS APPLICATIONS (NODE.JS: IN GO WE TRUST...'GOLANG USAGE IN DEVELOPMENT OF NODE.JS APPLICATIONS (NODE.JS: IN GO WE TRUST...
'GOLANG USAGE IN DEVELOPMENT OF NODE.JS APPLICATIONS (NODE.JS: IN GO WE TRUST...
 
'MICROFRONTENDS WITH REACT' by Liliia Karpenko
 'MICROFRONTENDS WITH REACT' by Liliia Karpenko 'MICROFRONTENDS WITH REACT' by Liliia Karpenko
'MICROFRONTENDS WITH REACT' by Liliia Karpenko
 
'Web performance metrics' BY ROMAN SAVITSKYI at OdessaJS'2020
'Web performance metrics' BY ROMAN SAVITSKYI at OdessaJS'2020'Web performance metrics' BY ROMAN SAVITSKYI at OdessaJS'2020
'Web performance metrics' BY ROMAN SAVITSKYI at OdessaJS'2020
 
'STORY OF ANOTHER ANIMATION' by YURII ARTYUKH at OdessaJS'2020
'STORY OF ANOTHER ANIMATION' by  YURII ARTYUKH at OdessaJS'2020'STORY OF ANOTHER ANIMATION' by  YURII ARTYUKH at OdessaJS'2020
'STORY OF ANOTHER ANIMATION' by YURII ARTYUKH at OdessaJS'2020
 
'JavaScript was invented in Odessa' by DMITRIY GUSEV at OdessaJS'2020
'JavaScript was invented in Odessa' by DMITRIY GUSEV at OdessaJS'2020'JavaScript was invented in Odessa' by DMITRIY GUSEV at OdessaJS'2020
'JavaScript was invented in Odessa' by DMITRIY GUSEV at OdessaJS'2020
 
'Why svelte' by BORYS MOHYLA at OdessaJS'2020
'Why svelte' by BORYS MOHYLA at OdessaJS'2020'Why svelte' by BORYS MOHYLA at OdessaJS'2020
'Why svelte' by BORYS MOHYLA at OdessaJS'2020
 
'Effective node.js development' by Viktor Turskyi at OdessaJS'2020
'Effective node.js development' by Viktor Turskyi at OdessaJS'2020'Effective node.js development' by Viktor Turskyi at OdessaJS'2020
'Effective node.js development' by Viktor Turskyi at OdessaJS'2020
 
'Tensorflow.js in real life' by Pavlo Galushko at OdessaJS'2020
'Tensorflow.js in real life' by Pavlo Galushko at OdessaJS'2020'Tensorflow.js in real life' by Pavlo Galushko at OdessaJS'2020
'Tensorflow.js in real life' by Pavlo Galushko at OdessaJS'2020
 

Recently uploaded

From Siloed Products to Connected Ecosystem: Building a Sustainable and Scala...
From Siloed Products to Connected Ecosystem: Building a Sustainable and Scala...From Siloed Products to Connected Ecosystem: Building a Sustainable and Scala...
From Siloed Products to Connected Ecosystem: Building a Sustainable and Scala...
Product School
 
Designing Great Products: The Power of Design and Leadership by Chief Designe...
Designing Great Products: The Power of Design and Leadership by Chief Designe...Designing Great Products: The Power of Design and Leadership by Chief Designe...
Designing Great Products: The Power of Design and Leadership by Chief Designe...
Product School
 
Empowering NextGen Mobility via Large Action Model Infrastructure (LAMI): pav...
Empowering NextGen Mobility via Large Action Model Infrastructure (LAMI): pav...Empowering NextGen Mobility via Large Action Model Infrastructure (LAMI): pav...
Empowering NextGen Mobility via Large Action Model Infrastructure (LAMI): pav...
Thierry Lestable
 
Search and Society: Reimagining Information Access for Radical Futures
Search and Society: Reimagining Information Access for Radical FuturesSearch and Society: Reimagining Information Access for Radical Futures
Search and Society: Reimagining Information Access for Radical Futures
Bhaskar Mitra
 
De-mystifying Zero to One: Design Informed Techniques for Greenfield Innovati...
De-mystifying Zero to One: Design Informed Techniques for Greenfield Innovati...De-mystifying Zero to One: Design Informed Techniques for Greenfield Innovati...
De-mystifying Zero to One: Design Informed Techniques for Greenfield Innovati...
Product School
 
Connector Corner: Automate dynamic content and events by pushing a button
Connector Corner: Automate dynamic content and events by pushing a buttonConnector Corner: Automate dynamic content and events by pushing a button
Connector Corner: Automate dynamic content and events by pushing a button
DianaGray10
 
Mission to Decommission: Importance of Decommissioning Products to Increase E...
Mission to Decommission: Importance of Decommissioning Products to Increase E...Mission to Decommission: Importance of Decommissioning Products to Increase E...
Mission to Decommission: Importance of Decommissioning Products to Increase E...
Product School
 
FIDO Alliance Osaka Seminar: Overview.pdf
FIDO Alliance Osaka Seminar: Overview.pdfFIDO Alliance Osaka Seminar: Overview.pdf
FIDO Alliance Osaka Seminar: Overview.pdf
FIDO Alliance
 
Epistemic Interaction - tuning interfaces to provide information for AI support
Epistemic Interaction - tuning interfaces to provide information for AI supportEpistemic Interaction - tuning interfaces to provide information for AI support
Epistemic Interaction - tuning interfaces to provide information for AI support
Alan Dix
 
Bits & Pixels using AI for Good.........
Bits & Pixels using AI for Good.........Bits & Pixels using AI for Good.........
Bits & Pixels using AI for Good.........
Alison B. Lowndes
 
How world-class product teams are winning in the AI era by CEO and Founder, P...
How world-class product teams are winning in the AI era by CEO and Founder, P...How world-class product teams are winning in the AI era by CEO and Founder, P...
How world-class product teams are winning in the AI era by CEO and Founder, P...
Product School
 
Assuring Contact Center Experiences for Your Customers With ThousandEyes
Assuring Contact Center Experiences for Your Customers With ThousandEyesAssuring Contact Center Experiences for Your Customers With ThousandEyes
Assuring Contact Center Experiences for Your Customers With ThousandEyes
ThousandEyes
 
UiPath Test Automation using UiPath Test Suite series, part 3
UiPath Test Automation using UiPath Test Suite series, part 3UiPath Test Automation using UiPath Test Suite series, part 3
UiPath Test Automation using UiPath Test Suite series, part 3
DianaGray10
 
GenAISummit 2024 May 28 Sri Ambati Keynote: AGI Belongs to The Community in O...
GenAISummit 2024 May 28 Sri Ambati Keynote: AGI Belongs to The Community in O...GenAISummit 2024 May 28 Sri Ambati Keynote: AGI Belongs to The Community in O...
GenAISummit 2024 May 28 Sri Ambati Keynote: AGI Belongs to The Community in O...
Sri Ambati
 
GDG Cloud Southlake #33: Boule & Rebala: Effective AppSec in SDLC using Deplo...
GDG Cloud Southlake #33: Boule & Rebala: Effective AppSec in SDLC using Deplo...GDG Cloud Southlake #33: Boule & Rebala: Effective AppSec in SDLC using Deplo...
GDG Cloud Southlake #33: Boule & Rebala: Effective AppSec in SDLC using Deplo...
James Anderson
 
FIDO Alliance Osaka Seminar: The WebAuthn API and Discoverable Credentials.pdf
FIDO Alliance Osaka Seminar: The WebAuthn API and Discoverable Credentials.pdfFIDO Alliance Osaka Seminar: The WebAuthn API and Discoverable Credentials.pdf
FIDO Alliance Osaka Seminar: The WebAuthn API and Discoverable Credentials.pdf
FIDO Alliance
 
State of ICS and IoT Cyber Threat Landscape Report 2024 preview
State of ICS and IoT Cyber Threat Landscape Report 2024 previewState of ICS and IoT Cyber Threat Landscape Report 2024 preview
State of ICS and IoT Cyber Threat Landscape Report 2024 preview
Prayukth K V
 
Neuro-symbolic is not enough, we need neuro-*semantic*
Neuro-symbolic is not enough, we need neuro-*semantic*Neuro-symbolic is not enough, we need neuro-*semantic*
Neuro-symbolic is not enough, we need neuro-*semantic*
Frank van Harmelen
 
GraphRAG is All You need? LLM & Knowledge Graph
GraphRAG is All You need? LLM & Knowledge GraphGraphRAG is All You need? LLM & Knowledge Graph
GraphRAG is All You need? LLM & Knowledge Graph
Guy Korland
 
IOS-PENTESTING-BEGINNERS-PRACTICAL-GUIDE-.pptx
IOS-PENTESTING-BEGINNERS-PRACTICAL-GUIDE-.pptxIOS-PENTESTING-BEGINNERS-PRACTICAL-GUIDE-.pptx
IOS-PENTESTING-BEGINNERS-PRACTICAL-GUIDE-.pptx
Abida Shariff
 

Recently uploaded (20)

From Siloed Products to Connected Ecosystem: Building a Sustainable and Scala...
From Siloed Products to Connected Ecosystem: Building a Sustainable and Scala...From Siloed Products to Connected Ecosystem: Building a Sustainable and Scala...
From Siloed Products to Connected Ecosystem: Building a Sustainable and Scala...
 
Designing Great Products: The Power of Design and Leadership by Chief Designe...
Designing Great Products: The Power of Design and Leadership by Chief Designe...Designing Great Products: The Power of Design and Leadership by Chief Designe...
Designing Great Products: The Power of Design and Leadership by Chief Designe...
 
Empowering NextGen Mobility via Large Action Model Infrastructure (LAMI): pav...
Empowering NextGen Mobility via Large Action Model Infrastructure (LAMI): pav...Empowering NextGen Mobility via Large Action Model Infrastructure (LAMI): pav...
Empowering NextGen Mobility via Large Action Model Infrastructure (LAMI): pav...
 
Search and Society: Reimagining Information Access for Radical Futures
Search and Society: Reimagining Information Access for Radical FuturesSearch and Society: Reimagining Information Access for Radical Futures
Search and Society: Reimagining Information Access for Radical Futures
 
De-mystifying Zero to One: Design Informed Techniques for Greenfield Innovati...
De-mystifying Zero to One: Design Informed Techniques for Greenfield Innovati...De-mystifying Zero to One: Design Informed Techniques for Greenfield Innovati...
De-mystifying Zero to One: Design Informed Techniques for Greenfield Innovati...
 
Connector Corner: Automate dynamic content and events by pushing a button
Connector Corner: Automate dynamic content and events by pushing a buttonConnector Corner: Automate dynamic content and events by pushing a button
Connector Corner: Automate dynamic content and events by pushing a button
 
Mission to Decommission: Importance of Decommissioning Products to Increase E...
Mission to Decommission: Importance of Decommissioning Products to Increase E...Mission to Decommission: Importance of Decommissioning Products to Increase E...
Mission to Decommission: Importance of Decommissioning Products to Increase E...
 
FIDO Alliance Osaka Seminar: Overview.pdf
FIDO Alliance Osaka Seminar: Overview.pdfFIDO Alliance Osaka Seminar: Overview.pdf
FIDO Alliance Osaka Seminar: Overview.pdf
 
Epistemic Interaction - tuning interfaces to provide information for AI support
Epistemic Interaction - tuning interfaces to provide information for AI supportEpistemic Interaction - tuning interfaces to provide information for AI support
Epistemic Interaction - tuning interfaces to provide information for AI support
 
Bits & Pixels using AI for Good.........
Bits & Pixels using AI for Good.........Bits & Pixels using AI for Good.........
Bits & Pixels using AI for Good.........
 
How world-class product teams are winning in the AI era by CEO and Founder, P...
How world-class product teams are winning in the AI era by CEO and Founder, P...How world-class product teams are winning in the AI era by CEO and Founder, P...
How world-class product teams are winning in the AI era by CEO and Founder, P...
 
Assuring Contact Center Experiences for Your Customers With ThousandEyes
Assuring Contact Center Experiences for Your Customers With ThousandEyesAssuring Contact Center Experiences for Your Customers With ThousandEyes
Assuring Contact Center Experiences for Your Customers With ThousandEyes
 
UiPath Test Automation using UiPath Test Suite series, part 3
UiPath Test Automation using UiPath Test Suite series, part 3UiPath Test Automation using UiPath Test Suite series, part 3
UiPath Test Automation using UiPath Test Suite series, part 3
 
GenAISummit 2024 May 28 Sri Ambati Keynote: AGI Belongs to The Community in O...
GenAISummit 2024 May 28 Sri Ambati Keynote: AGI Belongs to The Community in O...GenAISummit 2024 May 28 Sri Ambati Keynote: AGI Belongs to The Community in O...
GenAISummit 2024 May 28 Sri Ambati Keynote: AGI Belongs to The Community in O...
 
GDG Cloud Southlake #33: Boule & Rebala: Effective AppSec in SDLC using Deplo...
GDG Cloud Southlake #33: Boule & Rebala: Effective AppSec in SDLC using Deplo...GDG Cloud Southlake #33: Boule & Rebala: Effective AppSec in SDLC using Deplo...
GDG Cloud Southlake #33: Boule & Rebala: Effective AppSec in SDLC using Deplo...
 
FIDO Alliance Osaka Seminar: The WebAuthn API and Discoverable Credentials.pdf
FIDO Alliance Osaka Seminar: The WebAuthn API and Discoverable Credentials.pdfFIDO Alliance Osaka Seminar: The WebAuthn API and Discoverable Credentials.pdf
FIDO Alliance Osaka Seminar: The WebAuthn API and Discoverable Credentials.pdf
 
State of ICS and IoT Cyber Threat Landscape Report 2024 preview
State of ICS and IoT Cyber Threat Landscape Report 2024 previewState of ICS and IoT Cyber Threat Landscape Report 2024 preview
State of ICS and IoT Cyber Threat Landscape Report 2024 preview
 
Neuro-symbolic is not enough, we need neuro-*semantic*
Neuro-symbolic is not enough, we need neuro-*semantic*Neuro-symbolic is not enough, we need neuro-*semantic*
Neuro-symbolic is not enough, we need neuro-*semantic*
 
GraphRAG is All You need? LLM & Knowledge Graph
GraphRAG is All You need? LLM & Knowledge GraphGraphRAG is All You need? LLM & Knowledge Graph
GraphRAG is All You need? LLM & Knowledge Graph
 
IOS-PENTESTING-BEGINNERS-PRACTICAL-GUIDE-.pptx
IOS-PENTESTING-BEGINNERS-PRACTICAL-GUIDE-.pptxIOS-PENTESTING-BEGINNERS-PRACTICAL-GUIDE-.pptx
IOS-PENTESTING-BEGINNERS-PRACTICAL-GUIDE-.pptx
 

Maciej Treder ''Angular Universal - a medicine for the Angular + SEO/CDN issues''

  • 1. Summer. Sea. JavaScript. Angular Universal A medicine for the SEO/CDN issues Maciej Treder @maciejtreder
  • 2. Outline • SPA pitfall • Server-side rendering • Server vs. Browser • API optimization • Deployment • Prerendering & Summary SPA pitfall SSR Server vs. Browser APIs Deploy Summary
  • 3. SPA pitfall ng build • Ahead of Time compilation
  • 4. —prod flag • Ahead of Time compilation • Minified • Tree-shaked SPA pitfall
  • 5. ng build vs. —prod SPA pitfall
  • 6. SPA problem <IfModule mod_rewrite.c> RewriteEngine On RewriteBase / RewriteRule ^index.html$ - [L] RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule . index.html [L] </IfModule> .htaccess SPA pitfall
  • 7. SPA Problem GET / GET /anotherPage index.html GET /subpage GET /contact GET /home SPA pitfall
  • 8. SPA Problem GET / GET /anotherPage SPA pitfall
  • 11. Server Side Rendering GET / GET /anotherPage SSR
  • 12. Is it worth? curl localhost:8080 <!DOCTYPE html><html lang="en"><head> <meta charset="utf-8"> <title>SomeProject</title> <base href="/"> <meta name="viewport" content="width=device-width, initial-scale=1"> <link rel="icon" type="image/x-icon" href="favicon.ico"> <link rel="stylesheet" href="styles.3ff695c00d717f2d2a11.css"><style ng-transition="app-root"> /*# sourceMappingURL=data:application/ json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IiIsImZpbGUiOiJzcmMvYXBwL2FwcC5jb21wb25lbnQuY3NzIn0= */</ style></head> <body> <script type="text/javascript" src="runtime.26209474bfa8dc87a77c.js"></script><script type="text/javascript" src="es2015- polyfills.c5dd28b362270c767b34.js" nomodule=""></script><script type="text/javascript" src="polyfills.8bbb231b43165d65d357.js"></ script><script type="text/javascript" src="main.8a9128130a3a38dd7ee5.js"></script> <script id="app-root-state" type="application/json">{}</script></body></html> <app-root _nghost-sc0="" ng-version="7.2.9"><div _ngcontent-sc0="" style="text-align:center"><h1 _ngcontent-sc0=""> Welcome to someProject! </h1><img _ngcontent-sc0="" alt="Angular Logo" src="data:image/ svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTAgMjUwIj4KICAgIDxwYXRoIGZpbGw9IiNERD AwMzEiIGQ9Ik0xMjUgMzBMMzEuOSA2My4ybDE0LjIgMTIzLjFMMTI1IDIzMGw3OC45LTQzLjcgMTQuMi0xMjMuMXoiIC8+CiAgICA8cGF0aCBmaWxsPSIjQzMwMDJ GIiBkPSJNMTI1IDMwdjIyLjItLjFWMjMwbDc4LjktNDMuNyAxNC4yLTEyMy4xTDEyNSAzMHoiIC8+CiAgICA8cGF0aCAgZmlsbD0iI0ZGRkZGRiIgZD0iTTEyNSA1 Mi4xTDY2LjggMTgyLjZoMjEuN2wxMS43LTI5LjJoNDkuNGwxMS43IDI5LjJIMTgzTDEyNSA1Mi4xem0xNyA4My4zaC0zNGwxNy00MC45IDE3IDQwLjl6IiAvPgogI Dwvc3ZnPg==" width="300"></div><h2 _ngcontent-sc0="">Here are some links to help you start: </h2><ul _ngcontent-sc0=""><li _ngcontent-sc0=""><h2 _ngcontent-sc0=""><a _ngcontent-sc0="" href="https://angular.io/tutorial" rel="noopener" target="_blank">Tour of Heroes</a></h2></li><li _ngcontent-sc0=""><h2 _ngcontent-sc0=""><a _ngcontent-sc0="" href="https:// angular.io/cli" rel="noopener" target="_blank">CLI Documentation</a></h2></li><li _ngcontent-sc0=""><h2 _ngcontent-sc0=""><a _ngcontent-sc0="" href="https://blog.angular.io/" rel="noopener" target="_blank">Angular blog</a></h2></li></ul></app-root> SSR
  • 13. SSR
  • 15. —prod vs. universal Load HTML Bootstrap Load HTML Bootstrap SSR NO SSR First meaningful paint First meaningful paint SSR
  • 16. How to start? Official guide https://angular.io/guide/universal ng-toolkit https://github.com/maciejtreder/ng-toolkit SSR
  • 17. ng add @nguniversal/express-engine CREATE src/main.server.ts (220 bytes) CREATE src/app/app.server.module.ts (318 bytes) CREATE src/tsconfig.server.json (219 bytes) CREATE webpack.server.config.js (1360 bytes) CREATE server.ts (1500 bytes) UPDATE package.json (1876 bytes) UPDATE angular.json (4411 bytes) UPDATE src/main.ts (432 bytes) UPDATE src/app/app.module.ts (359 bytes) SSR
  • 18. Adjust your modules app.module.tsapp.server.module.ts @NgModule({ bootstrap: [AppComponent], imports: [ BrowserModule.withServerTransition({appId: 'my-app'}), //other imports ], }) export class AppModule {} import {NgModule} from '@angular/core'; import {ServerModule} from '@angular/platform-server'; import {ModuleMapLoaderModule} from ‘@nguniversal/module-map-ngfactory-loader'; import {AppModule} from './app.module'; import {AppComponent} from './app.component'; @NgModule({ imports: [ AppModule, ServerModule, ModuleMapLoaderModule ], bootstrap: [AppComponent], }) export class AppServerModule {} SSR
  • 19. Adjust your modules Official guide app.module.tsapp.server.module.ts @NgModule({ declarations: [AppComponent], imports: [ //common imports ] }) export class AppModule {} import {NgModule} from '@angular/core'; import {ServerModule} from '@angular/platform-server'; import {ModuleMapLoaderModule} from ‘@nguniversal/module-map-ngfactory-loader'; import {AppModule} from './app.module'; import {AppComponent} from './app.component'; @NgModule({ imports: [ AppModule, ServerModule, ModuleMapLoaderModule, //server specific imports ], bootstrap: [AppComponent], }) export class AppServerModule {} app.browser.module.ts @NgModule({ bootstrap: [AppComponent], imports: [ AppModule, BrowserModule.withServerTransition({appId: 'my-app'}), //browser specific imports ] }) export class AppModule {} //browser specific imports //server specific imports SSR
  • 20. ng add @ng-toolkit/universal CREATE local.js (248 bytes) CREATE server.ts (1546 bytes) CREATE webpack.server.config.js (1214 bytes) CREATE src/main.server.ts (249 bytes) CREATE src/tsconfig.server.json (485 bytes) CREATE src/app/app.browser.module.ts (395 bytes) CREATE src/app/app.server.module.ts (788 bytes) CREATE ng-toolkit.json (95 bytes) UPDATE package.json (1840 bytes) UPDATE angular.json (4022 bytes) UPDATE src/app/app.module.ts (417 bytes) UPDATE src/main.ts (447 bytes) SSR
  • 21. And let’s go! • npm run build:prod • npm run server Date: 2018-11-21T13:04:33.302Z Hash: 1a82cb687d2e22b5d12b Time: 10752ms chunk {0} runtime.ec2944dd8b20ec099bf3.js (runtime) 1.41 kB [entry] [rendered] chunk {1} main.09093ffa4ad7f66bc6ff.js (main) 169 kB [initial] [rendered] chunk {2} polyfills.c6871e56cb80756a5498.js (polyfills) 37.5 kB [initial] [rendered] chunk {3} styles.3bb2a9d4949b7dc120a9.css (styles) 0 bytes [initial] [rendered] > my-app@0.0.0 server /Users/mtreder/myApp > node local.js Listening on: http://localhost:8080 SSR
  • 22. Under the hood export const app = express(); app.use(compression()); app.use(cors()); app.use(bodyParser.json()); app.use(bodyParser.urlencoded({ extended: true })); const {AppServerModuleNgFactory, LAZY_MODULE_MAP} = require('./dist/server/main'); app.engine('html', ngExpressEngine({ bootstrap: AppServerModuleNgFactory, providers: [ provideModuleMap(LAZY_MODULE_MAP) ] })); server.ts SSR
  • 23. Under the hood app.get('/*', (req, res) => { res.render('index', {req, res}, (err, html) => { if (html) { res.send(html); } else { console.error(err); res.send(err); } }); }); server.ts SSR
  • 24. Under the hood app.set('view engine', 'html'); app.set('views', './dist/browser'); app.get('*.*', express.static('./dist/browser', { maxAge: '1y' })); server.ts SSR
  • 25. Server Side Rendering GET / GET /anotherPage SSR
  • 27. Browser vs. Server • document • window • navigator • file system • request Server vs. Browser
  • 28. Browser vs. Server public ngOnInit(): void { console.log(window.navigator.language); } Listening on: http://localhost:8080 ERROR ReferenceError: window is not defined at AppComponent.module.exports../src/app/app.component.ts.AppComponent.ngOnInit (/Users/mtreder/myApp/dist/server.js:118857:21) at checkAndUpdateDirectiveInline (/Users/mtreder/myApp/dist/server.js:19504:19) at checkAndUpdateNodeInline (/Users/mtreder/myApp/dist/server.js:20768:20) at checkAndUpdateNode (/Users/mtreder/myApp/dist/server.js:20730:16) at prodCheckAndUpdateNode (/Users/mtreder/myApp/dist/server.js:21271:5) at Object.updateDirectives (/Users/mtreder/myApp/dist/server.js:118833:264) at Object.updateDirectives (/Users/mtreder/myApp/dist/server.js:21059:72) at Object.checkAndUpdateView (/Users/mtreder/myApp/dist/server.js:20712:14) at ViewRef_.module.exports.ViewRef_.detectChanges (/Users/mtreder/myApp/dist/ server.js:19093:22) at /Users/mtreder/myApp/dist/server.js:15755:63 Server vs. Browser
  • 29. server? browser? import { Component, Inject, PLATFORM_ID, OnInit } from '@angular/core'; import { isPlatformBrowser, isPlatformServer } from '@angular/common'; @Component({ selector: 'home-view', templateUrl: './home.component.html' }) export class HomeComponent implements OnInit { constructor( private platformId) {} public ngOnInit(): void { if ( ) { console.log('I am executed in the browser!’); // window.url can be reached here } if (isPlatformServer(this.platformId)) { console.log('I am executed in the server!’); // window.url CAN’T be reached here } } } Server vs. Browser
  • 30. Wrapper Service • Determine if we are in the browser or server • Retrieve window or request object • Create ‘mock’ window based on request object if necessary Server vs. Browser
  • 31. REQUEST import { Component, OnInit, Inject, PLATFORM_ID, Optional } from ‘@angular/core’; import { REQUEST } from '@nguniversal/express-engine/tokens'; import { isPlatformServer } from '@angular/common'; @Component({ selector: 'app-root', templateUrl: './app.component.html', }) export class AppComponent implements OnInit { constructor( @Inject(REQUEST) private request: any, @Inject(PLATFORM_ID) private platformId: any) {} public ngOnInit(): void { if (isPlatformServer(this.platformId)) { console.log(this.request.headers); } } } import { REQUEST } from '@nguniversal/express-engine/tokens'; @Optional @Inject(REQUEST) private request: any, console.log(this.request.headers); Server vs. Browser
  • 32. REQUEST Listening on: http://localhost:8080 { host: 'localhost:8080', connection: 'keep-alive', 'cache-control': 'max-age=0', 'upgrade-insecure-requests': '1', 'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36', accept: 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8', 'accept-encoding': 'gzip, deflate, br', 'accept-language': 'en-US,en;q=0.9,ru;q=0.8', 'if-none-match': 'W/"40e-JviTST4QyiABJz2Lg+QxzZtiXv8"' } 'accept-language': 'en-US,en;q=0.9,ru;q=0.8', Server vs. Browser
  • 33. Wrapper Service @Injectable() export class WindowService { private _window: Window; constructor(@Inject(PLATFORM_ID) platformId: any, @Optional @Inject(REQUEST) private request: any ) { if (isPlatformServer(platformId)) { this._window = { navigator: { language: this.request.headers['accept-language'] }, URL: this.request.headers.host + '' + this.request.url }; } else { this._window = window; } } get window(): any { return this._window; } } Server vs. Browser
  • 34. Wrapper Service import { Component , OnInit, Inject} from '@angular/core'; import { WINDOW } from '@ng-toolkit/universal'; export class AppComponent implements OnInit { constructor(@Inject(WINDOW) private window: Window) {} public ngOnInit(): void { console.log(window.navigator.language); } } app.component.ts console.log(this.window.navigator.language); Server vs. Browser
  • 35. @ng-toolkit/universal import { NgtUniversalModule } from '@ng-toolkit/universal'; import { NgModule } from '@angular/core'; @NgModule({ imports:[ NgtUniversalModule ] }) export class AppModule { } app.module.ts Server vs. Browser
  • 36. Server/Browser modules @ngx-translate • i18n module • multiple ways of usage {{‘Welcome to' | translate}} <div [innerHTML]="'HELLO' | translate"></div> { "Welcome to": "Ласкаво просимо в" } uk.json Server vs. Browser
  • 37. Server/Browser modules import { TranslateHttpLoader } from '@ngx-translate/http-loader'; import { TranslateLoader, TranslateModule } from '@ngx-translate/core'; export function HttpLoaderFactory(http: HttpClient) { return new TranslateHttpLoader(http); } @NgModule({ imports:[ TranslateModule.forRoot({ loader: {provide: TranslateLoader, useFactory: HttpLoaderFactory, deps: [httpClient]} }) ] }) export class AppBrowserModule {} export function httpLoaderFactory(http: HttpClient): TranslateLoader { return new TranslateHttpLoader(http); } HttpLoaderFactory, app.browser.module.ts Server vs. Browser
  • 38. Server/Browser modules import { TranslateLoader, TranslateModule } from '@ngx-translate/core'; import { Observable, Observer } from 'rxjs'; import * as fs from 'fs'; export function universalLoader(): TranslateLoader { return { getTranslation: (lang: string) => { return Observable.create((observer: Observer<any>) => { observer.next(JSON.parse(fs.readFileSync(`./dist/assets/i18n/${lang}.json`, 'utf8'))); observer.complete(); }); } } as TranslateLoader; } @NgModule({ imports:[ TranslateModule.forRoot({ loader: {provide: TranslateLoader, useFactory: universalLoader} }) ] }) export class AppServerModule {} export function universalLoader(): TranslateLoader { app.server.module.ts universalLoader } Server vs. Browser
  • 39. Server/Browser modules import { Component, OnInit, Inject } from '@angular/core'; import { TranslateService } from '@ngx-translate/core'; import { WINDOW } from '@ng-toolkit/universal'; @Component({ selector: 'app-root', templateUrl: './app.component.html', }) export class AppComponent implements OnInit { constructor( @Inject(WINDOW) private window: Window, private translateService: TranslateService ) {} public ngOnInit(): void { this.translateService.use(this.window.navigator.language); } } app.component.ts this.translateService.use(this.window.navigator.language); Server vs. Browser
  • 43. DRY(c) Don’t repeat your calls export class AppComponent implements OnInit { public post: Observable<any>; constructor(private httpClient: HttpClient) {} public ngOnInit(): void { this.post = this.httpClient.get('https://jsonplaceholder.typicode.com/posts/1'); } } APIs
  • 46. HttpCacheModule npm install @nguniversal/common import { NgtUniversalModule } from '@ng-toolkit/universal'; import { CommonModule } from '@angular/common'; import { HttpClientModule } from '@angular/common/http'; import { TransferHttpCacheModule } from '@nguniversal/common'; import { NgModule } from '@angular/core'; import { AppComponent } from './app.component'; @NgModule({ declarations: [ AppComponent ], imports:[ CommonModule, NgtUniversalModule, TransferHttpCacheModule, HttpClientModule ] }) export class AppModule { } APIs
  • 47. TransferState • ServerTransferStateModule (@angular/platform-server) • BrowserTransferStateModule (@angular/platform-browser) • get(key, fallbackValue) • set(key, value) • has(key) • remove(key) APIs
  • 48. HTTP_INTERCEPTOR • Provided in the AppModule • Every http request made with HttpClient goes threw it • Used to transform request or response ie: • Adding authentication headers APIs
  • 49. HTTP_INTERCEPTOR @Injectable() export class ServerStateInterceptor implements HttpInterceptor {     constructor(private _transferState: TransferState) {}     intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {         return next.handle(req).pipe(tap(event => {             if (event instanceof HttpResponse) {                 this._transferState.set(makeStateKey(req.url), event.body);             }         }));     } } APIs
  • 50. HTTP_INTERCEPTOR @Injectable() export class BrowserStateInterceptor implements HttpInterceptor {     constructor(private _transferState: TransferState) { }     intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {         if (req.method !== 'GET') {             return next.handle(req);         }         const storedResponse: string = this._transferState.get(makeStateKey(req.url), null);         if (storedResponse) {             const response = new HttpResponse({ body: storedResponse, status: 200 }); this._transferState.remove(makeStateKey(req.url));             return of(response);         }         return next.handle(req);     } } APIs
  • 51. HTTP_INTERCEPTOR import {HTTP_INTERCEPTORS } from '@angular/common/http'; providers: [ { provide: HTTP_INTERCEPTORS, useClass: BrowserStateInterceptor, multi: true, } ] import {HTTP_INTERCEPTORS } from '@angular/common/http'; providers: [ { provide: HTTP_INTERCEPTORS, useClass: ServerStateInterceptor, multi: true, } ] APIs
  • 52. Performance export class RouteResolverService implements Resolve<any> { constructor( private httpClient: HttpClient, @Inject(PLATFORM_ID) private platformId: any ) {} public resolve(): Observable<any> { } } const watchdog: Observable<number> = timer(500); if (isPlatformBrowser(this.platformId)) { return this.httpClient.get<any>('https://jsonplaceholder.typicode.com/posts/1'); } return Observable.create(subject => { this.httpClient.get<any>('https://jsonplaceholder.typicode.com/posts/1') .subscribe(response => { subject.next(response); subject.complete(); }); }) .pipe(takeUntil(watchdog)) watchdog.subscribe(() => { subject.next('timeout'); subject.complete() }) APIs
  • 56. APIs
  • 58. Let’s go Serverless! • Function as a Service • Event-driven • Scalable • Pay for the up-time Deploy
  • 61. Prerender • Generating HTML files at a build time • Can be hosted from traditional hosting (ie. AWS S3) • Doesn’t perform dynamic request • https://github.com/maciejtreder/angular-ssr-prerender • @ng-toolkit/universal + npm run build:prerender Deploy
  • 62. Summary server-side renderingprerenderng build —prod SEO Performance Difficulty SEO + external calls Additional back-end logic Summary