Introduction

Angular 4 applications load the first bare HTML content before pulling the HTML content for the page being loaded using XMLHttpRequest. Single Page Applications are awesome! They load fast and give you a lot of control over how you want your application to run. They are parsed by the browser and thus you have control of the DOM elements and all of that goodness.

However, SPA’s are not SEO friendly off the bat because they need to dynamically change the meta tags and the content using JavaScript and this change is not usually picked up by the search engine’s bot. Since some search bots cannot parse JavaScript when crawling the website, they will only see the first bare content every time.

Although Google says their bots are now capable of rendering JavaScript, it is still a vague area and erring on the side of caution would be a better way to go in this matter. Also, there are other search engines that do not handle JavaScript. This article will cover how to make your Angular 4 application SEO friendly and thus easier to consume by search engines bots.

⚠️ This is not an Angular 4 tutorial and we will not be covering specifics on the Angular 4 framework. We will simply be showing you how to use server-side rendering to serve a fully generated HTML page.

Getting started with making our Angular 4 app SEO friendly

Before we get started on the tutorial, let us build a simple application that we will be using to test our implementation. The application will be a page that lists a bunch of topics on the homepage. We will not be connecting to any data source but instead, we will be hard-coding the data into the component.

Setting up our Angular 4 test application

To set up our Angular application, we will be using ng-cli, and we will be calling the application Blogist.

Creating a new application using ng-cli
To set up we will use ng new command to create the Angular 4 application.

1$ ng new Blogist

⚠️ Note: You will need the latest version of Angular CLI for this application to function correctly. The latest version is 1.3.x as at the time of writing this article.

Next, we will just create a component where we can then add the logic of our code to. We will use the ng g component command for this:

1$ ng g component ./blog/posts

Adding mock data to our PostComponent
For the sake of brevity, we will not be connecting to an external API. Instead, we will just be creating some mock data and using that data in our application.

Open the ./src/app/blog/posts.component.ts file, we will add some logic to the code to make sure it works as we want it to. First, let’s hard code some data to the file. Add a new method called postsData to the component.

1private postsData() {
2        return [
3            {
4                "title": "Making Angular.js realtime with Websockets by marble",
5                "pubDate": "2017-08-23 14:41:52",
6                "link": "https://blog.pusher.com/making-angular-js-realtime-with-pusher/#comment-10372",
7                "guid": "http://blog.pusher.com/?p=682#comment-10372",
8                "author": "marble",
9                "thumbnail": "",
10                "description": "always a big fan of linking to bloggers that I enjoy but dont get a great deal of link enjoy from",
11                "content": "<p>always a big fan of linking to bloggers that I enjoy but dont get a great deal of link enjoy from</p>",
12                "enclosure": [],
13                "categories": []
14            },
15            {
16                "title": "Making Angular.js realtime with Websockets by strapless strap on",
17                "pubDate": "2017-08-23 05:05:08",
18                "link": "https://blog.pusher.com/making-angular-js-realtime-with-pusher/#comment-10371",
19                "guid": "http://blog.pusher.com/?p=682#comment-10371",
20                "author": "strapless strap on",
21                "thumbnail": "",
22                "description": "very couple of internet websites that transpire to be detailed beneath, from our point of view are undoubtedly properly worth checking out",
23                "content": "<p>very couple of internet websites that transpire to be detailed beneath, from our point of view are undoubtedly properly worth checking out</p>",
24                "enclosure": [],
25                "categories": []
26            },
27            {
28                "title": "Making Angular.js realtime with Websockets by bondage restraints",
29                "pubDate": "2017-08-22 17:09:17",
30                "link": "https://blog.pusher.com/making-angular-js-realtime-with-pusher/#comment-10370",
31                "guid": "http://blog.pusher.com/?p=682#comment-10370",
32                "author": "bondage restraints",
33                "thumbnail": "",
34                "description": "very couple of web sites that occur to be in depth below, from our point of view are undoubtedly properly worth checking out",
35                "content": "<p>very couple of web sites that occur to be in depth below, from our point of view are undoubtedly properly worth checking out</p>",
36                "enclosure": [],
37                "categories": []
38            }
39        ];
40    }

To use our mock data created above, replace the constructor method of the PostsComponent class with the code below:

