Why AngularJS is highly popular?

Angularjs is an open-source JS framework which is used for creating Single page applications for mobile and web development. There is a whole large community of AngularJS developers; You can find a lot of places on the internet like Reddit, LinkedIn groups, and so on..

Angular 7 CRUD with Nodejs and MySQL Example

Angular 7 CRUD with Nodejs and MySQL Example

Angular7 CRUD with nodejs and mysql example - Hey there, Today we will proceed to create a demo for CRUD with Mysql, Express, Angular7(MEAN) and Nodejs from scratch using Angular CLI

Below are the requirements for creating the CRUD on MEAN

  • Node.js
  • Angular CLI
  • Angular 7
  • Mysql
  • IDE or Text Editor

We assume that you have already available the above tools/frameworks and you are familiar with all the above that what individually actually does.

So now we will proceed step by step to achieve the task.

1. Update Angular CLI and Create Angular 7 Application

At first, We have to update the Angular CLI to the latest version. Open the terminal then go to the project folder and then type the below command to update the Angular CLI

sudo npm install -g @angular/cli

Once the above task finishes, Next task is to create new angular application with below command. So go to your project folder and then type below command:

ng new angular7-crud

then go to the newly created folder of angular application with cd /angular7-crud  and type **ng serve. **Now, open the browser then go to http://localhost:4200 you should see this page.

Angular 7 CRUD with Nodejs and MySQL Example

2. Create a server with node.js express and Mysql for REST APIs

create a separate folder named server for server-side stuff, Then move inside folder and create server.js by typing touch server.js

Let’s have a look on the server.js file

let app = require('express')(),
server = require('http').Server(app),
bodyParser = require('body-parser')
express = require('express'),
cors = require('cors'),
http = require('http'),
path = require('path');
 
let articleRoute = require('./Routes/article'),
util = require('./Utilities/util');
 
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: false }));
 
app.use(cors());
 
app.use(function(err, req, res, next) {
return res.send({ "statusCode": util.statusCode.ONE, "statusMessage": util.statusMessage.SOMETHING_WENT_WRONG });
});
 
app.use('/article', articleRoute);
 
// catch 404 and forward to error handler
app.use(function(req, res, next) {
next();
});
 
/*first API to check if server is running*/
app.get('*', (req, res) => {
res.sendFile(path.join(__dirname, '../server/client/dist/index.html'));
})
 
 
server.listen(3000,function(){
console.log('app listening on port: 3000');
});

In the above file we can see, at the top, there are required packages for the app. Below that body parsing, middleware and routing is done.

The next task is to create routes and create a file article.js . So creating a folder name ‘Routes’ and adding article.js within it.

Add the below code for routing in article.js inside routing folder

let express = require('express'),
router = express.Router(),
util = require('../Utilities/util'),
articleService = require('../Services/article');
 
/**Api to create article */
router.post('/create-article', (req, res) => {
articleService.createArticle(req.body, (data) => {
res.send(data);
});
});
 
// /**Api to update article */
router.put('/update-article', (req, res) => {
articleService.updateArticle(req.body, (data) => {
res.send(data);
});
});
 
// /**Api to delete the article */
router.delete('/delete-article', (req, res) => {
articleService.deleteArticle(req.query, (data) => {
res.send(data);
});
});
 
/**Api to get the list of article */
router.get('/get-article', (req, res) => {
documentService.getArticle(req.query, (data) => {
res.send(data);
});
});
 
// /**API to get the article by id... */
router.get('/get-article-by-id', (req, res) => {
articleService.getArticleById(req.query, (data) => {
res.send(data);
});
});
 
module.exports = router;

Now create a folder named Utilities for all config, common methods and mysql connection config.

Now I am adding config values in a file named config.js

let environment = "dev";
 
let serverURLs = {
"dev": {
"NODE_SERVER": "http://localhost",
"NODE_SERVER_PORT": "3000",
"MYSQL_HOST": 'localhost',
"MYSQL_USER": 'root',
"MYSQL_PASSWORD": 'password',
'MYSQL_DATABASE': 'demo_angular7_crud',
}
}
 
let config = {
"DB_URL_MYSQL": {
"host": `${serverURLs[environment].MYSQL_HOST}`,
"user": `${serverURLs[environment].MYSQL_USER}`,
"password": `${serverURLs[environment].MYSQL_PASSWORD}`,
"database": `${serverURLs[environment].MYSQL_DATABASE}`
},
"NODE_SERVER_PORT": {
"port": `${serverURLs[environment].NODE_SERVER_PORT}`
},
"NODE_SERVER_URL": {
"url": `${serverURLs[environment].NODE_SERVER}`
}
};
 
module.exports = {
config: config
};

Now configure mysql connection. So I am writing the connection with database in a separate file. So creating a file named mysqkConfig.js under Utilities folder and adding the below line of code for mysql connection:

var config = require("../Utilities/config").config;
var mysql = require('mysql');
var connection = mysql.createConnection({
host: config.DB_URL_MYSQL.host,
user: config.DB_URL_MYSQL.user,
password: config.DB_URL_MYSQL.password,
database: config.DB_URL_MYSQL.database,
});
 
connection.connect(() => {
require('../Models/Article').initialize();
});
 
let getDB = () => {
return connection;
}
 
module.exports = {
getDB: getDB
}

Now I am creating separate file name util.js to save common methods and common status code/message:

// Define Error Codes
let statusCode = {
OK: 200,
FOUR_ZERO_FOUR: 404,
FOUR_ZERO_THREE: 403,
FOUR_ZERO_ONE: 401,
FIVE_ZERO_ZERO: 500
};
 
// Define Error Messages
let statusMessage = {
SERVER_BUSY : 'Our Servers are busy. Please try again later.',
DATA_UPDATED: 'Data updated successfully.',
DELETE_DATA : 'Delete data successfully',
 
};
 
module.exports = {
statusCode: statusCode,
statusMessage: statusMessage
}

Now the next part is model, So create a folder named Models and create a file **Article.js **and add the below code in it:

let mysqlConfig = require("../Utilities/mysqlConfig");
 
let initialize = () => {
mysqlConfig.getDB().query("create table IF NOT EXISTS article (id INT auto_increment primary key, category VARCHAR(30), title VARCHAR(24))");
 
}
 
module.exports = {
initialize: initialize
}

Now create DAO folder and add a file articleDAO.js for writting the mysql queries common functions:

let dbConfig = require("../Utilities/mysqlConfig");


 
let getArticle = (criteria, callback) => {
//criteria.aricle_id ? conditions += ` and aricle_id = '${criteria.aricle_id}'` : true;
dbConfig.getDB().query(`select * from article where 1`,criteria, callback);
}
 
let getArticleDetail = (criteria, callback) => {
    let conditions = "";
criteria.id ? conditions += ` and id = '${criteria.id}'` : true;
dbConfig.getDB().query(`select * from article where 1 ${conditions}`, callback);
}
 
let createArticle = (dataToSet, callback) => {
console.log("insert into article set ? ", dataToSet,'pankaj')
dbConfig.getDB().query("insert into article set ? ", dataToSet, callback);
}
 
let deleteArticle = (criteria, callback) => {
let conditions = "";
criteria.id ? conditions += ` and id = '${criteria.id}'` : true;
console.log(`delete from article where 1 ${conditions}`);
dbConfig.getDB().query(`delete from article where 1 ${conditions}`, callback);
 
}
 
let updateArticle = (criteria,dataToSet,callback) => {
    let conditions = "";
let setData = "";
criteria.id ? conditions += ` and id = '${criteria.id}'` : true;
dataToSet.category ? setData += `category = '${dataToSet.category}'` : true;
dataToSet.title ? setData += `, title = '${dataToSet.title}'` : true;
console.log(`UPDATE article SET ${setData} where 1 ${conditions}`);
dbConfig.getDB().query(`UPDATE article SET ${setData} where 1 ${conditions}`, callback);
}
module.exports = {
getArticle : getArticle,
createArticle : createArticle,
deleteArticle : deleteArticle,
updateArticle : updateArticle,
getArticleDetail : getArticleDetail
}

Now one create Services folder and add a file article.js for all the logic of API

let async = require('async'),
parseString = require('xml2js').parseString;
 
let util = require('../Utilities/util'),
articleDAO = require('../DAO/articleDAO');
//config = require("../Utilities/config").config;
 
 
/**API to create the atricle */
let createArticle = (data, callback) => {
async.auto({
article: (cb) => {
var dataToSet = {
"category":data.category?data.category:'',
"title":data.title,
}
console.log(dataToSet);
articleDAO.createArticle(dataToSet, (err, dbData) => {
if (err) {
cb(null, { "statusCode": util.statusCode.FOUR_ZERO_ONE, "statusMessage": util.statusMessage.SERVER_BUSY });
return;
}
 
cb(null, { "statusCode": util.statusCode.OK, "statusMessage": util.statusMessage.DATA_UPDATED,"result":dataToSet });
});
}
//]
}, (err, response) => {
callback(response.article);
});
}
 
