How to Build a Crypto Portfolio App with Ignite UI for Angular

Zdravko Kolev / Tuesday, August 7, 2018

I wanted to share with you how easy it is to create Angular application with our Ignite UI for Angular toolset.

What is the purpose of this application? First of all, to demonstrate our Angular components. Second, it’s to demonstrate a real use case, and I wanted to create a system that will track my cryptocurrencies portfolio in order to check my daily gains and losses.  As for the third reason,  to demonstrate the use of different Аngular techniques like utilizing Guards, Pipes, and Custom Templates with our components and, of course, using of Firebase for data storing and authentication.

The topic will cover:

  1. The use of Ignite UI Angular CLI
  2. Data services, Routing, Templating, Pipes, etc.
  3. The use of Firebase Data Storage, Authentication, and CRUD operations
  4. Coinmarketcap API to Retrieve real-time and historical price information

Ignite UI CLI

Okay, let's start! So, have you heard about Ignite UI CLI? Yep, this is a real thing that we’ve developed that helps you create Angular applications with a predefined set of components, executing just a few commands. It takes less than three minutes to install all necessary packages and to run the project with the help of our CLI, just follow our official Getting started page.

After the building of the app is completed along with the installation of all necessary packages, you will notice that Navigation drawer and Grid components were added and already configured. The same applies for the different responsive views, routing, styles, module imports, etc. - awesome, right?

export const routes: Routes = [
  { path: '', redirectTo: '/home', pathMatch: 'full' },
  { path: 'home', component: HomeComponent },
  ...
];

Okay, what's next? We want our app to show variety of things: we want to have user authentication, to track crypto data changes, and to store our Cryptocurrencies portfolio somewhere. For that purpose, Ignite UI for Angular provides all suitable components out of the box. We are going to use Chart for historical data changes and a Grid to show our crypto portfolio and provide the ability to add/update/delete cryptocurrencies, also List and Card.

For all of the above requirements we need to get the data from somewhere, this is where the min-api.cryptocompare steps in - This is a free service that provides information for all active cryptocurrencies in one call.

export class BlockGridComponent implements OnInit, AfterViewInit{
  public remoteData: CoinItem[];
  ...

  ngAfterViewInit() {
    this.dataService.getData().subscribe(res => {
      this.remoteData = res;
    });
  ...
}


App authentication 

Below I’ve highlighted some steps that I’ve took in order to set up the Firebase authentication of the Crypto App. AngularFireAuth is going to be used for user authentication and all related actions like login and logout. This firebase service provides methods for sign in with email and password, Google authentication provider, and Facebook auth provider. For a more detailed explanation, I recommend checking up the official Angular firebase repository and documentation.

  1. Set up a firebase project.
  2. Go to the Authentication tab and enable Google, Facebook, and Email/password sign-in providers.
  3. Based on the methods, generate Facebook ID and later on OAuth redirect URI.
  4. Add rules and publish the changes.
  5. Add the firebaseConfig to the Angular project.
  6. Create Login, Signup and Email components. (code for all)
  7. Create auth.service that will handle the redirects of registered and unregistered users (Route guards). As you can see from the code, (add code), the Portfolio page is accessible only for authorized users.

@Injectable()
export class AuthGuard implements CanActivate {

    constructor(private auth: AngularFireAuth, private router: Router, private route: ActivatedRoute) {}

    canActivate(route: ActivatedRouteSnapshot, routerState: RouterStateSnapshot): Observable<boolean> {
      return Observable.from(this.auth.authState)
        .take(1)
        .map(state => !!state)
        .do(authenticated => {
          if (!authenticated) {
            this.router.navigate([ '/login' ], {
              queryParams: {
                return: routerState.url
              }
            });
          }
      });
    }

}

For more details, have a look at the official firebase authentication getting started topic, and I can guarantee that you won't feel any kind of difficulties when setting it up.

Data storage and CRUD operations  

For data storing and data updates, our application is using Firebase Realtime Database which is a cloud-hosted database, stored as JSON and synchronized in realtime to every connected user. All users share on Realtime Database instance and automatically receive updates with the newest data.

Reading and writing of data is easy, and below are the steps that you could follow in order to achieve CRUD operations:

  • Create a separate service for data manipulations
  • Define `BlockItem` class that is going to be used for data fetch, adding, updating and removing of records.
  • Create a list binding/retrieve that will return an observable of data as a synchronized array of JSON objects. With metadata (underlying DBReference and snapshot key)

For more information check the official firestore documentation.

What about Grid CRUD? Yep, this is possible with the help of Firebase data storage and our great api. Check this file for more information on how to setup your Firebase platform which could provide you abilities for data storage, authentication, etc.

public deleteRow(cell) {
  let blockItem = cell.row.rowData;

  // Detele item from AngularFireList
  this.deleteItem(blockItem);

  // Stores deleted item for the 'Restore' Snackbar logic 
  this.deletedItem = new BlockItem()
  Object.assign(this.deletedItem, blockItem);
  ...
  this.snack.show();
}

public updateRow(evt) {
  const rowItem = evt.rowID;
  rowItem.holdings = evt.newValue;

  this.updateItem(rowItem);
}

Full code here.

Configure all components that are going to be used in the application

Fetched data is used from Charts, Grids, Lists and Card components (provide code for each).

For example, the Card component is using filtering pipes and sorting of data.

export class HomeComponent implements OnInit {
   cryptos: CoinItem[];
   public searchValue: string;

   constructor(private data: DataService, private router: Router) { }

   ngOnInit() {
      this.loadData();
   }

   private loadData() {
      this.data.getData()
         .subscribe(res => {
            this.cryptos = sortDataByKey(res, 'rank');
         });
   }

   get filterOptions() {
      const fo = new IgxFilterOptions();
      fo.key = 'fullName';
      fo.inputValue = this.searchValue ? this.searchValue : '';
      return fo;
   }
... 

Check up the code in order to see how each of the components is bound.

Grid

This is the main page that is going to be used for data manipulating and tracking of price changes. We are going to define a grid with five column (count depends on the screen size) and each column will have its own ng template for data representation. This includes images, icons for price movements, and using of decimal pipes.

The action buttons above the grid will handle manual data refresh and adding of new coins. igxDialog is going to be used for that purpose. A minimal validation of the coins is applied. For example, you won't be able to add already existing coins or coins that are not present in the coinmarketcap api. Each notification message is going to be shown via igxSnackBar.

For the coin holding updates, we are going to handle (onEditDone) and from there use the methods that we defined in the BlockItemService. Same applied for the `delete` and `add` coin buttons.

<igx-grid #grid1 [data]="blockItems" width="100%" height="500px" toolbarTitle="My portfolio"
   (onCellEdit)="updateRow($event)" (onSelection)="selectCell($event)" [showToolbar]="true" 
   [columnHiding]="true" [columnPinning]="true">
   ...
   <igx-column field="holdings" header="Holdings" editable="true" sortable="true">
      <ng-template igxCell let-cell="cell" let-ri="rowIndex" let-column="column">
         <div class="positionTop">
            ${{ calculateHoldings(cell.row.rowData.holdings, cell.row.rowData.price) | number:'0.2-2' }}
            <br />
            <b>{{ cell.row.rowData.holdings | number:'1.0-7' }}</b>
         </div>
      </ng-template>
   </igx-column>
   ...
   <igx-column header="Actions">
      <ng-template igxCell let-ri="rowIndex" let-column="column">
         <span igxButton="icon" igxRipple (click)='deleteRow()'>
            <igx-icon>highlight_off</igx-icon>
         </span>
      </ng-template>
   </igx-column>
</igx-grid>

Chart

This component is going to be used for visual representation of the coin price changes per day. Our igxFinancialChart is easily configurable, as you just pass the fetched data to “dataSource” and, voila, everything else will be handled by the chart. Additionally, the chart respects the general financial data structure.

  

     <igx-financial-chart [dataSource]="data" height="400px" width="100%" style="margin-top: 20px;" isToolbarVisible="false" chartType="candle">
      </igx-financial-chart>

One interesting item here that should be mentioned is the use of routes to pass data between views. The Statistics page is accessible through a couple of Views which are passing different coin names to be loaded in the chart.

List and Card

IgxList and IgxCard components are used to show a different visual representation of all properties associated with the returned items.

To sum up, everything is possible with the right tooling, and with that said, you definitely should consider using of our Ignite UI for Angular components for your next web/mobile application.

In the next article of this series, you will learn how to implement your Firebase configuration in details.

GitHub repository and hosted application.

Ignite UI for Angular