1public posts;
2
3    constructor() {
4        this.posts = this.postsData();
5    }

In the code above we have simply assigned the posts property to the postsData return value, which is our simulated API call response.

Creating a View for our PostsComponent
Now that we have our mock posts data. We will create a view that will display all the posts from our mock data.

Open your view ./app/blog/posts.component.html and enter the code below:

1<div class="jumbotron">
2        <h1>Blogist</h1>
3        <p>This is the best resource for the best web development posts.</p>
4    </div>
5    <div class="row">
6        <div class="col-xs-12 col-md-12">
7            <ul class="list-group">
8                <li class="list-group-item" *ngFor="let post of posts">
9                    <h4>{{post.title}}</h4>
10                </li>
11            </ul>
12        </div>
13    </div>

The code above just takes the posts data and loops through it; each time displaying the title of the post.

Next, open the index.html file and in the <head> replace the contents with the following code. It basically uses Bootstrap and adds a dummy navigation bar:

1<!doctype html>
2    <html lang="en">
3    <head>
4      <meta charset="utf-8">
5      <title>Blogist</title>
6      <base href="/">
7      <meta name="viewport" content="width=device-width, initial-scale=1">
8      <link rel="icon" type="image/x-icon" href="favicon.ico">
9      <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/css/bootstrap.min.css">
10    </head>
11    <body>
12      <nav class="navbar navbar-default">
13        <div class="container-fluid">
14          <div class="navbar-header">
15            <a class="navbar-brand" href="#">Blogist</a>
16          </div>
17          <ul class="nav navbar-nav">
18            <li class="active"><a href="#">Posts</a></li>
19            <li><a href="#">Web Development</a></li>
20            <li><a href="#">Graphic Design</a></li>
21          </ul>
22        </div>
23      </nav>
24      <div class="container">
25        <app-root>Loading...</app-root>
26      </div>
27    </body>
28    </html>

Registering the PostsComponent with our application module
The next thing we will do is register the PostsComponent with our application module.

? Note using the **ng g component** command will automatically register the component in your application module. So you might not need to do it again. If it has been done for you, you can skip this step.

If it has not been registered automatically, open the ./src/app/app.module.ts file and import the PostsComponent:

1import { PostsComponent } from './blog/posts.component';

Then in the NgModule declarations array, add the PostsComponent to the list:

1@NgModule({
2      declarations: [
3        ...
4        PostsComponent,
5      ],
6      ...
7    })

Displaying your Angular application
After registering our Posts component, we will then include it in our ./src/app/app.component.html file so the posts component will be displayed. Open the ./src/app/app.component.html file and add the following code inside it:

1<app-posts></app-posts>

Thats all!

Now when you run ng serve and go to the URL provided to you on your terminal. You should be able to see the page with our posts:

Great, that is exactly what we expected. However, when you view the URL’s source, you will notice that the entire body of the page is missing and just the <app-root>loading…</app-root> part is visible.

This is because of the way Angular works. It would load the parent template first, then after that load itself.

It will then start the DOM manipulation process that will insert the content of each subsequent page in the <app-root> tag.

Hence, when the search engine bot requests this page, it gets the HTML <app-root>Loading…</app-root> above and the content of the page which should have contributed to SEO is now unknown to the engine.

Making the Angular 4 application SEO friendly

Now that we have built the sample application, we can see right off the bat it is is not SEO friendly. So we will be using the Angular universal platform server to pre-render the templates server-side and serve that when the page is loaded.

? The Angular Universal project consists of the base platform API and the surrounding tools that enable developers do server-side rendering (or pre-rendering) of Angular applications.

To start, we will be installing the angular/platform-server package and the angular/animations package. Both are required for the platform server to function correctly. The platform server will be the one to provide the server-side rendering.

Run the command below in your terminal to install the dependencies required for server-side rendering of your Angular application:

1$ npm install --save @angular/platform-server @angular/animations

Once the packages have been installed successfully using NPM, open the ./src/app.modules.ts and make the following modification to the BrowserModule declaration:

1@NgModule({
2      ...
3      imports: [
4        BrowserModule.withServerTransition({appId: 'blogist'})
5      ],
6      ...
7    })

In the above code, we added the withServerTransition method to the BrowserModule and in there we passed the appId that is equal to the name of the application blogist. This addition ‘configures a browser-based application to transition from a server-rendered app, if one is present on the page’.