/**API to update the article */
let updateArticle = (data,callback) => {
async.auto({
articleUpdate :(cb) =>{
if (!data.id) {
cb(null, { "statusCode": util.statusCode.FOUR_ZERO_ONE, "statusMessage": util.statusMessage.PARAMS_MISSING })
return;
}
console.log('phase 1');
var criteria = {
id : data.id,
}
var dataToSet={
"category": data.category,
"title":data.title,
}
console.log(criteria,'test',dataToSet);
                    articleDAO.updateArticle(criteria, dataToSet, (err, dbData)=>{
                        if(err){
cb(null,{"statusCode":util.statusCode.FOUR_ZERO_ONE,"statusMessage":util.statusMessage.SERVER_BUSY});
                        return; 
                        }
                        else{
cb(null, { "statusCode": util.statusCode.OK, "statusMessage": util.statusMessage.DATA_UPDATED,"result":dataToSet });                        
                        }
                    });
}
}, (err,response) => {
callback(response.articleUpdate);
});
}
 
/**API to delete the subject */
let deleteArticle = (data,callback) => {
console.log(data,'data to set')
async.auto({
removeArticle :(cb) =>{
if (!data.id) {
cb(null, { "statusCode": util.statusCode.FOUR_ZERO_ONE, "statusMessage": util.statusMessage.PARAMS_MISSING })
return;
}
var criteria = {
id : data.id,
}
articleDAO.deleteArticle(criteria,(err,dbData) => {
if (err) {
console.log(err);
cb(null, { "statusCode": util.statusCode.FOUR_ZERO_ONE, "statusMessage": util.statusMessage.SERVER_BUSY });
return;
}
cb(null, { "statusCode": util.statusCode.OK, "statusMessage": util.statusMessage.DELETE_DATA });
});
}
}, (err,response) => {
callback(response.removeArticle);
});
}
 
/***API to get the article list */
let getArticle = (data, callback) => {
async.auto({
article: (cb) => {
articleDAO.getArticle({},(err, data) => {
if (err) {
cb(null, {"errorCode": util.statusCode.INTERNAL_SERVER_ERROR,"statusMessage": util.statusMessage.SERVER_BUSY});
return;
}
cb(null, data);
return;
});
}
}, (err, response) => {
callback(response.article);
})
}
 
/***API to get the article detail by id */
let getArticleById = (data, callback) => {
async.auto({
article: (cb) => {
let criteria = {
"id":data.id
}
articleDAO.getArticleDetail(criteria,(err, data) => {
if (err) {
console.log(err,'error----');
cb(null, {"errorCode": util.statusCode.INTERNAL_SERVER_ERROR,"statusMessage": util.statusMessage.SERVER_BUSY});
return;
}
cb(null, data[0]);
return;
});
}
}, (err, response) => {
callback(response.article);
})
}
 
module.exports = {
createArticle : createArticle,
updateArticle : updateArticle,
deleteArticle : deleteArticle,
getArticle : getArticle,
getArticleById : getArticleById
};

3. Create angular component for performing CRUD task of article

ng g component article

Above command will generate all required files for build article component and also automatically added this component to app.module.ts.

create src/app/article/article.component.css (0 bytes)
create src/app/article/article.component.html (23 bytes)
create src/app/article/article.component.spec.ts (614 bytes)
create src/app/article/article.component.ts (321 bytes)
update src/app/app.module.ts (390 bytes)

Now we need to add HttpClientModule to app.module.ts. Open and edit src/app/app.module.ts then add this import. And add it to @NgModule imports after BrowserModule. Now our app.module.ts will have following code:

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { ReactiveFormsModule } from '@angular/forms';
import { HttpModule } from '@angular/http';
 
import { AppComponent } from './app.component';
import { ArticleComponent } from './article.component';
import { ArticleService } from './article.service';
 
@NgModule({
imports: [
BrowserModule,
HttpModule,
ReactiveFormsModule
],
declarations: [
AppComponent,
ArticleComponent
],
providers: [
ArticleService
],
bootstrap: [
AppComponent
]
})
export class AppModule { }

Now create a service file where we will make all the request to the server for CRUD operation. Command for creating service is ng g service artcle , for now I have just created a file named it article.service.ts. Let's have a look in the code inside this file.

import { Injectable } from '@angular/core';
import { Http, Response, Headers, URLSearchParams, RequestOptions } from '@angular/http';
import { Observable } from 'rxjs';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/catch';
 
import { Article } from './article';
 
@Injectable()
export class ArticleService {
//URL for CRUD operations
    articleUrl = "http://localhost:3000/article";
    //Create constructor to get Http instance
    constructor(private http:Http) {
    }
    
    //Fetch all articles
getAllArticles(): Observable<Article[]> {
return this.http.get(this.articleUrl+"/get-article")
              .map(this.extractData)
         .catch(this.handleError);
 
}
    //Create article
createArticle(article: Article):Observable<number> {
     let cpHeaders = new Headers({ 'Content-Type': 'application/json' });
let options = new RequestOptions({ headers: cpHeaders });
return this.http.post(this.articleUrl+"/create-article", article, options)
.map(success => success.status)
.catch(this.handleError);
}
    //Fetch article by id
getArticleById(articleId: string): Observable<Article> {
        let cpHeaders = new Headers({ 'Content-Type': 'application/json' });
        let options = new RequestOptions({ headers: cpHeaders });
        console.log(this.articleUrl +"/get-article-by-id?id="+ articleId);
        return this.http.get(this.articleUrl +"/get-article-by-id?id="+ articleId)
             .map(this.extractData)
             .catch(this.handleError);
}   
    //Update article
updateArticle(article: Article):Observable<number> {
     let cpHeaders = new Headers({ 'Content-Type': 'application/json' });
        let options = new RequestOptions({ headers: cpHeaders });
return this.http.put(this.articleUrl +"/update-article", article, options)
.map(success => success.status)
.catch(this.handleError);
}
//Delete article    
deleteArticleById(articleId: string): Observable<number> {
        let cpHeaders = new Headers({ 'Content-Type': 'application/json' });
        let options = new RequestOptions({ headers: cpHeaders });
        return this.http.delete(this.articleUrl +"/delete-article?id="+ articleId)
             .map(success => success.status)
             .catch(this.handleError);
}   
    private extractData(res: Response) {
        let body = res.json();
return body;
}
private handleError (error: Response | any) {
        console.error(error.message || error);
        return Observable.throw(error.status);
}
}

In the above file we have made all the http request for the CRUD operation. Observables of rxjs library has been used to handle the data fetching from http request.

Now let's move to the next file, article.component.ts. Here we have all the login part of the app. Let's have a look code inside this file:

import { Component, OnInit } from '@angular/core';
import { FormControl, FormGroup, Validators } from '@angular/forms';
 
import { ArticleService } from './article.service';
import { Article } from './article';
 
@Component({
selector: 'app-article',
templateUrl: './article.component.html',
styleUrls: ['./article.component.css']
})
export class ArticleComponent implements OnInit {
//Component properties
allArticles: Article[];
statusCode: number;
requestProcessing = false;
articleIdToUpdate = null;
processValidation = false;
//Create form
articleForm = new FormGroup({
title: new FormControl('', Validators.required),
category: new FormControl('', Validators.required)   
});
//Create constructor to get service instance
constructor(private articleService: ArticleService) {
}
//Create ngOnInit() and and load articles
ngOnInit(): void {
     this.getAllArticles();
}
//Fetch all articles
 
getAllArticles() {
        this.articleService.getAllArticles()
         .subscribe(
data => this.allArticles = data,
                errorCode => this.statusCode = errorCode);
                
}
//Handle create and update article
onArticleFormSubmit() {
     this.processValidation = true;
     if (this.articleForm.invalid) {
     return; //Validation failed, exit from method.
     }
     //Form is valid, now perform create or update
this.preProcessConfigurations();
     let article = this.articleForm.value;
     if (this.articleIdToUpdate === null) {
     //Generate article id then create article
this.articleService.getAllArticles()
     .subscribe(articles => {
            
         //Generate article id    
         let maxIndex = articles.length - 1;
         let articleWithMaxIndex = articles[maxIndex];
         let articleId = articleWithMaxIndex.id + 1;
         article.id = articleId;
         console.log(article,'this is form data---');
         //Create article
    this.articleService.createArticle(article)
             .subscribe(successCode => {
                    this.statusCode = successCode;
                    this.getAllArticles();  
                    this.backToCreateArticle();
                 },
                 errorCode => this.statusCode = errorCode
             );
         });        
     } else {
  //Handle update article
article.id = this.articleIdToUpdate;        
     this.articleService.updateArticle(article)
     .subscribe(successCode => {
         this.statusCode = successCode;
                 this.getAllArticles();  
                    this.backToCreateArticle();
             },
         errorCode => this.statusCode = errorCode);  
     }
}
//Load article by id to edit
loadArticleToEdit(articleId: string) {
this.preProcessConfigurations();
this.articleService.getArticleById(articleId)
     .subscribe(article => {
            console.log(article,'poiuytre');
         this.articleIdToUpdate = article.id;
                    this.articleForm.setValue({ title: article.title, category: article.category });
                    this.processValidation = true;
                    this.requestProcessing = false;
         },
         errorCode => this.statusCode = errorCode);
}
//Delete article
deleteArticle(articleId: string) {
this.preProcessConfigurations();
this.articleService.deleteArticleById(articleId)
     .subscribe(successCode => {
         //this.statusCode = successCode;
                    //Expecting success code 204 from server
                    this.statusCode = 204;
                 this.getAllArticles();  
                 this.backToCreateArticle();
             },
         errorCode => this.statusCode = errorCode);
}
//Perform preliminary processing configurations
preProcessConfigurations() {
this.statusCode = null;
     this.requestProcessing = true;
}
//Go back from update to create
backToCreateArticle() {
this.articleIdToUpdate = null;
this.articleForm.reset(); 
     this.processValidation = false;
}
}

