Building News App using Angular 5

Tue May 1, 2018 · 6 min read

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==

Note

Hi, my name is Ahmed Abdelsalam. I've turned down every offer for advertisements or sponsored posts on this website. I write free resources that have helped thousands of people successfully transition into a web development career.
If you enjoy my content, please consider supporting what I do.