The next thing we will do is create an application server module. Create a new file ./src/app/app-server.module.ts

1import { NgModule } from '@angular/core';
2    import { AppModule } from './app.module';
3    import { AppComponent } from './app.component';
4    import { ServerModule } from '@angular/platform-server';
5
6    @NgModule({
7      imports: [
8        ServerModule,
9        AppModule,
10      ],
11      bootstrap: [
12        AppComponent
13      ]
14    })
15    export class AppServerModule { }

This is a basic Angular module that will act as our server module. The biggest thing to note in the above is that we import our AppModule into the server module so it will now be a part of the AppServerModule. This module will be where we will bootstrap our application from the server.

Adding title and meta tags to our Angular application
One last thing we will add to the application is support for Meta tags and Title on each page. With Angular universal, doing this is very easy.

Open the ./src/app/blog/posts.component.ts file and inside do the following:

Import Meta and Title from the @angular/platform-browser package:

1import { Meta, Title } from '@angular/platform-browser';

Now in the constructor method, add the following lines of code to it:

1constructor(meta: Meta, title: Title) {
2      this.posts = this.postsData();
3
4      // Sets the <title></title>
5      title.setTitle('Blogist');
6
7      // Sets the <meta> tag for the page
8      meta.addTags([
9        { name: 'author', content: 'Blogist' },
10        { name: 'description', content: 'This is a description.' },
11      ]);
12    }

The code above allows you to set the title for each of the pages you create and they will be pre-rendered using Angular Universal. This allows you finer control over the meta and title of the various pages.

Creating an Express server to make your Angular app SEO friendly
Let’s create an Express server. This will basically allow server-side rendering of the page.

Create a new file in ./src/server.ts and then add the contents below:

1import 'reflect-metadata';
2    import 'zone.js/dist/zone-node';
3    import { renderModuleFactory } from '@angular/platform-server'
4    import { enableProdMode } from '@angular/core'
5    import * as express from 'express';
6    import { join } from 'path';
7    import { readFileSync } from 'fs';
8    import { AppServerModuleNgFactory } from '../dist/ngfactory/src/app/app-server.module.ngfactory'
9
10    enableProdMode()
11
12    const PORT     = process.env.PORT || 4000
13    const DIST_DIR = join(__dirname, '..', 'dist')
14    const app = express();
15    const template = readFileSync(join(DIST_DIR, 'index.html')).toString()
16
17    app.engine('html', (_, options, callback) => {
18      const newOptions = { document: template, url: options.req.url };
19
20      renderModuleFactory(AppServerModuleNgFactory, newOptions)
21        .then(html => callback(null, html))
22    })
23
24    app.set('views', 'src')
25    app.set('view engine', 'html')
26
27    app.get('*.*', express.static(DIST_DIR))
28    app.get('*', (req, res) => {
29      res.render('index', { req })
30    })
31
32    app.listen(PORT, () => {
33      console.log(`App listening on http://localhost:${PORT}!`)
34    });

In this file, we have imported all of the packages we need to run our Express server. Particularly, we import AppServerModuleNgFactory, a file that does not yet exist but will be generated during our build process.

Next, we enableProdMode() which simply enables production mode on our application. We also use [renderModuleFactory](https://angular.io/api/platform-server/renderModuleFactory) to parse the HTML and render the page that was being loaded server-side. Every thing else in the code has to do with Express.

The next thing we want to do now is open our ./src/tsconfig.app.json file and add server.ts to the exclude section of the file.

1"exclude": [
2        "server.ts",
3        ...
4      ]

? The exclude property specifies a list of files to be excluded from compilation.

Also open the ./tsconfig.json file and add the snippet below to the file in the root of the project right below the compilerOptions property:

1...
2        "lib": [
3          "es2016",
4          "dom"
5        ]
6      },
7      "angularCompilerOptions": {
8          "genDir": "./dist/ngfactory",
9          "entryModule": "./src/app/app.module#AppModule"
10      }
11    }

? The genDir is where everything generated will mostly go. The entryModule accepts the path of our main bootstrapped module. The #AppModule at the end of the path is the name of the exported class.

The final step to take is updating the scripts property on our ./package.json file. You should either replace or append this to the keys already in the scripts property:

