Home
Blog
Building News App using Angular 5

Building News App using Angular 5

4/30/2018
This is the seventh part in a seven-part [series](https://blog.wb.gy/angular-5-article-series/) about the JavaScript framework, `Angular 5`. In this part, we'll go over turning our application into a `News Application`.

This is not intended to be a complete guide, but rather an overview of the basics to get you up and running so you can get to know using Angular Services and how to turn your Angular 5 application into a News app.

#Article Series:

  1. Creating Angular 5 application with Angular-cli
  2. Using Angular Material with Angular 5
  3. Deploy Angular 5 Application to Netlify
  4. Build PWA with Angular 5 App
  5. Build Dynamic themes for Angular Material
  6. Using FlexLayout with Angular 5
  7. Building News App using Angular 5 (You are here)

Final Demo here


I'm going to turn the application We created in the 6 previous articles, so you have to check them first! No, you must check all of them!

Our News app will be:

  • a Progressive web application (PWA)
  • With Angular Material Design
  • with a grid-system using Flex-Layout
  • Deployed to Netlify
  • With 2 dynamic themes
  • Later we can improve it with more option you can suggest or ask me what you want in the next article

#Build News Service

In the previous articles We created a component named posts. I'll use it to show our news posts.

First Let's discuss the layout of our posts component. I want to build two things: part for filtering the news and part for displaying the news itself

I'll use newsapi.org's api to get the news, so you have to signup and get a key.

Next, we have to create new angular service to get the news from the API. I'll name it news service. Now, in your application's root directory open your terminal and run:

1ng g service services/news --module app.module.ts

This command will create our news service and will update our app.module.ts.

We need to add something first in our app.module.ts to use it in new.service.ts

1import { HttpModule } from "@angular/http";

and add it to the imports Now your app.module.ts should look like

1import { BrowserModule } from "@angular/platform-browser";
2import { NgModule } from "@angular/core";
3import { BrowserAnimationsModule } from "@angular/platform-browser/animations";
4import { MaterialModule } from "./material.module";
5import { FlexLayoutModule } from "@angular/flex-layout";
6import { HttpModule } from "@angular/http";
7import { AppRoutingModule } from "./app-routing.module";
8import { FormsModule, ReactiveFormsModule } from "@angular/forms";
9
10import { ServiceWorkerModule } from "@angular/service-worker";
11import { AppComponent } from "./app.component";
12
13import { environment } from "../environments/environment";
14import { PostsComponent } from "./posts/posts.component";
15import { HomeComponent } from "./home/home.component";
16import { NavbarComponent } from "./navbar/navbar.component";
17import { ThemeService } from "./services/theme.service";
18import { NewsService } from "./services/news.service";
19
20@NgModule({
21  declarations: [AppComponent, PostsComponent, HomeComponent, NavbarComponent],
22  imports: [
23    BrowserModule,
24    BrowserAnimationsModule,
25    MaterialModule,
26    FlexLayoutModule,
27    AppRoutingModule,
28    FormsModule,
29    ReactiveFormsModule,
30    HttpModule,
31    ServiceWorkerModule.register("/ngsw-worker.js", {
32      enabled: environment.production
33    })
34  ],
35  providers: [ThemeService, NewsService],
36  bootstrap: [AppComponent]
37})
38export class AppModule {}

First Let's explain what we're going to do, I know that this part is so boring but you have to know it, so you can handle things later yourself. Next you have to replace your-key-here with your API key you got from your account here > https://newsapi.org/account


#Details of News Service

Next we need to build the functions to call the api from our news.service

We'll build 3 functions getTopHeadLines(), getNewBySource() and getSources(). getTopHeadLines: will get the Top Headlines news. and this will be displayed as the default news. getSources: it will give us all the news sources, we'll use them to filter our news. getNewBySource: We'll use the previous function to get the sources to use it in this function.

Here's the code of our service

1import { Injectable } from '@angular/core';
2import { Http, Headers, RequestOptions } from '@angular/http';
3import { environment } from '../../environments/environment';
4
5@Injectable()
6export class NewsService {
7  key = 'your-key-here';
8  constructor(private http: Http) { }
9  getTopHeadLines(){
10    return this.http.get('https://newsapi.org/v2/top-headlines?country=us&category=business&apiKey='+this.key);
11  }
12  getNewBySource(source){
13    return this.http.get('https://newsapi.org/v2/top-headlines?sources='+source+'&apiKey='+this.key);
14  }
15  getSources(){
16    return this.http.get('https://newsapi.org/v2/sources?apiKey='+this.key);
17  }
18}

#Adding `News Service to posts.component.ts

Now we need to edit our posts.component.ts. As we created 3 functions in our service we'll use them here in posts.component.ts

#First

you have to import new.service.ts.

#Second

create 2 variable: 1st: will be news= {articles:[]} 2nd: will be newsSources= {sources:[]}

#Third

I'll display articles from getTopHeadLines in ngOnInit and the new resources also.

Here's the code of our src/posts/posts.component.ts

1import { Component, OnInit } from "@angular/core";
2import { NewsService } from '../services/news.service';
3
4@Component({
5  selector: "app-posts",
6  templateUrl: "./posts.component.html",
7  styleUrls: ["./posts.component.scss"],
8  providers:[NewsService]
9})
10export class PostsComponent implements OnInit {
11  news= {articles:[]};
12  newsSources= {sources:[]};
13  filterSource='google-news';
14
15  constructor(private newsService: NewsService){}
16
17  ngOnInit() {
18    this.newsService.getTopHeadLines()
19        .subscribe(
20            response => this.news = response.json()
21    );
22    this.getnewsSources();
23  }
24
25
26  filterNews(source) {
27    this.news={articles:[]};
28    this.newsService.getNewBySource(source)
29    .subscribe(
30      response => this.news = response.json()
31    );
32  }
33
34  getnewsSources() {
35    this.newsService.getSources()
36      .subscribe(
37        response => this.newsSources = response.json()
38      );
39  }
40}

For the view of this component in posts.component.html I made 2 parts as I said at the beginning of this article: one for filtering the news and one to show them. I used Flex-Layout also. I added two things for the posts:

#Fix damaged images

You might noticed that there are some images that does not load correctly, to solve this problem I added this attribute onError="this.src='/assets/blank.png';" It will display default image for damaged images.

#Spinner loader

Also I added a spinner to been displayed while our application fetching the data from the api, I used the Angular Material module MatSpinnerModule.

Here's the code of posts.component.html

1<div
2  className="container margin-top"
3  fxLayout="wrap row"
4  fxLayout.xs="column"
5  fxLayoutGap="1%"
6  fxLayoutAlign="center"
7>
8  <mat-card className="filter">
9    <form #filternewsForm="ngForm">
10      <mat-form-field>
11        <mat-select
12          placeholder="News Source"
13          [(ngModel)]="filterSource"
14          name="source"
15          required
16        >
17          <mat-option
18            *ngFor="let source of newsSources.sources"
19            [value]="source.id"
20          >
21            {{source.name}} - {{ source.language }}
22          </mat-option>
23        </mat-select>
24      </mat-form-field>
25      <button
26        mat-raised-button
27        color="primary"
28        (click)="filterNews(filterSource)"
29        [disabled]="!filternewsForm.form.valid"
30      >
31        Filter News to
32        <span style="text-transform: capitalize; ">{{filterSource}}</span>
33      </button>
34    </form>
35  </mat-card>
36</div>
37
38<div className="loader" *ngIf="!((news.articles)?.length > 0)">
39  <mat-spinner></mat-spinner>
40</div>
41<div
42  className="container"
43  fxLayout="wrap row"
44  fxLayout.xs="column"
45  fxLayoutGap="1%"
46  fxLayoutAlign="center"
47>
48  <div *ngFor="let post of news.articles" fxFlex="20%">
49    <mat-card className="singleNews">
50      <img
51        mat-card-image
52        src="{{post.urlToImage}}"
53        onError="this.src='/assets/blank.png';"
54      />
55      <div className="cardbody">
56        <mat-card-title>{{post.title}}</mat-card-title>
57        <mat-card-content> <p>{{post.description}}</p> </mat-card-content>
58      </div>
59
60      <mat-card-actions align="end">
61        <a href="{{post.url}}" target="_balnk" mat-raised-button color="accent"
62          >Read More</a
63        >
64      </mat-card-actions>
65    </mat-card>
66  </div>
67</div>

I did some small styling in src/app/posts/posts.component.scss

1.singleNews {
2  margin-bottom: 15px;
3  img {
4    height: 200px;
5  }
6  mat-card-title {
7    font-size: 16px;
8    font-weight: bold;
9  }
10  .cardbody {
11    height: 100px;
12    overflow-y: auto;
13    margin: 0 -15px;
14    padding: 10px;
15  }
16}
17.filter {
18  margin-top: 100px;
19  margin-bottom: 50px;
20}

and I added some styling for the loader in src/styles.scss

1@import "./assets/styles/material-theme";
2body {
3  margin: 0;
4}
5.loader mat-spinner {
6  margin: 10px auto;
7}

#Change homepage content to Top News

For our home screen I want to make it dynamic, so let's display our Top Hot-lines there. We'll use the same code and service: Open src/app/home/home.component.html and add the following code:

1<div
2  className="homebtn"
3  fxLayout="wrap row"
4  fxLayout.xs="column"
5  fxLayoutGap="1%"
6  fxLayoutAlign="center"
7>
8  <a routerLink="/posts" mat-raised-button color="primary"
9    >Choose News Source</a
10  >
11</div>
12<div className="loader" *ngIf="!((news.articles)?.length > 0)">
13  <mat-spinner></mat-spinner>
14</div>
15<div
16  className="container"
17  fxLayout="wrap row"
18  fxLayout.xs="column"
19  fxLayoutGap="1%"
20  fxLayoutAlign="center"
21>
22  <div *ngFor="let post of news.articles" fxFlex="20%">
23    <mat-card className="singleNews">
24      <img
25        mat-card-image
26        src="{{post.urlToImage}}"
27        onError="this.src='/assets/blank.png';"
28      />
29      <div className="cardbody">
30        <mat-card-title>{{post.title}}</mat-card-title>
31        <mat-card-content> <p>{{post.description}}</p> </mat-card-content>
32      </div>
33
34      <mat-card-actions align="end">
35        <a href="{{post.url}}" target="_balnk" mat-raised-button color="accent"
36          >Read More</a
37        >
38      </mat-card-actions>
39    </mat-card>
40  </div>
41</div>

And replace the code into src/app/home/home.component.ts with

1import { Component, OnInit } from '@angular/core';
2import { NewsService } from '../services/news.service';
3
4@Component({
5  selector: 'app-home',
6  templateUrl: './home.component.html',
7  styleUrls: ['./home.component.scss'],
8  providers:[NewsService]
9
10})
11export class HomeComponent implements OnInit {
12  news= {articles:[]};
13
14
15  constructor(private newsService: NewsService){}
16
17  ngOnInit() {
18    this.newsService.getTopHeadLines()
19        .subscribe(
20            response => this.news = response.json()
21    );
22  }
23}

Finally let's add some styling open src/app/home/home.component.scss

1.singleNews {
2  margin-bottom: 15px;
3  img {
4    height: 200px;
5  }
6  mat-card-title {
7    font-size: 16px;
8    font-weight: bold;
9  }
10  .cardbody {
11    height: 100px;
12    overflow-y: auto;
13    margin: 0 -15px;
14    padding: 10px;
15  }
16}
17.homebtn {
18  margin: 40px 0;
19}

Now our application is done! Here's the final result

angular-5-pwa-material-netlify-dynamic-theme-flexlayout-services-news-ng-cli-app

And you can find the demo here