Now we have to show the task over browser, So lets have a look inside article.component.html file.

<h1 class="text-center">Angular 7 CRUD Demo App</h1>
<h3 class="text-center" *ngIf="articleIdToUpdate; else create">
Update Article for Id: {{articleIdToUpdate}}
</h3>
<ng-template #create>
<h3 class="text-center"> Create New Article </h3>
</ng-template>
<div>
<form [formGroup]="articleForm" (ngSubmit)="onArticleFormSubmit()">
<table class="table-striped" style="margin:0 auto;">
<tr><td>Enter Title</td><td><input formControlName="title">
   <label *ngIf="articleForm.get('title').invalid && processValidation" [ngClass] = "'error'"> Title is required. </label>
 </td></tr>
<tr><td>Enter Category</td><td><input formControlName="category">
   <label *ngIf="articleForm.get('category').invalid && processValidation" [ngClass] = "'error'"> Category is required. </label>
  </td></tr>  
<tr><td colspan="2">
   <button class="btn btn-default" *ngIf="!articleIdToUpdate">CREATE</button>
    <button class="btn btn-default" *ngIf="articleIdToUpdate">UPDATE</button>
   <button (click)="backToCreateArticle()" *ngIf="articleIdToUpdate">Go Back</button>
  </td></tr>
</table>
</form>
<br/>
<div class="text-center" *ngIf="statusCode; else processing">
<div *ngIf="statusCode === 201" [ngClass] = "'success'">
   Article added successfully.
</div>
<div *ngIf="statusCode === 409" [ngClass] = "'success'">
Article already exists.
</div>   
<div *ngIf="statusCode === 200" [ngClass] = "'success'">
Article updated successfully.
</div>   
<div *ngIf="statusCode === 204" [ngClass] = "'success'">
Article deleted successfully.
</div>   
<div *ngIf="statusCode === 500" [ngClass] = "'error'">
Internal Server Error.
</div> 
</div>
<ng-template #processing>
  <img *ngIf="requestProcessing" src="assets/images/loading.gif">
</ng-template>
</div>
<h3 class="text-center">Article List</h3>
<table class="table-striped" style="margin:0 auto;" *ngIf="allArticles">
<tr><th> Id</th> <th>Title</th><th>Category</th><th></th><th></th></tr>
<tr *ngFor="let article of allArticles" >
<td>{{article.id}}</td> <td>{{article.title}}</td> <td>{{article.category}}</td>
  <td><button class="btn btn-default" type="button" (click)="loadArticleToEdit(article.id)">Edit</button> </td>
  <td><button class="btn btn-default" type="button" (click)="deleteArticle(article.id)">Delete</button></td>
</tr>
</table>

Now since I have created server and client two separate folder for nodejs and angular task. So will run both the apps with npm start over two tabs of terminal.

On the browser, over link http://localhost:4200. App will look like below

Angular CRUD with Nodejs and MySQL Example

That’s all for now. Thank you for reading and I hope this post will be very helpful for creating CRUD operations with angular7,node.js & mysql.

================================================

Thanks for reading :heart: If you liked this post, share it with all of your programming buddies! Follow me on Facebook | Twitter

How to display the current date and time in AngularJS

How to display the current date and time in AngularJS

In this article, you'll learn how to display the current date and time in AngularJS application.

You can get current date and time with specific format like yyyy-mm-dd, dd/mm/yyyy, mm-dd-yyyy hh:mm:ss etc. You will display current date and time using angular js ng-bind directive.

You can see bellow example we use Date object for getting current date and time. So you can see bellow syntax, example, full example and also output bellow. let's see it and i hope it will help you.

Syntax:

{{ 'Date Object' | date: 'Date Format' }}

Example:

{{ CurrentDate | date: 'dd/MM/yyyy' }}

index.html:

<!doctype html>

<html>

<head>

  <title>How to get Current Date and Time in AngularJS? - ItSolutionStuff.com</title>

  <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.4/angular.min.js"></script>

</head>

<body ng-app='myapp'>

  
  <div ng-controller="myCtrl">

    <p>{{ CurrentDate | date:'dd/MM/yyyy'}}</p>

    <p>{{ CurrentDate | date:'MM/dd/yyyy'}}</p>

    <p>{{ CurrentDate | date:'dd/MM/yyyy HH:mm:ss'}}</p>

  </div>

  

</body>

  

<script type="text/javascript">

   

   var app = angular.module('myapp', []);

  

   app.controller('myCtrl', ['$scope', '$http', function ($scope, $http) {

      $scope.CurrentDate = new Date();

   }]);

  

</script>

</html>

Output:

02/07/2019
  
07/02/2019
  
02/07/2019 09:23:47

Best 17 Angular Libraries Every Angular Developers Should Know in 2019

Best 17 Angular Libraries Every Angular Developers Should Know in 2019

In this article, we list 17 useful Angular libraries that can help as you develop applications with Angular.

Angular is a web development framework for building robust single-page applications and systems. Developed and maintained by Google and community maintainers, Angular is a great library for building large scale web applications.

Angular has a huge and active community, thus, a lot of libraries have been introduced by the community to plug holes and extend the tooling provided by Angular. Today, we’ll look at some libraries that can be introduced into existing applications — libraries ranging from utility libraries to UI component libraries.

1. ng-bootstrap

It seems fair to start with the Angular implementation of the most popular UI library. The ng-bootstrap library was built from the top down using TypeScript. Unlike the previous version, it has dropped jQuery as a dependency, specifying Bootstrap’s CSS as its only other dependency. With most JavaScript components implemented, the library seems like a complete solution when using Bootstrap with Angular — as active development is ongoing, more components will be included. With almost 7k stars on GitHub, ng-bootstrap seems like a very popular choice for a lot of Angular developers.

The Angular.js version of this project is still available here, although it isn’t actively maintained.

Best 17 Angular Libraries

2. Angular Google Maps

Using the Google Maps library in Angular is always a serious hassle because the library is loaded using a script tag, so type definitions aren’t readily available. This causes some compile errors that need a lot of hacking to get rid of.

The Angular Google Maps library provides services and directives for implementing Google Maps services. There are directives available for creating maps, using markers, etc. The library also provides an async function that is useful for checking if the Google Maps library is loaded on the webpage.

The project has amassed almost 2k stars on GitHub. Visit their documentation to get started.

Best 17 Angular Libraries

3. ngx-translate

Building an application that supports multiple languages can be a serious struggle, especially for single-page applications. The ngx-translate is a great library for managing multiple languages in your Angular application. It provides services to load translations that can be used throughout the application. Translations can be defined and loaded using the TranslateService, and onChange listeners are also available for handling language changes within the application.

The setup is pretty straightforward, and the library is well documented with detailed examples. Visit their GitHub page to get started.

4. Angular2-jwt

Managing single-page applications that use web tokens for authentication usually requires using interceptors to append headers to network requests. While this is easy to implement, it is difficult to filter out requests that don’t require access tokens. This is where this impressive library comes in. Using the angular-jwt package by Auth0, you can load access tokens from the local storage or session storage. It provides an HttpInterceptor that appends authentication headers to the requests. The ability to blacklist or whitelist a domain is also available.

With almost 2k stars on GitHub, it is a well-documented library with adequate examples and only requires a few steps to get started.

5. AngularFire2

Looking to implement real-time functionality in your Angular application? Well look no further, this library uses the power of RxJS, Firebase and Angular to deliver data synchronization in real time. It also provides services and providers to query documents and collections on Cloud Firebase and the realtime database, handles authentication using Firebase, handles file upload to Cloud Storage, and sends Push Notifications. The package also supports server-side rendering and offline functionality. You can easily import each individual module to handle whichever functionality is required in your application. All documentation can be found in the library’s GitHub page.

