Building a Search-Engine Optimized PWA with Angular - Part 1
source link: https://www.tuicool.com/articles/hit/IjQNNbR
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
Building a Search-Engine Optimized PWA with Angular — Part 1
No matter how awesome the app you have built is, it won’t really shine with the users unless they can easily see it in their search results. This is why Search Engine Optimization (SEO) is something that any developer needs to have on their To-Do list when they are building their apps.
Back in 2017, surveys showed that about 27% of mobile users ignored the usual app stores and searched for apps on their own through search engines like Google and Bing.
Just open your the app store on your mobile device and search for something as simple as calculator. What you will get is an endless number of apps that somewhat do the same thing — Calculate.
This is why visibility is most important for app developers. Because people searching for apps through search engines will not venture farther that the 1st or 2nd page of the search results.
To understand how search engine optimization works, let’s take a look at one of my other articles, “How To Write Better Code in React”.
If you go to Google and just type “React” in the search bar, then currently you will find this article somewhere on Page 5!
But if you search for something like “better code react”, the article bumps all the way up to Page 1!
wThat’s enough about SEO for now. Now let’s take a look at Progressive Web Apps!
Based on a concept by Google, PWAs are a little different than the traditional mobile app. It is more like a website that is modified to work on mobile devices. The keyword here is “Progressive”, meaning these are apps/websites that can be gradually adjusted to the end device’s technical settings.
In this post, we will build a brand new Progressive Web App using Angular. You will get a hands-on experience of working with things like service workers and Angular Resolvers.
Getting Started
Build an Angular Project
Let’s start by creating a brand new Angular Project in our system. If you haven’t installed the Angular CLI in your device, then type the following command in your command terminal:
$ npm install -g @angular/cli
Then, create a new Angular project directory called comicstore
. I want to enable routing in my app and write the style using SASS instead of plain old CSS. So I will also have to pass in a couple of flags to the ng new
command as follows:
$ ng new comicstore --routing --style scss // wait for installation to complete $ cd comicstore
Run the ng serve
command to start the Angular Developement Server. The Server will launch the basic application in your browser at localhost:4200
.
But we don’t really need all this stuff that Angular has graciously provided us with. So let’s get rid of it by going to the file src/app/app.component.html
and replace the existing code with:
<h1>DC Comics Rebirth</h1>
Bootstrap
I am going to use Bootstrap a lot in this app, simply because it will save me time from writing down basic HTML and CSS things.
Go to the BootstrapCDN website and copy the URL for the Complete CSS package. Then in your project directory, go to the style.scss
file and paste the URL inside an import statement.
@import url(<a href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css" data-href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css" rel="nofollow noopener" target="_blank">https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css</a>)
Inline the HTML Template and Style
When I first started working with Angular, I noticed that each component in Angular has 4 files:
- The Component Class
- The Component’s HTML Template
- The Component’s StyleSheet
- The Component’s Tests
I realised that things would be much easier for me if I could move the HTML Template and the StyleSheet from an external file to the Component’s Class file itself. This would reduce the number of files for each of my App’s components and I won’t have to go into various files to make any changes to code later on.
To start, delete the files app.component.html
and app.component.scss
from the src/app
folder. Then go to app.component.ts
file and rename the templateUrl
property as just template
. Also, change this property’s content as shown below:
template: ` <h1>DC Comics Rebirth</h1> <router-outlet></router-outlet> `,
Similarly, rename the styleUrls
property as styles
and change the property’s content like this:
styles: [` h1{ color: #0476F2; } `]
We also need to tell Angular that we will be writing our HTML Template and Style inside the component file itself. To do this we will use the ng config
command as shown below:
$ ng config schematics.@schematics/angular.component.inlineStyle true $ ng config schematics.@schematics/angular.component.inlineTemplate true
You will then be able to see that some changes have been made to the schematics
object in the angular.json
file.
Building the Application Layout
Instead of wasting time building the layout of our app from scratch, we can just use the module ui
command to tell Angular to create a basic application layout for us. This layout will consist a component for header, footer, and the body of the layout.
The module ui
is used with the ng generate
command as shown below:
$ ng generate module ui --module App
This command will create a new folder called ui
inside the src/app
folder. Angular will add a import statement for this folder inside the app.module.ts
file.
Next, we need to generate components for Header
, Footer
, and the Layout
of the app.
$ ng generate component ui/containers/layout $ ng generate component ui/containers/header $ ng generate component ui/containers/footer
This will generate a main component file and a test file for each of these components.
Next, open the app-routing.module.ts
file and add the default route to the LayoutComponent
as shown below.
const routes: Routes = [{ path: '', component: LayoutComponent, children: [], }];
By doing this, our app will initially render the contents of LayoutComponent
. You will also notice that our app still displays the h1
element that we had written inline in the app.component.ts
file. To make things simpler, let’s move it to the layout.component.ts
file.
First, go to app.component.ts
file, and remove the styles
array. Also remove the h1
element from the template
property.
Now go to the layout.component.ts
file that is located inside the app/ui/containers/layout
folder, and rewrite the template
property as shown below:
template: ` <app-header></app-header> <div class="container my-8 py-8"> <router-outlet></router-outlet> </div> <app-footer></app-footer> `,
If get an error on your browser, it is probably because Angular has not added the RouterModule
as an import to the ui.module.ts
file. If that is the case, then go ahead and take care of it right away.
@NgModule({
imports: [
CommonModule,
RouterModule
],
declarations: [
LayoutComponent,
HeaderComponent,
FooterComponent
]
})
As the name of this app suggests, this app is a dummy store for comics. And since I mostly follow DC Comics, that is what this store is going to “sell”. So I am going to Google the logo for DC Comics and save it in the src/assets
folder as logo.svg
.
Lets go ahead and add this logo to the header
component of our app. Go the the header.component.ts
file and add the following properties to the HeaderComponent
class.
public logo = 'assets/logo.svg'; public title = 'DC Comics Rebirth'; public links = [{ label: 'Comics', url: '/Comics', }]
Erase the template
property’s content. Instead, we will add a new nav
tag as shown below:
template: ` <nav class=navbar navbar-expand navbar-blue fixed-top> `
You will notice that there is now a thick black line at the top of the browser. Next, let’s add the logo and title to this header. Below the nav
element, write:
<a routerLink="/" class="navbar-brand"> <img [attr.src]="logo" [attr.alt]="title" width="30" height="30> {{title}} </a>
I had also create a link called Comics . So I am going to add this link to the header component as well. Below the a
tag, write a new div
element as shown below:
<div class="collapse navbar-collpase"> <div class="navbar-nav"> <a class="nav-item nav-link" *ngFor="let link of links" [routerLink]="link.urk" routerLinkActive="active" [routerLinkActiveOptions]="{ exact: true }"> {{ link.label }} </a> </div> </div>
Finally, your header component should look something like this:
Let’s take care of the footer component as well. Go to the footer.component.ts
file and erase everything inside the template property. Lets add a new nav
element inside the template
property with same classes as that of the one in header.
template: ` <nav class="navbar navbar-expand navbar-dark bg-dark fixed-bottom"> `
You should a thick black border at the bottom the browser page. This will be our footer. You can write anything you want in here. I am going to write a simple disclaimer.
<div class="navbar-text m-auto text-white"> The comics and characters and everything else presented here belongs to DC Comics. </div>
Retrieving Data in Angular
Let’s start by creating a new routing file called comic
.
$ ng g m comic --routing
This command will create a new folder called comic
inside the app
folder.
Open the app-routing.module.ts
file and create two new routes inside the children
array.
children: [{ path: '', pathMatch: 'full', redirectTo: '/comics', }, { path: 'comics', loadChildren: './comic/comic.module#ComicModule' }],
Here, the first route will redirect the user from any undefined routes to the comics
route. The second route is the actual comics
route.
Now when you refresh the browser, it will directly take you to the comics
route.
Lets also create a new module for comic
:
$ ng g cl comic/models/comic
You will now get a new folder called models
inside the comic
folder. It will contain a single file names comic.ts
. Add the following properties inside this file.
export class Comic { public id: string; public name: string; public description: string; public image: string; public price: number; }
Next, create a couple of containers to help us retrieve and pass data to other components.
$ ng g c comic/containers/comic-list $ ng g c comic/containers/comic-detail
These commands will create two new container components — comic-list
and comic-detail
.
Open comic-routing.module.ts
file and add a route to ComicListComponent
. The path for this route will just be an empty string. Similarly, add a route to ComicDetailComponent
with the path set to :id
.
const routes: Routes = [{ path: '', component: ComicListComponent, }, { path: ':id', component: ComicDetailComponent, }]
Open the comic-list.component.ts
file and set the template
property to comics
.
template: ` {{comics | json}} `,
Do the same thing for the template
property of comic-detail.component.ts
file.
Inside the component class of comic-list.component.ts
and comic-detail.component.ts
, add a new comics
property that is of Comic[]
type and initialize it as an empty array.
public comic: Comic[] = new Comic();
Then open the comic-detail.component.ts
file and inject ActivatedRoute
inside the class’s constructor method. This will allow us to fetch the id
property from the URL. Also assign this.comic.id
to this.route.snapshot.paraMap.get('id')
inside ngOnInit
.
constructor(private route: ActivatedRoute) {} ngOnInit() { this.comic.id = this.route.snapshot.paramMap.get('id'); }
Make sure that ActivatedRoute
is imported inside this file.
import { ActivatedRoute } from ‘@angular/router’;
This concludes the Part 1 of this series. Part 2 will be up very soon.
Shared with :heart: inBit’s blog
Bit seamlessly turns any component or module into an API you can share and use across projects. Give it a try alone or with your team, it’s open source :)
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK