TL;DR: Server-side rendering of your Angular project!
Angular Universal takes your Angular project and renders it on the server. By running your component and template compilation on the server, user experience faster load times. Using Angular Universal also allows you to run API calls for data fetching on the server for a quick cache hit response. Server-side rendering is great for SEO and Link Previews!
Most of Angular's problems are due to the setup. That is an entirely different article all together and you for most projects you can use a starter repo or cli to avoid any setup issues.
Due to the application being rendered on the server, you lose access to all browser APIs. This includes access to window
object and the DOM. Generally not haven't access to browser APIs isn't an issue as long as you aren't directly querying the DOM using methods like:
document.getElementById()
document.querySelector()
Instead, use Angular directives like viewChild/viewChildren
to get access to the element:
import { Component, ViewChild } from '@angular/core';
@Component({
template: `
<div>
<p #myChild>Pick me!</p>
</div>
`
})
export class PickMe {
@ViewChild('myChild') pickMe;
}
You should never wrap code in a long setTimeout just to avoid slow your rendering on the server
Always make sure any external libraries are Universal ready as they can break on the server if they use any browser API.
Sometimes you need to limit some code from running unless they're in the correct environment. In Angular we have ways to know when your component is being rendered on the server or the browser and only executing code when needed via isPlatformBrowser
or isPlatformServer
:
import { PLATFORM_ID } from '@angular/core';
import { isPlatformBrowser, isPlatformServer } from '@angular/common';
// ...
constructor(@Inject(PLATFORM_ID) private platformId: string) {
// ...
}
ngOnInit() {
if (isPlatformBrowser(this.platformId)) {
// Client only code.
// ...
}
if (isPlatformServer(this.platformId)) {
// Server only code.
// ...
}
}
//...
Don't manipulate the ElementRef.nativeElement
directly. We should use the Renderer2
to ensure that in any environment we're able to change our view.
// ...
constructor(element: ElementRef, renderer: Renderer2) {
renderer.setStyle(element.nativeElement, 'font-size', 'x-large');
}
// ...
HTML is a markup language that gets converted from a string into the DOM in JavaScript. When we query the DOM we are really traversing the JavaScript tree for a Node where we can run even more methods or change values. When we change the value of a single Node we are really changing the properties
const input = document.querySelector('input');
input['value'] = 'newValue';
this is different from attributes which are the initial values of our Node when it's transformed into the DOM
<input value="initialValue">
On the server if we change any of these properties then the values won't be reflected as HTML when we convert the server's version of the DOM into a string. For most of the basic attributes any changes to these properties are reflected correctly for example the src
property.
<img [src]="http://example.com/image.png">
In Angular we are also able to bind to the attributes using attr
<img [attr.src]="http://example.com/image.png">
Keep your directives stateless as much as possible. For stateful directives, you may need to provide an attribute that reflects the corresponding property with an initial string value such as src
in img tag. For our native element the src
attribute is reflected as the src
property of the element type HTMLImageElement
.
Set
{initialNavigation: 'enabled'}
in
RouterModule.forRoot([], {initialNavigation: 'enabled'})
Here https://angular.io/api/router/ExtraOptions#initialNavigation Or here from the definition file:
/**
* @whatItDoes Represents an option to configure when the initial navigation is performed.
*
* @description
* * 'enabled' - the initial navigation starts before the root component is created.
* The bootstrap is blocked until the initial navigation is complete.
* * 'disabled' - the initial navigation is not performed. The location listener is set up before
* the root component gets created.
* * 'legacy_enabled'- the initial navigation starts after the root component has been created.
* The bootstrap is not blocked until the initial navigation is complete. @deprecated
* * 'legacy_disabled'- the initial navigation is not performed. The location listener is set up
* after @deprecated
* the root component gets created.
* * `true` - same as 'legacy_enabled'. @deprecated since v4
* * `false` - same as 'legacy_disabled'. @deprecated since v4
*
* The 'enabled' option should be used for applications unless there is a reason to have
* more control over when the router starts its initial navigation due to some complex
* initialization logic. In this case, 'disabled' should be used.
*
* The 'legacy_enabled' and 'legacy_disabled' should not be used for new applications.
*
* @experimental
*/
export declare type InitialNavigation = true | false | 'enabled' | 'disabled' | 'legacy_enabled' | 'legacy_disabled';
DO NOT USE IT It's that simple. If you need to query the DOM, set a viewChild/viewChildren
. Need to change styles, use ngClass/ngStyle
. Need a reference to an element, use ElementRef
. Jquery always introduces more problems than it fixes. This does not include libraries such as D3.