This is the seventh part in a seven-part 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:

ng 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

import { HttpModule } from '@angular/http'; 

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

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { MaterialModule } from './material.module';
import { FlexLayoutModule } from '@angular/flex-layout';
import { HttpModule } from '@angular/http'; 
import { AppRoutingModule } from './app-routing.module';
import { FormsModule,ReactiveFormsModule } from '@angular/forms';

import { ServiceWorkerModule } from '@angular/service-worker';
import { AppComponent } from './app.component';

import { environment } from '../environments/environment';
import { PostsComponent } from './posts/posts.component';
import { HomeComponent } from './home/home.component';
import { NavbarComponent } from './navbar/navbar.component';
import { ThemeService } from './services/theme.service';
import { NewsService } from './services/news.service';

@NgModule({
  declarations: [
    AppComponent,
    PostsComponent,
    HomeComponent,
    NavbarComponent
  ],
  imports: [
    BrowserModule,
    BrowserAnimationsModule,
    MaterialModule,
    FlexLayoutModule,
    AppRoutingModule,
    FormsModule,
    ReactiveFormsModule,
    HttpModule,
    ServiceWorkerModule.register('/ngsw-worker.js', { enabled: environment.production })
  ],
  providers: [ThemeService, NewsService],
  bootstrap: [AppComponent]
})
export 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

import { Injectable } from '@angular/core';
import { Http, Headers, RequestOptions } from '@angular/http';
import { environment } from '../../environments/environment';

@Injectable()
export class NewsService {
  key = 'your-key-here';
  constructor(private http: Http) { }
  getTopHeadLines(){
    return this.http.get('https://newsapi.org/v2/top-headlines?country=us&category=business&apiKey='+this.key);
  }
  getNewBySource(source){
    return this.http.get('https://newsapi.org/v2/top-headlines?sources='+source+'&apiKey='+this.key);
  }
  getSources(){
    return this.http.get('https://newsapi.org/v2/sources?apiKey='+this.key);
  }
}

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

import { Component, OnInit } from "@angular/core";
import { NewsService } from '../services/news.service';

@Component({
  selector: "app-posts",
  templateUrl: "./posts.component.html",
  styleUrls: ["./posts.component.scss"],
  providers:[NewsService]
})
export class PostsComponent implements OnInit {
  news= {articles:[]};
  newsSources= {sources:[]};
  filterSource='google-news';

  constructor(private newsService: NewsService){}

  ngOnInit() {
    this.newsService.getTopHeadLines()
  		.subscribe(
  			response => this.news = response.json()
    );
    this.getnewsSources();
  }


  filterNews(source) {
    this.news={articles:[]};
    this.newsService.getNewBySource(source)
    .subscribe(
      response => this.news = response.json()
    );
  }

  getnewsSources() {
    this.newsService.getSources()
      .subscribe(
        response => this.newsSources = response.json()
      );
  } 
}

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

<div class="container margin-top" fxLayout="wrap row" fxLayout.xs="column" fxLayoutGap="1%" fxLayoutAlign="center">
  <mat-card class="filter">
    <form #filternewsForm="ngForm">
      <mat-form-field>
        <mat-select placeholder="News Source" [(ngModel)]='filterSource' name="source" required>
          <mat-option *ngFor="let source of newsSources.sources" [value]="source.id">
            {{source.name}} - {{ source.language }}
          </mat-option>
        </mat-select>
      </mat-form-field>
      <button mat-raised-button color="primary" (click)='filterNews(filterSource)' [disabled]="!filternewsForm.form.valid">Filter News to
        <span style="text-transform: capitalize; ">{{filterSource}}</span>
      </button>

    </form>
  </mat-card>
</div>

<div class="loader" *ngIf="!((news.articles)?.length > 0)">
    <mat-spinner></mat-spinner>
  </div>
<div class="container" fxLayout="wrap row" fxLayout.xs="column" fxLayoutGap="1%" fxLayoutAlign="center">
  <div *ngFor="let post of news.articles" fxFlex="20%">

    <mat-card class=singleNews>
      <img mat-card-image src="{{post.urlToImage}}" onError="this.src='/assets/blank.png';">
      <div class="cardbody">
        <mat-card-title>{{post.title}}</mat-card-title>
        <mat-card-content>
          <p>{{post.description}}</p>
        </mat-card-content>
      </div>

      <mat-card-actions align="end">
        <a href="{{post.url}}" target="_balnk" mat-raised-button color="accent">Read More</a>
      </mat-card-actions>
    </mat-card>

  </div>

</div>

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

.singleNews {
  margin-bottom: 15px;
  img {
    height: 200px;
  }
  mat-card-title {
    font-size: 16px;
    font-weight: bold;
  }
  .cardbody {
    height: 100px;
    overflow-y: auto;
    margin: 0 -15px;
    padding: 10px;
  }
}
.filter {
  margin-top: 100px;
  margin-bottom: 50px;
}

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

@import "./assets/styles/material-theme";
body {
  margin: 0;
}
.loader mat-spinner {
  margin: 10px auto;
}

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:

<div class="homebtn" fxLayout="wrap row" fxLayout.xs="column" fxLayoutGap="1%" fxLayoutAlign="center">
  <a routerLink="/posts" mat-raised-button color="primary">Choose News Source</a>
</div>
<div class="loader" *ngIf="!((news.articles)?.length > 0)">
  <mat-spinner></mat-spinner>
</div>
<div class="container" fxLayout="wrap row" fxLayout.xs="column" fxLayoutGap="1%" fxLayoutAlign="center">

  <div *ngFor="let post of news.articles" fxFlex="20%">

    <mat-card class=singleNews>
      <img mat-card-image src="{{post.urlToImage}}" onError="this.src='/assets/blank.png';">
      <div class="cardbody">
        <mat-card-title>{{post.title}}</mat-card-title>
        <mat-card-content>
          <p>{{post.description}}</p>
        </mat-card-content>
      </div>

      <mat-card-actions align="end">
        <a href="{{post.url}}" target="_balnk" mat-raised-button color="accent">Read More</a>
      </mat-card-actions>
    </mat-card>

  </div>

</div>

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

import { Component, OnInit } from '@angular/core';
import { NewsService } from '../services/news.service';

@Component({
  selector: 'app-home',
  templateUrl: './home.component.html',
  styleUrls: ['./home.component.scss'],
  providers:[NewsService]
  
})
export class HomeComponent implements OnInit {
  news= {articles:[]};


  constructor(private newsService: NewsService){}

  ngOnInit() {
    this.newsService.getTopHeadLines()
  		.subscribe(
  			response => this.news = response.json()
    );
  }
}

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

.singleNews {
  margin-bottom: 15px;
  img {
    height: 200px;
  }
  mat-card-title {
    font-size: 16px;
    font-weight: bold;
  }
  .cardbody {
    height: 100px;
    overflow-y: auto;
    margin: 0 -15px;
    padding: 10px;
  }
}
.homebtn{
  margin:40px 0;
}

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