Tuesday, September 24, 2019

Creating a News App Project with Angular 8 and Bootstrap

Hello everyone, in this tutorial we are going to create a news app with Angular 8 and Bootstrap. We will be consuming free edition of the The Guardian Developer API to fetch the news articles. We will be including pagination, search and category based news for the purpose of this tutorial.

Once we are done we will have an app looking something like this.
Creating a News App Project with Angular 8 and Bootstrap : Final App

Get the API-Key

Firstly we will have to register on developer portal of The Guardian in order to obtain the Api Key which is required to consume its resources. Head to their portal and click on Register Developer Key. Follow the steps and you will have your unique Api Key. Head over to their documentation page in order to know about the structure of the response object so that it is easy for you to understand once we start consuming these API's.

Setup Angular Project

Use Angular CLI to generate a new project named news-app. Once created install the latest version of Bootstrap and FontAwesome icons by referring the steps mentioned on the below links:


We will also be using ngx-pagination for implementing client side pagination on the news articles which are fetched. To add that to your project refer to the below link:

Create Components/Models/Services

Creating a News App Project with Angular 8 and Bootstrap - Project Setup

  • Create three components named dashboard, header and footer.
  • Create a folder named shared
  • Create a service named news inside the shared folder
  • Create a class named news.model inside the shared folder
import { BrowserModule } from '@angular/platform-browser';
import { NgModule, PipeTransform } from '@angular/core';
import {FormsModule} from '@angular/forms'
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { HeaderComponent } from './header/header.component';
import { DashboardComponent } from './dashboard/dashboard.component';
import { NewsService } from './shared/news.service';
import {HttpClientModule} from '@angular/common/http';
import {NgxPaginationModule} from 'ngx-pagination';
import { pipe } from 'rxjs';
import { FooterComponent } from './footer/footer.component';

@NgModule({
  declarations: [
    AppComponent,
    HeaderComponent,
    DashboardComponent,
    FooterComponent
  ],
  imports: [
    BrowserModule,
    AppRoutingModule,
    FormsModule,
    HttpClientModule,
    NgxPaginationModule
    
  ],
  providers: [NewsService],
  bootstrap: [AppComponent]
})
export class AppModule { }

  • Register the news service as a provider in the app.module.ts class as above.
  • Also import the HttpClientModule, FormsModule here as we will be using these modules as well.

Consuming the API

Firstly, we will create the News Model which we will use to store the articles which are returned from the API. Although there are multiple fields in the api, we will be using only few of them to show a preview of the article.Copy the code below into your news.model.ts file


export class NewsModel {
public sectionId: string;
public webPublicationDate : Date;
public webTitle: string;
public webUrl : string;
public thumbnail : string
public trailText : string
}

Below is the snapshot of the default response of the API, but since we are also showing a thumbnail, we will be using a property called show-fields=all to get additional fields as well. You can know more about these additional fields and properties here. As mentioned above, make sure you have gone through the documentation to know the capabilities of the API so that you can modify you application as per it.
Creating a News App Project with Angular 8 and Bootstrap - Default API response

Add the following code to your news.service.ts file. Here we will be consuming the endpoints using Http and store the result after mapping it into NewsModel type of object.


import { Injectable } from '@angular/core';
import { NewsModel } from './news.model';
import { HttpClient, HttpClientModule, HttpParams } from '@angular/common/http';
import { HttpHeaders } from '@angular/common/http';  
import { Observable } from 'rxjs';  

@Injectable({
  providedIn: 'root'
})
export class NewsService {
newsArticles : NewsModel[] = [];
temp:NewsModel = new NewsModel;

gaurApiKey = "add your api key here";
gaurUrl = "http://content.guardianapis.com/search?show-fields=all&page-size=20&api-key=";
gaurSectionUrl = "https://content.guardianapis.com/search?show-fields=all&page-size=20&q="

  constructor(private http:HttpClient) { }

  GetAllGaurdian()
  {
    return this.http.get(this.gaurUrl + this.gaurApiKey);
  }