6. ng2-file-upload

Handling file uploads in any single-page application isn’t a task that’s fun to deal with. It would be great if an external library could handle file upload within your web application. Valon-software, the makers of ngx-bootstrap, has you covered with ng2-file-upload, a library that makes file upload a breeze.

The library supports drag-and-drop functionality alongside the good old file select implementation. It provides a utility class (FileUploader) that handles the different file upload methods. It also provides events to monitor the file upload progress, as well as errors and success during the upload.

The library is actively maintained and has almost 2k stars on Github.

Best 17 Angular Libraries

7. Angular Material 2

The list wouldn’t be complete without mentioning a library that implements Google’s Material Design specifications. Angular Material 2 is a components library created by the Angular team. It features a set of components implementing the Material Design specs, ranging from buttons to dialogs, bottom sheets, etc. It features fully customizable themes and a rich set of components that can be used to quickly build an application. Angular Material 2 comes with almost 40 components, with more components under development and four pre-built themes.

Get started with Angular Material 2 by visiting their documentation or GitHub page.

Angular Libraries

8. ngrx/store

Managing state in small applications isn’t really complicated and state can be easily managed within individual components, but when there’s a need to share data between several components, the need for a proper state management system arises. NgRx offers reactive libraries optimized for Angular. It offers reactive statement for Angular in a package called ngrx/store. This package uses RxJS technologies to offer state management similar to Redux. The store allows developers write consistent and performant applications in a state-controlled environment. Very similar to Redux, the ngrx/store library uses Action, Reducers, Select and Store to manage the data flow within Angular applications. Get started with ngrx/store by following the steps listed in the library’s documentation.

9. Cloudinary Angular SDK

Cloudinary is SaaS web platform for managing media assets on mobile and web applications. It provides services for upload, storage, manipulation and delivery of media assets. Cloudinary offers an SDK for Angular that can be used in Angular applications for resizing and image conversion. The SDK can also be used for delivering different image sizes on different screens. It allows for easy delivery of video and image assets from Cloudinary’s storage.

Visit Cloudinary’s website to read more about about end-to-end management of media assets. The SDK can be found here on GitHub.

10. ng2-pdf-viewer

The ng2-pdf-viewer is a library for viewing and interacting with PDFs on a web application. The library providers a component for rendering PDF documents. The component can also be used for performing operations on the selected PDF like: resizing, rotating, searching through the document, etc. You can render files locally or provide a link to an external document. This library is great for managing PDF files on your web application, and there’s a lot it can handle using directives.

Visit their official documentation page or their page on GitHub.

11. ngx-charts

When working with data in a web application, the need for data visualization arises, thus the need for a data visualization library that can handle various forms of customizations while rendering. ngx-charts is quite interesting because their charts rely mostly on using Angular to animate SVGs, which offers more speed and flexibility as the library has been optimized for use in Angular.

It also uses d3 for math functions, scales and axis, etc. It comes with ten or more color schemes while making the charts fully customizable using CSS. Visit their demo page to view the different themes and color schemes available and their GitHub page to get started with the library. The library has garnered almost 3k stars on GitHub and is actively maintained.

Angular Libraries

12. ng-seed/universal

This great library has so many features packaged within it, it should be the Swiss army knife for every Angular developer. It consists of the following packages:

  • ngx-meta: for handling meta tags, title tags and SEO enhancement.
  • ngx-cache: for managing application wide data.
  • ngx-auth: for managing jwt-based authentication.

It comes with a couple of other packages for handling server-side rendering, lazy loading, state management and webpack configurations.

Clone the repository on GitHub and follow the instructions to get started.

13. Augury

When building web applications, browser DevTools play an important part in the development process. It provides features for debugging, diagnosing and editing web applications. When dealing with Angular applications, DevTools only lets you interact with the end product of your code, which means your Angular components, directives, etc. have been converted to JavaScript, HTML and CSS.

Augury as a browser extension allows you debug and visualize your Angular application in its pre-compiled state. With Augury, you can inspect your components and ensure they’re functioning as they should. Augury works better with source maps, so ensure that you generate source maps for a better experience while using Augury.

You can download the extension for Chrome or Firefox. Visit their GitHub page if you wish to contribute or raise issues.

14. ngx-moment

Moment.js is a utility library for manipulating time (not what you think). It provides a set of functions for parsing, formatting, validating, etc. dates and time using JavaScript. ngx-moment builds on the Moment.js library, providing Angular pipes for use within components. It comes packed with pipes for the functions provided by Moment.js, thus effectively eliminating the overhead of importing the functions into every component for use.

The library is actively maintained and is relatively easy to get started with. Visit the GitHub page and run through the documentation to get started.

15. ngx pipes

Fun times when Angular.js came packed with a set of pipes for transforming data before rendering. Filters is what they were called in Angular.js. Well, for some performance reasons, more recent Angular versions don't include pipes for filtering or ordering lists. Angular pipes is a library that contains a set of useful pipes for use in your Angular project. It contains pipes for performing actions like: trimming, reversing, matching and scanning strings, plucking, shuffling and ordering Arrays.

It is well documented and easy to integrate. Getting started should be a breeze and, soon enough, you’ll start getting more done with pipes. Visit the documentation or their GitHub page to get started.

16. Angular Epic Spinners

When dealing with interactivity on a webpage, you have to think about notifying users when processes not visible to them are ongoing. When the time comes, you are required to display a loading indicator. Some sites have custom loading indicators for their application, but if you’d rather have a set of easily available spinners, then this spinners library should be your go-to.

Angular Epic Spinners is built on the epic-spinners library, with Angular components for each component available in the library. Each component can be imported as an individual module and rendered anywhere within your application. You can select from any of 20 indicators available in the library. You can view the demo page or head straight to their GitHub page.

Best 17 Angular Libraries Every Angular Developers Should Know in 2019

17. Apollo Angular

GraphQL is a query language for APIs and a runtime for fulfilling queries made with data. It allows developers to request for data they need in specific areas of their application. Apollo client is a library used to consume data from GraphQL endpoints. Apollo has different client libraries for consuming data on the frontend – libraries exist for React, Angular, Vue, etc.

Apollo Angular is a client library built for Angular applications to consume GraphQL endpoints. Apollo Angular is agnostic of any router used within the application. It also supports server-side rendering. The documentation page is well written with adequate examples to help you get started.

Summary

People sometimes avoid using external libraries in their applications during development. While that’s acceptable in some instances, external libraries can help reduce development time significantly. There are a lot of libraries that might have achieved whatever you’re struggling with during development. The task is finding the right library that fits into your applications and ensuring it fulfills its purpose. Happy coding.

Thanks for reading

If you liked this post, share it with all of your programming buddies!

Follow us on Facebook | Twitter

Further reading

Angular 8 (formerly Angular 2) - The Complete Guide

Angular & NodeJS - The MEAN Stack Guide

The Web Developer Bootcamp

Angular 8 is coming

MEAN Stack Angular 8 CRUD Web Application

Angular 8 + Spring Boot 2.2: Build a CRUD App Today!

Build A Real World Beautiful Web APP with Angular 8 — A to Z

Build A Real World Beautiful Web APP with Angular 8 — A to Z

Build A Real World Beautiful Web APP with Angular 8 — A to Z Ultimate Guide (2019) . No more ugly tutorials projects ! I wanted the brand to reflect it's core values through the design and you can see it screaming: Minimalistic, Simple, Clean and Easy To Use! Dark Mode ❤️

No more ugly tutorials projects ! No more fictional brands examples!

Today I am going to show you how to build a real world beautiful weather app that is production ready from scratch starting from design to development all the way to deployment using Adobe XD, Angular 7 & Firebase!

Why?

I am sick and tired of developers using background red and basic ugly UI CSS to teach people things that are not production ready and I feel upset when I see thousands of beautiful but practically unusable design projects on Behance and Dribble that designers never took the time to make them useful in any way by taking an extra step to build & deploy those apps so people can benefit from them.

1 man +1 entire project +1 blog post + maybe a video tutorial soon :)

⚡️ Supercharged with all the bells and whistles 😮 🔔

  • Based on the latest & greatest version of Angular v7
  • Firebase Authentication and Firestore (using AngularFire Lite🔥)
  • Server Side Rendered (SEO)
  • 100/100 Lighthouse PWA score
  • Modern CSS: Grid Layout & Flex Box
  • Mobile friendly and full responsive
  • 2 Modes : Dark Mode & Light Mode
  • Beautiful Minimalistic Design
  • Note: This tutorial is part of a series that will cover how to build this app and the long list of features listed above progressively and if you want me to notify you when a new tutorial comes up you can signup to my newsletter here. 💌

Step 1: Design

I have designed the weather app in latest version of Adobe XD. You can download the design file from here so you can see how the different layers stack up to form the final design.angular 8 tutorial

A. Branding