1{
2      ...
3      "scripts": {
4        "prestart": "ng build --prod && ./node_modules/.bin/ngc",
5        "start": "ts-node src/server.ts"
6      },
7      ...
8    }

We have commands registered to the start and prestart scripts on the ./package.json file. Because we added a pre to the name start, it will run automatically before the start script is called.

Testing our SEO friendly Angular 4 application

Once you have finished making these changes to the application, go to the terminal and run the following command:

1$ npm run start

This will run the prestart script which contains the commands ng build --prod && ./node_modules/.bin/ngc and then run the script start which contains the command ts-node src/server.ts. Once the commands are completed, you should see an output close to this on your terminal:

When you visit the page now you should still see the same output as you saw before. However, when you view the source, you should see the fully rendered HTML. This would be how the search engine bots will see the page.

Conclusion

In this article, we have explored how to make your Angular 4 Single Page Application (SPA) SEO friendly by using Angular 4 Universal. Hopefully, you have learnt a thing or two and the fear of bad SEO optimisation will not stop you from using Angular 4 for your applications any more.

The source code to this application can be found on GitHub. Star and leave feedback, issues or comments. If you also have any questions, feel free to leave them below.

代做工资流水公司江门做工资流水单九江收入证明代开济宁打工资流水账单衡阳背调银行流水揭阳流水账单价格泉州打工资银行流水吉林打转账流水镇江背调工资流水制作漳州个人流水样本德阳银行流水电子版报价上饶房贷收入证明多少钱滁州代开企业对公流水天津工资流水账单代办太原开购房银行流水衡阳车贷工资流水 样本镇江代做车贷银行流水包头开企业对公流水扬州工资流水打印泰安制作企业银行流水包头房贷银行流水 公司包头代办工资代付流水济宁办理房贷收入证明重庆转账银行流水打印珠海办理背调工资流水西安办理银行流水单上海转账流水嘉兴查询车贷银行流水舟山在职证明费用金华办企业流水打印青岛入职流水办理香港通过《维护国家安全条例》两大学生合买彩票中奖一人不认账让美丽中国“从细节出发”19岁小伙救下5人后溺亡 多方发声卫健委通报少年有偿捐血浆16次猝死汪小菲曝离婚始末何赛飞追着代拍打雅江山火三名扑火人员牺牲系谣言男子被猫抓伤后确诊“猫抓病”周杰伦一审败诉网易中国拥有亿元资产的家庭达13.3万户315晚会后胖东来又人满为患了高校汽车撞人致3死16伤 司机系学生张家界的山上“长”满了韩国人?张立群任西安交通大学校长手机成瘾是影响睡眠质量重要因素网友洛杉矶偶遇贾玲“重生之我在北大当嫡校长”单亲妈妈陷入热恋 14岁儿子报警倪萍分享减重40斤方法杨倩无缘巴黎奥运考生莫言也上北大硕士复试名单了许家印被限制高消费奥巴马现身唐宁街 黑色着装引猜测专访95后高颜值猪保姆男孩8年未见母亲被告知被遗忘七年后宇文玥被薅头发捞上岸郑州一火锅店爆改成麻辣烫店西双版纳热带植物园回应蜉蝣大爆发沉迷短剧的人就像掉进了杀猪盘当地回应沈阳致3死车祸车主疑毒驾开除党籍5年后 原水城县长再被查凯特王妃现身!外出购物视频曝光初中生遭15人围殴自卫刺伤3人判无罪事业单位女子向同事水杯投不明物质男子被流浪猫绊倒 投喂者赔24万外国人感慨凌晨的中国很安全路边卖淀粉肠阿姨主动出示声明书胖东来员工每周单休无小长假王树国卸任西安交大校长 师生送别小米汽车超级工厂正式揭幕黑马情侣提车了妈妈回应孩子在校撞护栏坠楼校方回应护栏损坏小学生课间坠楼房客欠租失踪 房东直发愁专家建议不必谈骨泥色变老人退休金被冒领16年 金额超20万西藏招商引资投资者子女可当地高考特朗普无法缴纳4.54亿美元罚金浙江一高校内汽车冲撞行人 多人受伤

代做工资流水公司 XML地图 TXT地图 虚拟主机 SEO 网站制作 网站优化