📜 ⬆️ ⬇️

We are friends of Angular with Google (Angular Universal)

Angular friends with Google


Google hates SPA


When we talk about modern online stores, we imagine servers that are hard to understand, rendering thousands of static pages. Moreover, these thousands of rendered pages are one of the reasons why Single Page Applications did not take root in e-commerce. Even the largest e-commerce stores still look like a bunch of static pages. For the user, this is an endless cycle of clicks, expectations and page reloads.



Single-page applications are pleasantly distinguished by the dynamism of user interaction and the more complex UX. But sadly not usually user comfort is sacrificed for SEO optimization. For SEOs, a site on angular is a kind of problem, since it is difficult for search engines to index pages with dynamic content.


Another disadvantage of the SPA is the preview of the site. For example, a user just bought a new TV in our online store and wants to recommend it to his friends in social networks. The preview of the link in the case of Angular will look like this:


site preview

We love JS and Angular. We believe that a cool and convenient UX can be built on this technology stack, and we can solve all the related problems. At some point, we ran into Angular Universal . This is the Angular module for server-side rendering. At first it seemed to us, here it is - the solution! But the joy was premature - and the lack of large projects with its application is proof of that.


As a result, we began to develop components for the online store, using the usual Angular 2 , and waited for Universal be combined with Angular Core . At the moment, the merging of projects has not yet happened, and it is not yet clear when it will happen (or how the final version will be compatible with the current implementation), but Universal itself has already migrated to the Angular repository in github.


Despite these difficulties, our goal remained the same - to create cool web applications on Angular with server rendering for indexing by search engines. As a result, our team has developed more than 20 universal components for the rapid assembly of an online store . How was this eventually achieved?


What is Angular Universal


First of all, let's discuss what Angular Universal is . When we run our application on Angular 2 and open the source code, we’ll see something like this:


 <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Angular 2 app</title> <!-- base url --> <base href="/"> <body> <app></app> </body> </html> 

Front-end frameworks, such as Angular, dynamically add content, styles, and data to a tag.
For an application that is primarily focused on business and is dependent on the indexation of the site by search engines, it is important that our content is indexed and was in the top of Google.

To solve problems with Angular Universal indexing, we are able to perform server-side rendering. Our page will be created on the “backend server” written on Node.Js, .NET , and the user's browser will get a page with all the usual tags in it - , - .
')
In our case, this is excellent, because search engine crawlers are very well prepared for indexing static web pages. We are developing a standard application on Angular , and Universal takes care of server rendering.

Up to this point, everything looks good? Perhaps, but the devil is hidden in the details, as always.
So, we want to share with you the pitfalls that we encountered on our way.


Angular Universal Pitfalls


Do not touch the DOM


When we started testing the components of our store using Universal , we had to spend some time to understand why our server crashes when it starts up without displaying the server page. For example, we have a Session Flow component that tracks user activity during a session (user movement, clicks, referrer, information about the user's device, etc.). After searching for issues on GitHub we realized that Universal does not have a wrapper over the DOM.


DOM .


If you clone this Angular Universal starter and open browser.module.ts you will see that in the providers array the Universal developers provide two boolean :


 providers: [ { provide: 'isBrowser', useValue: isBrowser }, { provide: 'isNode', useValue: isNode }, ... ] 

So, if you want rendering on the server to work, you need to inject these variables into your components and wrap code fragments in the runtime check where the interaction with the DOM takes place directly. For example, like this:


 @Injectable() export class SessionFlow{ private reffererUrl : string; constructor(@Inject('isBrowser') private isBrowser){ if(isBrowser){ this.reffererUrl = document.referrer; } } } 

Universal automatically adds false if it is a server, and true if the browser. Maybe Universal developers will later review this implementation, and we will not have to worry about it.


If you want to actively interact with DOM elements, use Angular API services such as ElementRef , Renderer or ViewContainer .


Correct routing


Since the server reflects our application, we had a problem with the routing.


, Angular, .
Therefore, do not forget to add the same routing on the server as on the client. For example, we have such routes on the client:


 [ { path: '', redirectTo: '/home', pathMatch: 'full' }, { path: 'products', component: ProductsComponent }, { path: 'product/:id', component: ProductComponent} ] 

Then you need to create a server.routes.ts file with an array of server routes. You can not add the root route:


 export const routes: string[] = [ 'products', 'product/:id' ]; 

Finally, add routes to the server:


 import { routes } from './server.routes'; ... other server configuration app.get('/', ngApp); routes.forEach(route => { app.get(`/${route}`, ngApp); app.get(`/${route}/*`, ngApp); }); 

Prerenering start page


One of the most important features of Angular Universal is the pre-rendering. From the Kissmetrics study, it follows that 47% of consumers expect a web page to load in 2 seconds or less. It was very important for us to display the page as quickly as possible. Thus, the pre-rendering at Universal is just about our task. Let's take a closer look at what it is and how to use it.


When a user opens the URL of our store, Universal immediately returns a pre-prepared HTML page with content, and then then begins to download the entire application in the background. Once the application is fully loaded, Universal replaces the original page with our application. You ask what will happen if the user starts interacting with the page before downloading the application? Do not worry, the Preboot.js library will record all the events that the user will execute and after downloading the application will execute them already in the application.


To enable pre-rendering, simply add the preboot: true server preboot: true :


 res.render('index', { req, res, preboot: true, baseUrl: '/', requestUrl: req.originalUrl, originUrl: `http://localhost:${ app.get('port') }` } ); 

Adding Meta Tags


In the case of an SEO site, adding meta tags is very important. For example, we have a Product component that represents a specific product page. It should asynchronously receive product data from the database and, based on this data, add meta tags and other special information for the search robot.


The Angular Universal team created the angular2-meta service to easily manipulate meta tags. Insert a meta-service into your component and a few lines of code add meta tags to your page:


 import { Meta, MetaDefinition } from './../../angular2-meta'; @Component({ selector: 'main-page', templateUrl: './main-page.component.html', styleUrls: ['./main-page.component.scss'] }) export class MainPageComponent { constructor(private metaService: Meta){ const name: MetaDefinition = { name: 'application-name', content: 'application-content' }; metaService.addTags(name); } } 

In the next version of Angular, this service will be moved to @ angular / platform-server


Data caching


Angular Universal launches your XHR request twice: one on the server and the other on loading the store application.


But why do we need to request data on the server twice? PatricJs created an example of how to make an Http request on the server once and cache the received data for the client. View the source code of the sample here . To use it in the Model service and call the get method to make http calls with caching:


 public data; constructor(public model: ModelService) { this.universalInit(); } universalInit() { this.model.get('/data.json').subscribe(data => { this.data = data; }); } 

findings


Server-side rendering with Angular Universal allows us to create customer-oriented e-commerce applications no longer worrying about indexing your application. In addition, the “prendering” function allows you to immediately show the site for your client, improving the rendering time (which is a rather unpleasant moment for Angular applications due to the large size of the library itself).

Source: https://habr.com/ru/post/324926/


All Articles