  GetGaurdianSearchResult(section:string)
  {
    return this.http.get(this.gaurSectionUrl + section + "&api-key=" + this.gaurApiKey );
  }

 public CusotomMapper(item) : NewsModel{
          
    this.temp = new NewsModel;
      this.temp.thumbnail = item["fields"].thumbnail;
      this.temp.sectionId= item["sectionId"];
      this.temp.webPublicationDate = item["webPublicationDate"];
      this.temp.webTitle = item["webTitle"];
      this.temp.webUrl = item["webUrl"];
      this.temp.trailText = item["fields"].trailText;
      return this.temp;
  }
}

Firstly we create a newsArticles variable which is array of type NewsModel. We also create a temp variable called temp which we will be using to convert the response into our type.

We then have three variables to store api key, general endpoint url and the url to filter news based on search terms or categories. The query parameter "q" is used to pass on the search term, api-key to pass the unique key etc.

We then have two methods, GetAllGaurdian(), used to get all the news and GetGaurdianSearchResult() which is used to get news based on some search text or filter

We then have a method named custom mapper which takes in the response object and creates a NewsModel type of object based upon the fields which we are using in are app.

Note: Since the response has these fields in hierarchical structure hence we had to write this mapper.

Working on the Components

header.component.html

<nav class="navbar navbar-light bg-success navbar-expand-lg">
    <a class="navbar-brand mt-1" href="#">
        <i class="fa fa-newspaper-o fa-2x text-dark  d-inline-block align-top">
    </i> Ng News</a>
    <button class="navbar-toggler" data-toggle="collapse" data-target="#navbaritem" aria-expanded="false" aria-label="Toggle navigation">
          <span class="navbar-toggler-icon"></span>
</button>

    <div class="collapse navbar-collapse" id="navbaritem">
        <ul class="navbar-nav mr-auto mt-2 mt-lg-0 lead">
            <li class="nav-item active ">
                <a class="nav-link" href="#" (click)="filterClicked($event)">World</a>
            </li>
            <li class="nav-item">
                <a class="nav-link" href="#" (click)="filterClicked($event)">India</a>
            </li>
            <li class="nav-item ">
                <a class="nav-link" href="#" (click)="filterClicked($event)">Tech</a>
            </li>
            <li class="nav-item">
                <a class="nav-link" href="#" (click)="filterClicked($event)">Sports</a>
            </li>
            <li class="nav-item">
                <a class="nav-link" href="#" (click)="filterClicked($event)">Business</a>
            </li>

        </ul>

        <form class="form-group d-flex ml-auto mb-0" #form="ngForm" (submit)="onSubmit(form)">
            <input type="text" class="form-control mr-2 align-self-center" required placeholder="Search" name="searchText" [ngModel]="searchText" value="">
            <button class="btn btn-outline-warning align-self-center" [disabled]="!form.valid" type="submit">Search</button>
        </form>
    </div>
</nav>

Here we design the navigation bar which contains few links to fetch news based on catgories and a search box as well.

Creating a News App Project with Angular 8 and Bootstrap - NavBar.png

We wont be handling any events in this components, hence all its events like the nav clicks and search are emitted to its parent i.e. app component.

header.component.ts

import { Component,  OnInit, Output, EventEmitter } from '@angular/core';
import { NgForm } from '@angular/forms';

@Component({
  selector: 'app-header',
  templateUrl: './header.component.html',
  styleUrls: ['./header.component.css']
})
export class HeaderComponent implements OnInit {
  filterText : string;
@Output() search = new EventEmitter();
@Output() filterButton = new EventEmitter();

  constructor() { }

  ngOnInit() {
  }
  onSubmit(form : NgForm)
  {
    console.log(form);
    this.search.emit(form);
  }

  filterClicked($event)
  {
    this.filterText = $event.target.text;
    this.filterButton.emit(this.filterText);
  }
}

Here the form submit event i.e. raised on the Search button and the <a> click events are emitted using search and filterButtons emitter.

