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:
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:
PWA
)Angular Material
DesignFlex-Layout
Netlify
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
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}
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
you have to import new.service.ts
.
create 2 variable:
1st: will be news= {articles:[]}
2nd: will be newsSources= {sources:[]}
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:
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.
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}
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
And you can find the demo here