I wanted the brand to reflect it’s core values through the design and you can see it screaming: Minimalistic, Simple, Clean and Easy To Use!

  • Colors:

2 saturated primary colors to give it that fresh modern look

  • Typefaces:

No custom fonts in here just the stock ‘Sans Serif’ so we don’t have to load any fonts of the CDN for maximum performance.

  • Logo:

Logos are not rocket science! but logo designers try hard to make you believe so! but in reality they mostly get paid for the ceremony they create and the film they produce to hypnotize the client.

Think about it for a moment the Nike logo ( a check mark shape ) designed for 35$ , Pepsi (rotating the old simple 3 colored waves logo in 2008 costed the company $1,000,000 that is one million dollars folks!)

anyways here the logo I designed a simple M shape that is upside down using two intersecting cards colored using the primary palate of the brand. Simple, effective and most importantly it costs 0$ 😄

B. UI / UX

The app mainly uses cards with a soft shadow as it was pieces of papers floating. Only the most important pieces of information is displayed upfront to avoid cluttering the UI and the fluid animations give us extra points in the UX department.

  • Light Mode (Default) 🌲

Dark Mode ❤️

  • icons:

the user must tell the weather conditions at a glance so I designed a custom pack of icons from scratch to go well with the whole website design and here is the first version of the pack

  • illustrations:

We want to reduce the user efforts to guess as much as possible but in the same time fill up the empty space with a pleasing visual representation of almost everything.

The illustrations I created should help the user identify the selected city without reading any single letter because people are lazy nowadays!

For the cities illustrations I went for a gradient flashy design style with a saturated color palette for a visually rich city details page.

I know how crazy you think I am when I processed to design an illustration for each city the users selects. Obviously, this is an insane amount of work as there is 195 countries in the world but I started with 4 illustrations for now and I kept the rest of the 191 to design throughout the years😅

  1. Tunisia Illustration:

2. Qatar Illustration:

3. Japan Illustration:

4. France Illustration:

and for the complete illustrations project click here to see it on my Behance profile.

Step 2: Development

Here were most guides skip most of the early steps and assume you know everything and then they show you the result to get impressed and nothing more.

But, I am going to try my best to make everyone capable to follow this tutorial even beginners without making the tutorial insanely long and start things off with installing nodejs and the angular CLI which will generate the basic structure and scaffold our Angular 7 app.

install nodejs from the official website from here and open your command line prompt in your OS and install the Angular CLI and typescript globally using the following lines:

npm i -g typescript 
npm i -g @angular/cli

after that just run the following command to generate the App using the Angular CLI and don’t forget to add the routing flag which create a good starting point for the app pagination and routing.

ng new Minimus --routing

once the CLI finishes generating your project files and installs all the dependencies it needs of NPM we are going to start the development server and open our app in the browser using the following command (-o flag is just to open a new browser tab automatically with the correct URL pointing to your app)

ng serve -o 

A. Templates And Styling

But before I start want to make sure that you get the most of this tutorial so please don’t just copy and paste, read the code and then open your editor and browser side by side and type everything down in your own way because that is the only way you can learn. I typed everything myself to build this app and you should too so you can understand everything thing form start to finish.

Now back to the project where we just completed the basic setup of the app and now we are going to start writing our HTML and CSS. So open up your project in your favourite text editor and let’s dive right in Woooho!

  • App Component

we are going to use the root component app.component as our navbar component and we are going to show it and hide it conditionally depending if the user is logged in or not (We will implement Authentication in Part II with Angularfire Lite in the sires).

Here I thought of using some components off the angular material library but I decided to keep the production app as light as possible by avoiding any 3rd party library unless it is really necessary like Angularfire Lite.