dashboard.component.html

<div class="container-fluid py-5 bg-light">
    <div class="row">
        <div class="col-md-4 col-lg-3 my-3 d-flex align-items-stretch" *ngFor="let item of newsService.newsArticles | paginate: { itemsPerPage: 8, currentPage: p }">
            <div class="card">
                <img [src]="item.thumbnail" alt="" class="card-img-top img-fluid img-thumbnail">
                <div class="card-body">
                    <div class="card-title text-capitalize">
                        <h5>{{item.webTitle}}</h5>
                    </div>
                    <p class="card-text">
                        {{ (item.trailText | slice:0:100 ) +'..' }}
                    </p>
                    <div class="d-flex justify-content-left mt-3">
                        <p class="badge badge-secondary mr-3">{{item.webPublicationDate | date}}</p>
                        <p class="badge badge-secondary ">{{item.sectionId}}</p>
                    </div>

                </div>
                <div class="card-footer bg-warning d-flex justify-content-end">

                    <a [href]="item.webUrl" class="btn btn-success" target="_blank">
                        <i class="fa fa-book mr-1"></i>Read More
                    </a>
                </div>
            </div>
        </div>
    </div>
    <pagination-controls class="text-center" (pageChange)="p = $event"></pagination-controls>
</div>

Dashboard is the main component in terms of UI as here we will have the bootstrap code to show the news articles using cards layout.

dashboard.component.ts

import { Component, OnInit } from '@angular/core';
import { NewsService } from '../shared/news.service';
import { NewsModel } from '../shared/news.model';
import { Form } from '@angular/forms';
import { Pipe, PipeTransform } from '@angular/core';
import { element } from 'protractor';
@Component({
  selector: 'app-dashboard',
  templateUrl: './dashboard.component.html',
  styleUrls: ['./dashboard.component.css']
})
export class DashboardComponent implements OnInit {

  articles : any;
  temp : NewsModel = new NewsModel;
  constructor(private newsService : NewsService) { }

  ngOnInit() {
      this.FetchHeadlines();
           }

  FetchHeadlines()
  {
     this.newsService.GetAllGaurdian()
        .subscribe((result) =>
        {
          this.articles = result;
                  this.articles.response.results.forEach(element => {
                       this.newsService.newsArticles.push(this.newsService.CusotomMapper(element));
          });
        }) 
  }
}

The dashboard component consumes the NewsService using dependency injection in the constructor to fetch all news posts once the app is first loaded.

The response from API is stored in a variable named result and then we store it in a local array variable named articles. Then this array is iterated through and each item in it is passed through the custom mapper we created in news service to generate an object of type NewsModel. Once generated this object is stored in the newsArticle array which is used throughout the application to display the news 

footer.component.html  (optional)

<footer class="footer bg-dark py-5">
    <div class="container">
        <div class="row">
            <div class="col text-center">
                <h1 class="display-5 text-uppercase text-light mb-0">
                    <strong>Ng-News</strong>
                </h1>
                <div class="title-underline bg-primary">
                </div>
                <p class="text-white lead font-italic my-2">Web App built with Angular 8, Bootstrap and Gaurdian API</p>
            </div>
        </div>
    </div>
</footer>

footer.component.ts

No changes are required in the footer component and the CLI generated code fragment work as expected.

app.component.html

<app-header (search)="searchNews($event)" (filterButton)="filterNews($event)"></app-header>
<app-dashboard></app-dashboard>
<app-footer></app-footer>

Events from the Header component are handled using the searchNews() and filterNews() functions defined here.

app.component.ts


import { Component } from '@angular/core';
import { Form } from '@angular/forms';
import { NewsService } from './shared/news.service';
import { NewsModel } from './shared/news.model';


