Create a cryptocurrency app with Angular


In this tutorial, we are going to create a CryptoCurrency Price Update app using Angular 9.

You will learn about Angular 9 application development. You will also learn how to use the Subject class from RxJS, a ReactiveX library for JavaScript.

Let’s get started By creating Angular 9 project

Install the Angular CLI

To install the CLI using npm, open a terminal window and enter the following command :

 npm install -g @angular/cli

now, Run the CLI command ng new and provide the name cryptoapp, as shown below:

ng new cryptoapp

Run the application

cd cryptoapp
ng serve --open

your app will automatically at http://localhost:4200/ as shown below:

Angular 9 tutorial

Create application components

Now, let’s create the components.

In the New, AngularCli enter the following command to create a bunch of new components

ng generate component search
ng generate component list-card
ng generate component list
ng generate component dashboard

Create AppService

In this tutorial, we will be using just one Angular Service, named AppService. In Angular, a Service is a broad category encompassing any value, function, or feature that your application needs.

ng generate service App

Add Bootstrap CSS and Font Awesome

Let’s add bootstrap CSS and font awesome to our project by updating the contents of src/index.html add the code snippet below in the head section of the HTML document:

  <!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>Cryptoapp</title>
  <base href="/">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="icon" type="image/x-icon" href="favicon.ico">
  <link rel="stylesheet"
href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.css">
<link href='//fonts.googleapis.com/css?family=Open+Sans:400,700'
rel='stylesheet' type='text/css'>
<link rel="stylesheet"
href="//netdna.bootstrapcdn.com/font-awesome/4.3.0/css/font-awesome.css" />
</head>
<body>
  <app-root></app-root>
</body>
</html>

 

Install Angular dropdown-multi-select

Angular Dropdown Multiselect for Bootstrap CSS is used to create customizable dropdown multiselect in AngularX, TypeScript with bootstrap CSS.

npm install angular2-multiselect-dropdown

In order to make the multiselect component available in our application, we need to update src/app/app.module.ts as follows:

 import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';

import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { SearchComponent } from './search/search.component';
import { ListCardComponent } from './list-card/list-card.component';
import { ListComponent } from './list/list.component';
import { DashboardComponent } from './dashboard/dashboard.component';
import {MultiselectDropdownModule} from 'angular-2-dropdown-multiselect';
import { FormsModule } from '@angular/forms';
import { HttpClientModule } from '@angular/common/http';