First things first open app.component.hml and delete all the boilerplate HTML automatically generated by the CLI and get your HTML to look like something like this:




    
        Welcome Back
        
            ![](https://avatars3.githubusercontent.com/u/5658460?s=460&v=4)
        
        
            Hamed Baatour
            [email protected]
        
    
    

    
    

    




    

        
          
                
          

            
              
            

        

        ### Today



        
            Light

            
                
                  
                  
            


            Dark
        

    

    

    -->
    
        

        
    

    

    
        Copyright © 2018 Minimus
    

Minimus - app.component.html

Bonus Tip (optional):

If you want to use Emmet (an editor plugin) as a faster way of typing HTML you can refer to the plugin cheat sheet I usually refer to.

SVG icons

To get the svg icons and the logo here is a list of gists I created on my Github profile so you can use them (copying and pasting is allowed here) :

— Styling the root component

and it’s time for some css to style our navbar, just take a quick look at the css below and look at the achieved result and then go ahead and write down your own css as it does not have to be exactly the same.

“be inspired don’t copy because everybody is an artist”

.root__container {
  width: 100vw;
  height: 100vh;
  display: grid;
  grid-template-columns: auto;
  grid-template-rows: 0.5fr auto;
  position: relative;
}

/*
================
    Header
================
*/

/*
    Slide Menu
= = = = = = = = =
*/
.side-menu__container {
  position: fixed;
  left: 0;
  top: 0;
  width: 100%;
  height: 100%;
  overflow: hidden;
  pointer-events: none;
  z-index: 25;
}

.side-menu__container-active {
  pointer-events: auto;
}

.side-menu__container::before {
  content: '';
  cursor: pointer;
  position: absolute;
  display: block;
  top: 0;
  left: 0;
  height: 100%;
  width: 100%;
  background-color: #0c1066;
  opacity: 0;
  transition: opacity 300ms linear;
  will-change: opacity;
}

.side-menu__container-active::before {
  opacity: 0.3;
}

.slide-menu {
  box-sizing: border-box;
  transform: translateX(-103%);
  position: relative;
  top: 0;
  left: 0;
  z-index: 10;
  height: 100%;
  width: 90%;
  max-width: 26rem;
  background-color: white;
  box-shadow: 0 0 2rem rgba(0, 0, 255, 0.1);
  display: grid;
  grid-template-columns: 1fr;
  grid-template-rows: 2fr 4fr 1fr;
  grid-gap: 1rem;
  transition: transform 300ms linear;
  will-change: transform;
}

.slide-menu-active {
  transform: none;
}

.menu-header {
  background: linear-gradient(to right, #00FF9B, #5f84fb);
  display: grid;
  grid-template-rows: 1fr 4fr;
  grid-template-columns: 1fr 4fr;
  grid-template-areas: "greeting greeting" "image details";
  box-sizing: border-box;
  width: 100%;
  align-content: center;
  color: white;
  box-shadow: 0 0.5rem 2rem rgba(0, 0, 255, 0.2);
}

.greeting__text {
  grid-area: greeting;
  font-size: 1.25rem;
  letter-spacing: 0.15rem;
  text-transform: uppercase;
  margin-top: 1rem;
  justify-self: center;
  align-self: center;
}

.account-details {
  grid-area: details;
  display: flex;
  flex-flow: column;
  margin-left: 1rem;
  align-self: center;
}

.name__text {
  font-size: 1.15rem;
  margin-bottom: 0.5rem;
}

.email__text {
  font-size: 0.9rem;
  letter-spacing: 0.1rem;
}

.menu-body {
  display: grid;
  width: 100%;
}

.profile-image__container {
  grid-area: image;
  margin-right: 0.5rem;
  border-radius: 50%;
  height: 4rem;
  width: 4rem;
  overflow: hidden;
  display: flex;
  justify-content: center;
  align-items: center;
  background-color: white;
  align-self: center;
  margin-left: 2rem;
}

.profile__image {
  max-width: 4rem;
}

/*Header*/
.main__header {
  width: 100%;
  display: grid;
  grid-template-columns: 1fr 1fr 0.25fr;
  grid-template-rows: 1fr;
  box-shadow: 0 0 2rem rgba(0, 0, 255, 0.1);
  height: 4rem;
  margin: 0;
  align-items: center;
  transition: background-color 500ms linear;
  animation: 1s ease-in-out 0ms 1 fadein;
}

.main__header-dark {
  background-color: #2B244D;
  color: white;
}

.toggle-button__container {
  cursor: pointer;
  position: relative;
  margin: 0 0.5rem;
}

.mode-toggle__input {
  -webkit-appearance: none;
  -moz-appearance: none;
}

.mode-toggle__bg {
  height: 1rem;
  width: 2rem;
  border-radius: 0.5rem;
  background-color: rgba(0, 0, 0, 0.5);
  display: inline-block;
  transition: background-color 300ms linear;
}

.mode-toggle__circle {
  height: 1.30rem;
  width: 1.30rem;
  background-color: #2B244D;
  position: absolute;
  top: -0.2rem;
  border-radius: 50%;
  box-shadow: 0 0 0 rgba(0, 0, 255, 0.5);
  transition: left 300ms linear;
  left: 0.1rem;
}

.mode-toggle__circle-checked {
  background-color: white;
  left: 1.75rem;
}

.mode-toggle__bg-checked {
  background-color: #FF0070;
}

.mode-toggle__text {
  font-size: 0.75rem;
  text-transform: uppercase;
  letter-spacing: 0.1rem;
}

/*Content*/
.left__section {
  display: grid;
  grid-template-rows: 1fr;
  grid-template-columns: 1fr 1fr;
  max-width: 5rem;
}

.date__text {
  text-transform: uppercase;
  letter-spacing: 0.1rem;
  display: inline;
  margin: 0.5rem 0;
}

/*SVGs*/
.hamburger__icon {
  position: relative;
  z-index: 35;
  height: 1rem;
  padding: 0.5rem 1.5rem;
  margin-right: 1rem;
  cursor: pointer;
}

.logo__icon {
  height: 2rem;
  margin-left: 1rem;
}

.logo__text {
  fill: #2B244D;
}

.logo__text-dark {
  fill: #ffff;
}

.hamburger__icon__fill {
  fill: #2B244D;
}

.hamburger__icon__fill-dark {
  fill: #ffff;
}

/*
================
    Body
================
*/

.main-container__bg {
  height: 100%;
  width: 100%;
  position: absolute;
  top: 0;
  left: 0;
  z-index: -2;
  opacity: 0;
  background: white;
  transition: opacity 300ms linear;
}

.main-container__bg-dark {
  opacity: 1;
  background: linear-gradient(to bottom, #B290FF, #2E1D65);
  transition: opacity 300ms linear;
}

/*
================-
    Footer
================
*/
.main__footer {
  background: transparent;
  position: absolute;
  bottom: 1rem;
  left: 1.5rem;
  z-index: 100;
}

.copyright__text {
  letter-spacing: 0.1rem;
  color: white;
}

@media only screen and (max-width: 300px) {
  .slide-menu {
    width: 100%;
  }
}

Minimus - app.component.css

CSS explanation:

  • Layout:
display: grid;  
grid-template-columns: auto;
grid-template-rows: 0.5fr auto;

here I am basically using CSS grid layout to divide the page in order to have one small top row for the nav bar and a much larger second row to contain our router outlet which is the main content of the page. Think about it like this small diagram:

  • sidenav
.side-menu__conatiner {
position: fixed; 
left: 0;
top: 0 }

places the sidenav container at the top left of the viewport

.side-menu__container::before {...}

used to fade the background with a blue tint when the sidebar slides in

will-change: opacity;

is used to inform the browser ahead of time that the entire background opacity will change so we can achieve a better rendering performance you can read more here.

.slide-menu { transform: translateX(-103%); }

this should pull the side-menu out of the view port and when we click the hamburger menu we should add a .slide-menu-active class which will reset the transform css property and endup sliding the menu from the left of the viewport:

.slide-menu-active {  transform: none; }

  • mode toggle

a small UI element trick that I want to cover is how I styled is the theme toggle button. Basically I set the appearance css property of a standard checkbox input to none just to completely remove any default styling of the input (this is different from display: none which completely hides the element) and then I used two different classes for the toggle button background and the circle to change the color and the position of the circle depending on a boolean variable stored on the component using the built in ngClass directive in Angular which let you toggle css classes easily.

  • Home Component

this is the home component where we show the different weather cards of the user’s favourite cities and from there he can click on the add city card which route him to the add city component to add a new city to his home page.

first we need to generate this component using the CLI using the following command:

ng g c home

the HTML markup has nothing more than a container and two other components for now but we will dynamically add cards depending on the user favourite cities in the upcoming parts of the tutorial:



  
  


  • Weather Card Component

Here is used the ngSwitch directive to check for the weather conditions and change


  
  Paris
    

      
        

        
        

        
        


        
        

      
    
    
        {{ currentTemp }}
        °
        {{ condition }}
    
    
        
            
                
            

            {{ minTemp }}
            Min
        
        
            
                
            
            {{ maxTemp }}
            Max
        
    

Miminus - weather-card.component.html

and now to some CSS styling of the component:

/*
====================
Weather Card Styling
====================
*/
.weather__card {
  display: grid;
  grid-template-columns: 1fr;
  grid-template-rows: 1fr 1fr 1fr;
  box-shadow: 0 0 2rem rgba(0, 0, 255, 0.1);
  justify-items: center;
  padding: 2rem;
  margin: 2rem;
  width: 19rem;
  height: 30rem;
  cursor: pointer;
  background-color: white;
  border-radius: 1.75rem;
  animation: 1.25s ease-in-out 0ms 1 fadein;
}

.weather__card-dark {
  background: linear-gradient(to bottom, #711B86, #00057A);
  color: white;
}

.city-name__text {
  text-transform: uppercase;
  font-size: 1.4rem;
  letter-spacing: 0.1rem;
  margin-bottom: 1rem;
}

.temperature__text {
  align-self: end;
  width: 100%;
  font-size: 4rem;
  font-weight: 100;
  letter-spacing: 0.1rem;
}

.temperature-metric__text {
  text-align: start;
  font-size: 3rem;
}

.min-max__container {
  display: grid;
  grid-template-rows: 1fr;
  grid-template-columns: 1fr 1fr;
  align-items: center;
}

.min__container, .max__container {
  margin: 1rem 3rem;
  display: grid;
  grid-template-columns: 1fr;
  grid-template-rows: 1fr 1fr;
}

.min-arrow__icon, .max-arrow__icon {
  height: 1.25rem;
  margin: auto;
}

.max-arrow__icon {
  margin-bottom: -0.05rem;
}

.weather-condition__text {
  display: block;
  font-size: 1rem;
  text-transform: uppercase;
  letter-spacing: 0.1rem;
  text-align: center;
}

.max__text {
  color: #FF0070;
}

.min__text {
  color: #00FF9B;
}

.max__text, .min__text {
  font-size: 1rem;
  text-align: center;
}

.max-temperature__text, .min-temperature__text {
  text-align: center;
  font-size: 2rem;
}

.weather-icon__container {
  width: 10rem;
  margin-bottom: 2rem;
  display: flex;
  justify-content: center;
}

.weather-icon__container > svg {
  width: 10rem;
}

Minimus - weather-card.component.css

DarkMode

In the CSS you can notice that I added two classes for most UI elements and the reason for this is we want to add extra css classes for the dark theme with -dark suffix so we can toggle them afterwards using again the ngClass directive based on the theme toggle button state.

  • Add Card Component

Here is the add card component I have added a div wrapper that has a conditional dark mode ngClass directive like most of the UI element and I have added a the Angular router routerLink attribute to navigate the user to the add city page when the card is clicked


  
  Add city
  
  
    
    
  
  

Minimus - add-card.component.html

in terms of CSS again nothing complicated here as the main card uses also gird layout to create 2 rows to evenly space its content. don’t forget also to add the box-shadow property to add some light drop shadow to the card.

.add__card {
  background-color: #ffffff;
  box-shadow: 0 0 2rem rgba(0, 0, 255, 0.1);
  display: grid;
  grid-template-columns: 1fr;
  grid-template-rows: 1fr 1fr;
  padding: 2rem;
  margin: 2rem;
  width: 19rem;
  height: 30rem;
  justify-items: center;
  cursor: pointer;
  border-radius: 1.75rem;
  animation: 1.25s ease-in-out 0ms 1 fadein;
  color: #443282;
}

.add__card-dark {
  background: linear-gradient(to bottom, #711B86, #00057A);
  color: white;
}

.card__title {
  text-transform: uppercase;
  letter-spacing: 0.1rem;
}

.city__illustration {
  width: 20rem;
}

.body__container {
  align-self: end;
  display: flex;
  justify-content: space-between;
  align-items: center;
  flex-flow: column;
}

.add__icon {
  width: 10rem;
  margin-bottom: 1.15rem;
}

Minimus - add-card.component.css

  • Details Component

Here I injected the weather service to retrieve the weather data (more on that later) and set each day name, temperature and weather condition in a separate public variable that I can access in the template to display it:

import {Component, OnDestroy, OnInit} from '@angular/core';
import {ActivatedRoute} from '@angular/router';
import {WeatherService} from '../../services/weather/weather.service';
import {Subscription} from 'rxjs';

@Component({
  selector: 'app-details',
  templateUrl: './details.component.html',
  styleUrls: ['./details.component.css']
})
export class DetailsComponent implements OnInit, OnDestroy {

  city: string;
  state: string;
  temp: number;
  hum: number;
  wind: number;

  today: string;

  day1Name: string;
  day1State: string;
  day1Temp: number;


  day2Name: string;
  day2State: string;
  day2Temp: number;

  day3Name: string;
  day3State: string;
  day3Temp: number;

  day4Name: string;
  day4State: string;
  day4Temp: number;

  day5Name: string;
  day5State: string;
  day5Temp: number;

  sub1: Subscription;
  sub2: Subscription;
  sub3: Subscription;
  sub4: Subscription;
  sub5: Subscription;

  constructor(public activeRouter: ActivatedRoute, public weather: WeatherService) {
  }

  ngOnInit() {

    const todayNumberInWeek = new Date().getDay();
    const days = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
    this.today = days[todayNumberInWeek];

    this.activeRouter.paramMap.subscribe((route: any) => {

      this.city = route.params.city;
      this.sub1 = this.weather.getWeatherState(this.city).subscribe((state) => this.state = state);
      this.sub2 = this.weather.getCurrentTemp(this.city).subscribe((temperature) => this.temp = temperature);
      this.sub3 = this.weather.getCurrentHum(this.city).subscribe((humidity) => this.hum = humidity);
      this.sub4 = this.weather.getCurrentWind(this.city).subscribe((windspeed) => this.wind = windspeed);
      this.sub5 = this.weather.getForecast(this.city).subscribe((data: any) => {
        console.log(data);
        for (let i = 0; i < data.length; i++) {
          const date = new Date(data[i].dt_txt).getDay();
          console.log(days[date]);
          if (((date === todayNumberInWeek + 1) || (todayNumberInWeek === 6 && date === 0)) && !this.day1Name) {
            this.day1Name = days[date];
            this.day1State = data[i].weather[0].main;
            this.day1Temp = Math.round(data[i].main.temp);

          } else if (!!this.day1Name && !this.day2Name && days[date] !== this.day1Name) {
            this.day2Name = days[date];
            this.day2State = data[i].weather[0].main;
            this.day2Temp = Math.round(data[i].main.temp);

          } else if (!!this.day2Name && !this.day3Name && days[date] !== this.day2Name) {
            this.day3Name = days[date];
            this.day3State = data[i].weather[0].main;
            this.day3Temp = Math.round(data[i].main.temp);

          } else if (!!this.day3Name && !this.day4Name && days[date] !== this.day3Name) {
            this.day4Name = days[date];
            this.day4State = data[i].weather[0].main;
            this.day4Temp = Math.round(data[i].main.temp);

          } else if (!!this.day4Name && !this.day5Name && days[date] !== this.day4Name) {
            this.day5Name = days[date];
            this.day5State = data[i].weather[0].main;
            this.day5Temp = Math.round(data[i].main.temp);

          }
        }
      });

    });

  }

  ngOnDestroy() {
    this.sub1.unsubscribe();
    this.sub2.unsubscribe();
    this.sub3.unsubscribe();
    this.sub4.unsubscribe();
    this.sub5.unsubscribe();
  }

}

Minimus - details.component.ts

Obviously there are a lot of filtering and modification done to the data from the weather service so in the next part will move some of the logic to a service.

Please do not forget also to unsubscribe from each subscription in the ngOnDestroy life cycle hook of the component to avoid memory leaks.

The details component have a lot of svgs which made the HTML very long so here is the full component template including the svg icons so I don’t have to include it here.

with the CSS I have followed the dark mode styling for now:

.details-page__wrapper-dark {
  background: linear-gradient(#FC7DB8, #495CFC);
  height: 100%;
  width: 100%;
  display: flex;
  justify-content: center;
  align-items: center;
  position: relative;
  overflow: hidden;
}

.background-gradient__circle {
  position: absolute;
  top: 50%;
  right: 0;
  transform: translateY(-50%);
  z-index: 1;
  height: 120%;
}

.main-weather__card-dark {
  background-color: white;
  height: 85%;
  width: 60%;
  border-radius: 1rem;
  position: relative;
  z-index: 3;
  display: grid;
  grid-template-columns: 1fr;
  grid-template-rows: 0.5fr 1.25fr;
  justify-items: center;
}

.card-header__container-dark {
  width: 100%;
  max-height: 20rem;
  position: relative;
  z-index: 1;
}

.back__button {
  position: absolute;
  top: 2rem;
  left: 2.25rem;
  width: 5rem;
  cursor: pointer;
  z-index: 3;
}

.city__illustration {
  width: 100%;
  border-radius: 1rem 1rem 0 0;
  position: relative;
}

.header-content__wrapper {
  position: absolute;
  z-index: 2;
  color: white;
  top: 0;
  display: grid;
  grid-template-rows: 1fr;
  grid-template-columns: repeat(2, 1fr);
  width: 100%;
  height: 100%;
}

.temperature__text {
  font-size: 6rem;
  letter-spacing: 0.75rem;
}

.city-name__container {
  display: flex;
  justify-content: center;
  align-items: center;
  padding-bottom: 25%;
}

.city-name__underline {
  background: transparent;
  border-radius: 5px;
  height: 5px;
  box-shadow: 0 3rem 0 0 #ffffff;
}

.city-name__text {
  text-transform: uppercase;
  letter-spacing: 0.3rem;
  font-size: 1.75rem;
  padding-bottom: 2rem;
}

.today-weather__container {
  align-self: center;
  justify-self: center;
  display: grid;
  width: 100%;
  grid-template-rows: 3fr 1fr;
  grid-template-columns: 1fr;
  justify-items: center;
  grid-gap: 2rem;
}

.temp-state__container {
  display: flex;
  justify-content: center;
  flex-flow: column;
}

.weather-state__text {
  letter-spacing: 0.5rem;
  font-size: 1.15rem;
  text-transform: uppercase;
  margin-top: 0.25rem;
}

.hum-wind__container {
  display: flex;
  align-items: center;
  margin-left: -4rem;
}

.hum-wind__separator {
  margin: 0 2rem;
  width: 2px;
  height: 2.5rem;
  background-color: white;
}

.hum__text, .wind__text {
  text-transform: uppercase;
  letter-spacing: 0.2rem;
  font-size: 0.8rem;
  margin-bottom: 1rem;
}

.hum__container, .wind__container {
  display: flex;
  flex-flow: column;
  justify-content: center;
  align-items: center;
}

/*
================
     BODY
================
*/

.body-content__wrapper {
  display: grid;
  grid-template-columns: 1fr 1.75fr;
  grid-template-rows: 1fr;
  justify-items: center;
  box-sizing: border-box;
  grid-column-gap: 1rem;
  width: 100%;
  padding: 2rem;
}

.forecast__container {
  display: flex;
  flex-flow: row;
  align-items: center;
  align-self: center;
  justify-self: center;
}

.twitter-feed__container {
  margin-top: 1rem;
  width: 100%;
}

.twitter-feed__text {
  color: #0c1066;
  font-size: 1.25rem;
}

.twitter__icon {
  width: 1.5rem;
}

.twitter-feed-tag__text {
  font-size: 0.85rem;
  color: #5f84fb;
  letter-spacing: 0.1rem;
  text-transform: uppercase;
}

.twitter-feed__header {
  display: grid;
  grid-template-rows: 2rem;
  grid-template-columns: 0.5fr 1.5fr 1fr;
  align-items: center;
  justify-items: center;
  width: 100%;
}

.day-weather__container {
  display: flex;
  flex-flow: column;
  margin: 2rem 1.5rem;
  justify-content: center;
  align-items: center;
}

.day-name__text {
  font-size: 1.5rem;
  color: #39437a;
  font-weight: bold;
  text-transform: uppercase;
  margin-bottom: 0.5rem;
}

.forecast-condition__icon {
  height: 4rem;
}

.day-temp__text {
  font-size: 1.85rem;
  color: #0c1066;
  letter-spacing: 0.25rem;
  margin: 0.75rem 0;
  text-align: center;
  padding-left: 0.35rem;
}

.day-state__text {

  font-size: 0.65rem;
  text-transform: uppercase;
  letter-spacing: 0.2rem;
  color: #2B244D;
}

Minimus - details.component.css

B. Services

we want to decouple the logic of retrieving the API weather data from a specific component and move it to a separate service that we can use throughout the application and again we are going to use the short hand format to generate a service using the CLI.

  • Weather service
ng g s weather

this service uses the OpenWeatherMap API to retrieve the weather information and makes some modifications before feeding the data to the components at the end. The API does not inform us about the maximum and minimum temperature value and the free plan also restricts us to access only the 5 days/3 hour forecast data so what I ended up doing is I looped through the 3 hours interval temperatures and extracted an approximate max and min value.

and here is the weather.service.ts code:

import {Injectable} from '@angular/core';
import {HttpClient} from '@angular/common/http';
import {Subject} from 'rxjs';

@Injectable()
export class WeatherService {

  constructor(public http: HttpClient) {
  }

  getCityWeatherByName(city: string, metric: 'metric' | 'imperial' = 'metric'): Subject {
    const dataSub = new Subject();
    this.http.get(
      `https://api.openweathermap.org/data/2.5/weather?q=${city}&units=${metric}&APPID=952d6b1a52fe15a7b901720074680562`)
      .subscribe((data) => {
        dataSub.next(data['weather']);
      }, (err) => {
        console.log(err);
      });
    return dataSub;
  }

  getCitiesWeathersByNames(cities: Array, metric: 'metric' | 'imperial' = 'metric'): Subject {
    const citiesSubject = new Subject();
    cities.forEach((city) => {
      citiesSubject.next(this.http.get(
        `https://api.openweathermap.org/data/2.5/weather?q=${city}&units=${metric}&APPID=952d6b1a52fe15a7b901720074680562`));
    });
    return citiesSubject;
  }

  getWeatherState(city: string): Subject {
    const dataSubject = new Subject();
    this.http.get(
      `https://api.openweathermap.org/data/2.5/weather?q=${city}&APPID=952d6b1a52fe15a7b901720074680562`)
      .subscribe((data) => {
        dataSubject.next(data['weather'][0].main);
      });
    return dataSubject;
  }

  getCurrentTemp(city: string, metric: 'metric' | 'imperial' = 'metric'): Subject {
    const dataSubject = new Subject();
    this.http.get(
      `https://api.openweathermap.org/data/2.5/weather?q=${city}&units=${metric}&APPID=952d6b1a52fe15a7b901720074680562`)
      .subscribe((weather: any) => {
        dataSubject.next(Math.round(Number(weather.main.temp)));
      });
    return dataSubject;
  }


  getCurrentHum(city: string, metric: 'metric' | 'imperial' = 'metric'): Subject {
    const dataSubject = new Subject();
    this.http.get(
      `https://api.openweathermap.org/data/2.5/weather?q=${city}&units=${metric}&APPID=952d6b1a52fe15a7b901720074680562`)
      .subscribe((weather: any) => {
        console.log(weather);
        dataSubject.next(weather.main.humidity);
      });
    return dataSubject;
  }


  getCurrentWind(city: string, metric: 'metric' | 'imperial' = 'metric'): Subject  {
    const dataSubject = new Subject();
    this.http.get(
      `https://api.openweathermap.org/data/2.5/weather?q=${city}&units=${metric}&APPID=952d6b1a52fe15a7b901720074680562`)
      .subscribe((weather: any) => {
        dataSubject.next(Math.round(Math.round(weather.wind.speed)));
      });
    return dataSubject;
  }


  getMaxTemp(city: string, metric: 'metric' | 'imperial' = 'metric'): Subject  {
    const dataSubject = new Subject();
    let max: number;
    this.http.get(
      `https://api.openweathermap.org/data/2.5/forecast?q=${city}&units=${metric}&APPID=952d6b1a52fe15a7b901720074680562`)
      .subscribe((weather: any) => {
        max = weather.list[0].main.temp;
        weather.list.forEach((value) => {
          if (max < value.main.temp) {
            max = value.main.temp;
          }
        });
        dataSubject.next(Math.round(max));
      });
    return dataSubject;
  }

  getMinTemp(city: string, metric: 'metric' | 'imperial' = 'metric'): Subject  {
    const dataSubject = new Subject();
    let min: number;
    this.http.get(
      `https://api.openweathermap.org/data/2.5/forecast?q=${city}&units=${metric}&APPID=952d6b1a52fe15a7b901720074680562`)
      .subscribe((weather: any) => {
        min = weather.list[0].main.temp;
        weather.list.forEach((value) => {
          if (min > value.main.temp) {
            min = value.main.temp;
          }
        });
        dataSubject.next(Math.round(min));
      });
    return dataSubject;
  }

  getForecast(city: string, metric: 'metric' | 'imperial' = 'metric'): Subject>  {
    const dataSubject = new Subject>();
    this.http.get(
      `https://api.openweathermap.org/data/2.5/forecast?q=${city}&units=${metric}&APPID=952d6b1a52fe15a7b901720074680562`)
      .subscribe((weather: any) => {
        dataSubject.next(weather.list);
      });
    return dataSubject;
  }

}

Minimus- weather.service.ts

as you can see all the functions return a Subject which we will use to broadcast the modified data to any component that subscribes to it. This free weather API sucks and I might create a tutorial on how to transform this ugly REST API to a much nicer GraphQL one so stay tuned.

a quick run through on what the different service functions do:

getWeatherState : the current weather state e.g. cloudy - clear…

getCurrentTemp: the current temperature number

getMinTemp: the minimum temperature (based on 3 hours interval)

getMinTemp: the minimum temperature (based on 3 hours interval)

getCurrentHum: current humidity value (number)

getCurrentWind: currenty wind speed (number)

getForecast: get weather data for the 5 upcoming days

getCityWeatherByName: return the entire weather data from the API of city name provided as a string

getCitiesWeathersByNames: returns the entire weather data from the API of the city names provided as an array

  • UI service

this is small service that has functions that we are going to utilize to share the state of the UI like the theme mode selected (dark or light) application wide.

import { Injectable } from '@angular/core';
import { BehaviorSubject } from 'rxjs';

@Injectable()
export class UiService {

  darkModeState: BehaviorSubject;

  constructor() {
    // TODO: if the user is signed in get the default value from Firebase
    this.darkModeState = new BehaviorSubject(false);
  }
}

Minimus - ui.service.ts

C. Routing

We have already generated the routing module when we created our app with the CLI but we have to make some modifications to the routing.module.ts to tell Angular what are the different routes (URLs) and their associated components (pages).

import {NgModule} from '@angular/core';
import {Routes, RouterModule} from '@angular/router';
import {HomeComponent} from './pages/home/home.component';
import {DetailsComponent} from './pages/details/details.component';
import {AddComponent} from './pages/add/add.component';
import {LoginComponent} from './pages/login/login.component';
import {SignupComponent} from './pages/signup/signup.component';

const routes: Routes = [
    {path: '', component: HomeComponent},
    {path: 'details/:city', component: DetailsComponent},
    {path: 'add', component: AddComponent},
    {path: 'login', component: LoginComponent},
    {path: 'signup', component: SignupComponent},
];

@NgModule({
    imports: [RouterModule.forRoot(routes)],
    exports: [RouterModule]
})
export class AppRoutingModule {
}

Minimus - app-routing.module.ts

Conclusion

We made a good progress in this very first part of building the Minimus Weather App we got most of the design work from branding to UI and UX decisions, We wrote a lot of HTML & CSS and we made it look beautiful.

Live Demo (V2): https://minimus-weather.firebaseapp.com

Github Repo: https://github.com/hamedbaatour/Minimus

30s ad

Angular 7 with Angular Material and Firebase Cloud Firestore

NgRx In Depth (Angular 7 and NgRx 7, with FREE E-Book)

A Quick Guide to Angular 7 in 4 Hours

Complete Angular 7 - Ultimate Guide - with Real World App

Go Full Stack with Spring Boot and Angular 7

Is React killing Angular?

The issue that Angular is facing has much less to do with React and far more to do with the fact that they’re… well… Angular…

The issue that Angular is facing has much less to do with React and far more to do with the fact that they’re… well… Angular…

Angular’s Critical Flaw (in my eyes) is that the new version tries to do too many things at once and simultaneously scares off the very audience they are trying to appeal to.

The current Angular 1.x audience is the one audience that really matters the most - they are the users of the current product and the ones that are most naturally going to explore Angular 2.0 in the near future. What they get with Angular 1.x is dependency injection, directives, $scope, controllers, two-way data-binding… basically this:

When they load up the Angular 2.0 docs and wonder what directives are going to look like in “the new version” they are instead greeted with this:

React-like components, data-binding being subdivided, services injecting into components, which do things with templates, which do things with directives… but components are also directives…? Who knows? Screw it, let’s make a simple Todo application. I mean… Tour of Heroes application. Oh wait… the docs are all written in TypeScript while the JavaScript version is still under construction. Because, as is only natural, Google doesn’t seem to believe in JavaScript as a programming language.

The current React audience is the 800 lbs Gorilla in the room. Having come around at a time when Angular announced 2.0 would not be compatible with 1.x, React more or less swooped in and stole the hearts and minds of legions of developers with a “simple view library”. Angular wants them back and tries to appeal to them with some of the creature comforts that the React community enjoys - Components, Server-side rendering, understandable data flow.

However, the issue Angular faces on this front is that Angular is trying to build a great “framework” and views React as a “library” that they are competing with - but React has moved on from “just the view layer” to an “ecosystem”. State management can easily be handled with Flux/Redux, routing is simple with React Router, you can declare dependencies with Relay & GraphQL, and you can “learn once, write anywhere” with React Native. Angular tries to appeal to the React audience with server-side rendering, components, and a logical data flow - but they are just following the standard React has set. With much more bloat. And documentation written in TypeScript (while the majority of React’s users are focused on ES6/7).


In conclusion, React is in no way killing Angular - Angular allocated all the rope necessary to hang itself. If the Angular team wants to acquire the mindshare of developers it needs to figure out who its audience is and actually appeal to them; and it wouldn’t hurt if they took a moment to write their documentation in ES5 - the language that is currently used on browsers, mobile devices, servers, and desktop applications - and stopped trying to do everything in their power to not write native JavaScript for once.