@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  title = 'news-app';
  searchText = "";
  resultSet : any;

  constructor(private newsService : NewsService) { }

  searchNews($event){
   
     this.searchText = $event.form.controls["searchText"].value;

     this.newsService.GetGaurdianSearchResult(this.searchText)
     .subscribe((result) => {
        this.resultSet = result;
        this.newsService.newsArticles = [];
        this.resultSet.response.results.forEach(element => {
                console.log(this.newsService.CusotomMapper(element));
        
        this.newsService.newsArticles.push(this.newsService.CusotomMapper(element))
      })
  
    })
  }

  filterNews(text: string)
  {
    console.log(text);
  
    // this.newsService.GetSearchResult(text)
    this.newsService.GetGaurdianSearchResult(text)
    .subscribe((result) => {
      this.newsService.newsArticles = [];
      this.resultSet = result;
      this.resultSet.response.results.forEach(element => {
           this.newsService.newsArticles.push(this.newsService.CusotomMapper(element));
    })
  
  })

}
}

Here we have two separate function defined for better understanding as the same could be achieved through a single function itself. The searchNews() is called when the user enters a search term and clicks on Search button in the navigation bar. This function in turn calls the GetGaurdianSearchResult() function which returns result based on the search query.

Creating a News App Project with Angular 8 and Bootstrap - Event Emitter


The filterNews() function is called when the user clicks on any of the navigation links we had like India, Business etc. The event sends the inner text of that link which is passed onto the GetGaurdianSearchResult() function.

That it's, we have finished Creating a News App Project with Angular 8 and Bootstrap tutorial. If you have any trouble following the steps of the tutorial, you can compare it with the working source code uploaded here.

11 comments:
Write comments
  1. I like your post very much. It is very much useful for my research. I hope you to share more info about this. Keep posting angularjs online training

    ReplyDelete
    Replies
    1. The effectiveness of IEEE Project Domains depends very much on the situation in which they are applied. In order to further improve IEEE Final Year Project Domains practices we need to explicitly describe and utilise our knowledge about software domains of software engineering Final Year Project Domains for CSE technologies. This paper suggests a modelling formalism for supporting systematic reuse of software engineering technologies during planning of software projects and improvement programmes in Final Year Projects for CSE.

      Software management seeks for decision support to identify technologies like JavaScript that meet best the goals and characteristics of a software project or improvement programme. JavaScript Training in Chennai Accessible experiences and repositories that effectively guide that technology selection are still lacking.

      Aim of technology domain analysis is to describe the class of context situations (e.g., kinds of JavaScript software projects) in which a software engineering technology JavaScript Training in Chennai can be applied successfully

      Delete
  2. Such a strikingly basic article.I basically wish to offer a creature proceed for the standard data you have perfect here on this post. rojadirecta

    ReplyDelete
  3. The article looks magnificent, but it would be beneficial if you can share more about the suchlike subjects in the future. Keep posting. bootstrap button

    ReplyDelete
  4. I found so many interesting stuff in your blog especially its discussion. From the tons of comments on your articles, I guess I am not the only one having all the enjoyment here! keep up the good work... truthful news

    ReplyDelete
  5. An interesting discussion is worth comment. I think that you need to write more on this matter, it may not be a taboo subject but usually individuals are not enough to speak on such topics. To the next. Cheers https://royalcbd.com/product/cbd-oil-1000mg/

    ReplyDelete
  6. The writer has written this blog in a very idiomatic manner.
    Arsprojecta

    ReplyDelete
  7. If you are a property investor and you want to stay informed of the latest in the property market then you need to keep in touch with all the sources which are reliable sources of news related to the world of real estate. This real estate news can not only help you in staying updated with the latest information but also help you in knowing about the trends in the market. These trends and the news help you in making informed and wise decisions.ventarticle

    ReplyDelete
  8. I conceive this website has very wonderful indited content material posts . new york web designs

    ReplyDelete
  9. If running proves to be a problem then it may be wise to find alternative exercises such as circuit training, weight training, swimming or cycling. nyc web designer

    ReplyDelete
  10. There is evidently a lot to know about this. I consider you made certain nice points in features also. branding sf

    ReplyDelete