@NgModule({
declarations: [
AppComponent,
SearchComponent,
ListCardComponent,
ListComponent,
DashboardComponent
],
imports: [
BrowserModule,
AppRoutingModule,
MultiselectDropdownModule,
FormsModule,
HttpClientModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }

The API

We will be using the api provided by coinmarketcap.com. Using the sample data returned, let us create a typescript shapefile named coin.ts with the following content:

 export class Coin {
id: string;
name: string;
symbol: string;
rank: string;
price_usd: string;
price_btc: string;
'24h_volume_usd': string;
market_cap_usd: string;
available_supply: string;
total_supply: string;
percent_change_1h: string;
percent_change_24h: string;
percent_change_7d: string;
last_updated: string;
}

Similarly, create a new file called localdata.ts in the app folder with the following content:

 export let cryptoCurrencies = [
'0x',
'ATMChain',
'AdEx',
'Aeternity',
'Aragon',
'Ardor',
'Ark',
'Augur',
'Bancor',
'Basic Attenti...',
'Binance Coin',
'BitConnect',
'BitShares',
'Bitcoin',
'Bitcoin Cash',
'BitcoinDark',
'Bitdeal',
'Bitquence',
'Blocknet',
'Byteball Bytes',
'Bytecoin',
'Bytom',
'Cardano',
'ChainLink',
'Civic',
'Dash',
'Decred',
'Dentacoin',
'DigiByte',
'DigixDAO',
'Dogecoin',
'EOS',
'Edgeless',
'Ethereum',
'Ethereum Classic',
'Factom',
'FirstCoin',
'FunFair',
'GXShares',
'GameCredits',
'Gas',
'Gnosis',
'Golem',
'Hshare',
'I/O Coin',
'IOTA',
'Iconomi',
'Kin',
'Komodo',
'Kyber Network',
'Lisk',
'Litecoin',
'Loopring',
'Lykke',
'MCAP',
'MaidSafeCoin',
'Metal',
'Metaverse ETP',
'MonaCoin',
'Monaco',
'Monero',
'NAV Coin',
'NEM',
'NEO',
'Neblio',
'Nexus',
'NoLimitCoin',
'Nxt',
'OmiseGO',
'PIVX',
'Particl',
'Populous',
'Pura',
'Qtum',
'Ripple',
'SALT',
'Siacoin',
'SingularDTV',
'SmartCash',
'Status',
'Steem',
'Stellar Lumens',
'Storj',
'Stratis',
'Syscoin',
'TRON',
'TaaS',
'TenX',
'Tether',
'Ubiq',
'VeChain',
'Verge',
'Veritaseum',
'Vertcoin',
'Walton',
'Waves',
'ZCoin',
'Zcash',
'ZenCash',
'iExec RLC'
];

App Service

AppService is a general-purpose class we will use to fetch data from the API, and pass that data on to components.

Our appservice.ts file ha following content

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

import {cryptoCurrencies} from './localdata';
import {Coin} from './coin';

const API_BASE_URL = 'https://api.coinmarketcap.com/v1/';
const coins: Coin[] = [];
@Injectable()
export class AppService {
  private allCoins: Coin[];
  private filteredCoins: Coin[];
  private filter: number[];

  coinsSubject: Subject<Coin[]>;
  filteredCoinsSubject: Subject<Coin[]>;
  apiSubject: Subject<string>;
  fiatSubject: Subject<string>;

  constructor(private http: HttpClient) {
    this.filter = [];
    this.coinsSubject = new Subject();
    this.filteredCoinsSubject = new Subject();
    this.apiSubject = new Subject();
    this.fiatSubject = new Subject();
  }

  getCryptoOptions() {
    return cryptoCurrencies;
  }

  loadMarketCaps(fiat: string) {
    this.fiatSubject.next(fiat);
    const url = API_BASE_URL + 'ticker/';
    let params = new HttpParams();
    params = params.append('limit', '25');
    if (fiat !== 'usd') {
      // TODO: check if fiat is valid
      params = params.append('convert', fiat);
    }
    this.apiSubject.next('loading...');
    this.http
      .get<Coin[]>(url, {params})
      .subscribe(
      data => {
        this.allCoins = data;
        this.announceCoins();
        this.filterMarketCaps();
      }
      );
    //    this.allCoins = mock.data;
  }

  filterMarketCaps() {
    this.filteredCoins = [];
    if (this.filter.length === 0) {
      this.allCoins.forEach((coin) => this.filteredCoins.push(coin));
    }

    if (this.filter.length > 0) {
      this.filter.forEach((i) => {
        this.filteredCoins.push(this.allCoins[i]);
      });
    }
    this.announceFilteredCoins();
  }

  announceCoins() {
    this.coinsSubject.next(this.allCoins);
  }

  announceFilteredCoins() {
    this.filteredCoinsSubject.next(this.filteredCoins);
  }

  updateFilter(filter: number[]) {
    this.filter = [];
    filter.forEach((elem) => {
      this.filter.push(elem);
    });
    this.filterMarketCaps();
  }

} 

In the file above First we import {Injectable} from @angular/core, {Subject} from rxjs, {HttpClient, HttpParams} from @angular/common/http
Next we imported the coin shape Coin from ./coin, and we create a constant API_BASE_URL to hold the base url of the api.

App Component

Update the content of src/app/app.component.html with the content below. We display the title in an h2 element, and we set the dashboard component as a child view.

 <div class="container">
  <div class="row">
  <div class="col-sm-6 col-sm-offset-3">
  <h2 class="text-center">{{ title }}</h2>
  <app-dashboard></app-dashboard>
  </div>
  </div>
 </div> 

Dashboard Component

The dashboard component will serve as a parent component for two components, the search component, and the list component.
Replace the contents of `src/app/dashboard/dashboard.component.html` with the code snippet below

 <app-search></app-search>
<app-list></app-list> 

Now, update src/app/dashboard/dashboard.component.ts by importing AppService, and adding AppSercieto the providers’ array which enables Angular’s Dependency Injector for AppService

 
import { Component, OnInit } from '@angular/core';
import {AppService} from '../app.service';

@Component({
selector: 'app-dashboard',
templateUrl: './dashboard.component.html',
styleUrls: ['./dashboard.component.sass'],
providers: [AppService]

})
export class DashboardComponent implements OnInit {

constructor() { }

ngOnInit() {
}

}

Search Component

The code below goes in src/app/search-filter/search.component.html

 <div>
    <form>
    <div class="form-group">
    <select id="selectedCurrency" name="selectedCurrency" [(ngModel)]="selectedCurrency"
    (change)="selectCurrency($event.target.value)" class="form-control"
    id="fiat">
    <option value="">Select fiat currency</option>
    <option *ngFor="let currency of currencies" value="{{currency}}">{{currency.toUpperCase()}}</option>
    </select>
    </div>
    <div class="form-group">
    <ss-multiselect-dropdown id="cryptocurrencies"
    name="cryptocurrencies" [texts]="myTexts" [settings]="mySettings"
    [options]="cryptoCurrOptions" [(ngModel)]="optionsModel"
    (ngModelChange)="filterChange($event)"></ss-multiselect-dropdown>
    </div>
    </form>
    </div>
     

and the code below goes in src/app/search/search.component.ts

 import {Component, EventEmitter, Input, OnInit, Output} from '@angular/core';
import {AppService} from '../app.service';
import {IMultiSelectOption, IMultiSelectSettings, IMultiSelectTexts} from 'angular-2-dropdown-multiselect';

@Component({
selector: 'app-search',
templateUrl: './search.component.html',
styleUrls: ['./search.component.css'],
providers: []
})
export class SearchComponent implements OnInit {
currencies: string[];
cryptoCurrOptions: IMultiSelectOption[];
selectedCurrency: string;
optionsModel: number[];

// Settings configuration
mySettings: IMultiSelectSettings = {
enableSearch: true,
checkedStyle: 'fontawesome',
buttonClasses: 'btn btn-default btn-block',
dynamicTitleMaxItems: 5,
displayAllSelectedText: true
};

// Text configuration
myTexts: IMultiSelectTexts = {
checkAll: 'Select all',
uncheckAll: 'Unselect all',
checked: 'item selected',
checkedPlural: 'items selected',
searchPlaceholder: 'Find',
searchEmptyResult: 'Nothing found...',
searchNoRenderText: 'Type in search box to see results...',
defaultTitle: 'Filter cryptos',
allSelected: 'All selected',
};

constructor(private appService: AppService) {
this.currencies = ['usd', 'eur'];
this.selectedCurrency = '';
this.cryptoCurrOptions = [];
this.appService.coinsSubject.subscribe({
next: (v) => this.updateCryptoOptions(v),
});
}

ngOnInit() {
}

selectCurrency(newValue) {
this.appService.loadMarketCaps(newValue);
}

filterChange(newValue) {
// BUG method should not be triggered by filter select
this.appService.updateFilter(newValue);
}

updateCryptoOptions(coins) {
this.cryptoCurrOptions = [];
coins.forEach((coin, index) => {
this.cryptoCurrOptions.push({
id: index,
name: coin.id.charAt(0).toUpperCase() + coin.id.slice(1)
});
});
}
}

List Component

This component manages the display of a list of cards. One for each crypto coin data returned from the API. The coins model is an array of the data rendered by each coin. Let us update the contents of src/app/list/list.component.html

 <div class="list-cards pb-4">
    <app-list-card *ngFor="let coin of coins" [coin]="coin" [fiat]="fiat"></app-list-card>
   </div>
   <div *ngIf="!coins">
   {{noDataMsg || 'Nothing to display'}}
   </div> 

In src/app/list/list.component.ts , we subscribe to three RxJs subjects in our Service.

 import {Component, Input, OnInit} from '@angular/core';
import {AppService} from '../app.service';
import {Coin} from '../coin';
@Component({
selector: 'app-list',
templateUrl: './list.component.html',
styleUrls: ['./list.component.sass']
})
export class ListComponent implements OnInit {
coins: Coin[];
noDataMsg: string;
fiat: string;
constructor(private appService: AppService) {
this.noDataMsg = 'Select fiat currency to get started';
this.appService.filteredCoinsSubject.subscribe({
next: (v) => this.updateCoins(v),
});
this.appService.apiSubject.subscribe({
next: (msg) => this.noDataMsg = msg,
});
this.appService.fiatSubject.subscribe({
next: (newValue) => this.fiat = newValue,
});
}
updateCoins(coins: Coin[]) {
this.coins = [];
coins.forEach((coin) => this.coins.push(coin));
}
ngOnInit() {
}
}

ListCard Component

The list-card component is basically to display the market cap summary of a single cryptocurrency. The content of src/app/list-card/list-card.component.html is below

 <div class="card text-center mt-4">
    <div class="card-body">
    <h2 class="card-title">{{ coin.id }}</h2>
    <h4 class="mb-0 pb-1">{{fiat.toUpperCase()}} {{ coin['price_'+fiat] }}</h4>
    </div>
    <ul class="list-group list-group-flush">
    <li class="list-group-item">24 hours: <span [class]="coin.percent_change_24h.charAt(0)==='-' ? 'red' : 'green' ">{{ coin.percent_change_24h
    }}</span></li>
    <li class="list-group-item">7 days: <span [class]="coin.percent_change_24h.charAt(0)==='-' ? 'red' : 'green' ">{{ coin.percent_change_7d }}</span></li>
    </ul>
   </div> 

and src/app/list-card/list-card.component.ts

import {Component, OnInit, Input} from '@angular/core';
@Component({
selector: 'app-list-card',
templateUrl: './list-card.component.html',
styleUrls: ['./list-card.component.sass']
})
export class ListCardComponent {
@Input() coin;
@Input() fiat;
}

And below is the final result:

Crypto app Angular

conclusion

I hope you learned a few things about Angular. Every article can be made better, so please leave your suggestions and contributions in the comments below. If you have questions about any of the steps, please do ask also in the comments section below.

[epcl_columns]  [epcl_button label=”Code source” url=”https://github.com/Dunebook/cryptoappAngular” type=”outline” color=”red” size=”large” icon=”fa-github” target=”_blank” rel=”dofollow”][/epcl_button]       [epcl_button label=”Project Demo” url=”https://cryptod.netlify.com/” type=”outline” color=”red” size=”large” icon=”fa-github” target=”_blank” rel=”dofollow”]    [/epcl_columns]


Share on social media

//