Introduction to Module and Directives in AngularJS - ZeptoBook

Angular 7 CRUD With Node.JS API

Angular 7 CRUD With Node.JS API

In this article, we are going to create a very basic Angular 7 CRUD (Create, Read, Update and Delete) application with Node.JS REST API endpoints. With the help of our REST API, we will work on our MongoDB database, and perform various operations on Products collection.

In this article, we are going to create a very basic Angular 7 CRUD (Create, Read, Update and Delete) application with Node.JS REST API endpoints. With the help of our REST API, we will work on our MongoDB database, and perform various operations on Products collection.

Angular 7 Prerequisites

Before start writing the Angular 7 app, please make sure that, you have latest version of:

  • Node.JS and NPM installed on your system
  • Angular 7 CLI installed on your system
MongoDB Backend Overview

Let me introduce you about the MongoDB Document structure. We have Products collection in our database, and this Products collection have multiple documents related to various products.

This is the screenshot of our MongoDB Products collection.

And this is the screenshot of each Product document.

{
    "_id": {
        "$oid": "5c3162ed7829481868c3afe9"
    },
    "title": "HP",
    "description": "15 inch HP Laptop",
    "price": 1500,
    "company": "HP",
    "createdAt": {
        "$date": "2019-01-06T02:07:41.477Z"
    },
    "updatedAt": {
        "$date": "2019-01-06T02:07:41.477Z"
    },
    "__v": 0
}

Node.JS Rest API Endpoints

You can get all details of Node.JS Rest API in the above link, which I shared on the top of this post. Let me share again, which endpoints we have for our application. These are the list of our API endpoints.

// Create a new Product
    app.post('/products', products.create);

    // Retrieve all Products
    app.get('/products', products.findAll);

    // Retrieve a single Product with productId
    app.get('/products/:productId', products.findOne);

    // Update a Note with productId
    app.put('/products/:productId', products.update);

    // Delete a Note with productId
    app.delete('/products/:productId', products.delete);

Creating Angular 7 Project

Let’s start with our Angular 7 app. Using above Node.JS API endpoints, we are going to perform CRUD operation. This will be the screen of our Angular app.

Create your angular app using the ng new  command in your terminal.

1. Creating Model Class In Angular

After creating our app, let’s first create our Product Model class as per our database Product Document properties.

ProductModel.ts

export class ProductModel {
    _id: string;
    title: String;
    description: String;
    price: Number;
    company: String;
}

2. Creating Service In Angular

Let’s create our Product Service class, which will interact with our Node.JS endpoints for all product related operations.

product.service.ts

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { ProductModel } from './ProductModel';

@Injectable({
  providedIn: 'root'
})
export class ProductService {

  constructor(private http: HttpClient) { }

  baseurl: string = "http://localhost:3000/";

  getAllProducts(){
    return this.http.get<ProductModel[]>(this.baseurl + 'Products');
  }

  getProductById(id: string){
    return this.http.get<ProductModel>(this.baseurl + 'Products' + '/' + id);
  }

  addProduct(product: ProductModel){
    return this.http.post(this.baseurl + 'Products', product);
  }

  deleteProduct(id: string){
    return this.http.delete(this.baseurl + 'Products' + '/' + id);
  }

  updateProduct(product: ProductModel){
    return this.http.put(this.baseurl + 'Products' + '/' + product._id, product);
  }
}

We have imported HttpClient and ProductModel in our service class.

This is our service url endpoint.

baseurl: string = “http://localhost:3000/”;
In this class, we have different methods. These are:

  • Node.JS and NPM installed on your system
  • Angular 7 CLI installed on your system

Import Product Service in app.module

Now, after creating the product service, we have to import this in our app.module.ts file. Let’s do it now.

app.module.ts

Add this line on the top of app.module.ts file.

baseurl: string = “http://localhost:3000/”;
And this ProductService in Providers[] array.
baseurl: string = “http://localhost:3000/”;## 3. Creating Component For Listing All Products

First of all, we are going to fetch all the products in our database. Create list-products component, and add this to our routing module. This component will fetch all the products from the database and display it in tabular form. Add routing details of this component in our route file.

app-routing.module.ts

Import ListProductsComponent in our routing file.

baseurl: string = “http://localhost:3000/”;
And, add this in routes constant.
baseurl: string = “http://localhost:3000/”;
This is our list-products component html file.

list-products.component.html

<div class="container col-md-12"> 
    <div>
        <button (click)="addProduct()" class="btn btn-info">Add Product</button>
      </div> 
  <h2>All Products</h2>  
  <div class="table-responsive table-container">  
    <table class="table table-dark table-hover table-striped">  
      <thead class="thead-light">  
        <tr>  
          <th>Title</th>  
          <th>Description</th> 
          <th>Price($)</th> 
          <th>Company</th>
          <th>Action</th> 
        </tr>  
      </thead>  
      <tbody>  
        <tr *ngFor="let product of products"> 
           
          <td>{{product.title}}</td>  
          <td>{{product.description}}</td>
          <td>{{product.price | currency : "USD" : true}}</td> 
          <td>{{product.company}}</td> 
           
          <td>  
            <button (click)="deleteProduct(product)" class="btn btn-info"> Delete</button>  
            <button (click)="updateProduct(product)" style="margin-left: 20px;" class="btn btn-info"> Edit</button>  
          </td>  
        </tr>  
      </tbody>  
    </table>  
  </div> 
   
</div> 

list-products.component.ts

import { Component, OnInit } from '@angular/core';
import { Router } from "@angular/router";
import { ProductModel } from '../ProductModel';
import { ProductService } from '../product.service';

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

  products: ProductModel[];

  constructor(private productService: ProductService, private router: Router) { }

  ngOnInit() {
    this.getAllProducts();
  }

  getAllProducts(): void {
    this.productService.getAllProducts().subscribe(data=>{
      this.products = data;
    });
  };

  addProduct(): void {
    this.router.navigate(['add-product']);
  }

  deleteProduct(product: ProductModel){
    
    this.productService.deleteProduct(product._id).subscribe(data=>{
      console.log(data);
      this.getAllProducts();
    });
  }

  updateProduct(product: ProductModel){
    localStorage.removeItem("productId");
    localStorage.setItem("productId", product._id);
    this.router.navigate(['edit-product']);
  }

}

To get the list of all products, we have created getAllProducts() function, and call it in ngOnInit().

4. Creating Component For Adding Product

This component will be used to add a product in our database. Add routing details of this component in our route file.

app-routing.module.ts

Add this line on the top of app-routing.module.ts file.

baseurl: string = “http://localhost:3000/”;
And, add this in routes constant.
baseurl: string = “http://localhost:3000/”;
This is our add-product component file.

add-product.component.html

<div class="col-md-3">
    <h2 class="text-center">Add Product</h2>
    <form [formGroup]="addForm" (ngSubmit)="onSubmit()">
      <div class="form-group">
        <label>Title:</label>
        <input type="text" formControlName="title" placeholder="Title" class="form-control" [ngClass]="{ 'is-invalid': submitted && f.title.errors }">
        <div *ngIf="submitted && f.title.errors">
          <div *ngIf="f.title.errors.required">Title is required</div>
        </div>
      </div>
  
      <div class="form-group">
        <label>Description:</label>
        <input type="text" formControlName="description" placeholder="Description" class="form-control" [ngClass]="{ 'is-invalid': submitted && f.description.errors }">
        <div *ngIf="submitted && f.description.errors">
          <div *ngIf="f.description.errors.required">Description is required</div>
        </div>
      </div>
  
      <div class="form-group">
        <label>Price ($):</label>
        <input type="text" formControlName="price" placeholder="Price" class="form-control" [ngClass]="{ 'is-invalid': submitted && f.price.errors }">
        <div *ngIf="submitted && f.price.errors">
          <div *ngIf="f.price.errors.required">Price is required</div>
        </div>
      </div>

      <div class="form-group">
          <label>Company:</label>
          <input type="text" formControlName="company" placeholder="Company" class="form-control" [ngClass]="{ 'is-invalid': submitted && f.company.errors }">
          <div *ngIf="submitted && f.company.errors">
            <div *ngIf="f.company.errors.required">Company is required</div>
          </div>
      </div>
  
      <button type="submit"  class="btn btn-info">Save</button>
    </form>
  </div>

add-product.component.ts

import { Component, OnInit } from '@angular/core';
import { FormBuilder, FormGroup, FormControl, Validators } from "@angular/forms";
import { ProductService } from '../product.service';
import { first } from "rxjs/operators";
import { Router } from "@angular/router";

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

  constructor(private formBuilder: FormBuilder, private router: Router, private productService: ProductService) { }

  addForm: FormGroup;
  submitted = false;

  ngOnInit() {
    this.addForm = this.formBuilder.group({
      _id: [],
      title: ['', Validators.required],
      description: ['', Validators.required],
      price: ['', Validators.required],
      company: ['', Validators.required]
    });
  }

  onSubmit(){
    this.submitted = true;
    
    if(this.addForm.valid){
      this.productService.addProduct(this.addForm.value)
      .subscribe( data => {
        console.log(data);
        this.router.navigate(['']);
      });
    }
  }

  // get the form short name to access the form fields
  get f() { return this.addForm.controls; }

}

In this component class, we have used Validators to check whether all required fields are filled or not. When user click on Add Product button on Product List page, user will be navigating to the Add Product page.

                   ![](https://i1.wp.com/www.zeptobook.com/wp-content/uploads/2019/01/Add-Product.png?fit=635%2C1024&ssl=1)

In the above pic, if you try to save without filing all the fields, it will red highlight the textbook and let you not to save the product. So, fill all the fields and save the new product. As soon as your product is saved, you will be redirecting again to the all products page with newly product added.

Also, notice the following code in list-products.component.html file. We have (click) event on the Add Product button, and calling the addProduct() method of list-products.component.ts class. In this method, we are simply navigating to our route add-product, and opening the add-product component page.

addProduct(): void {
    this.router.navigate(['add-product']);
  }

5. Delete Product

In order to delete any product, we don’t need to create any component. For each product in grid on list-products.component.html component, we have Delete button attached to it. There is an deleteProduct(product) method on (click) event of this Delete button. deleteProduct(product) method is defined in list-products.component.ts file.

deleteProduct(product: ProductModel){
    this.productService.deleteProduct(product._id).subscribe(data=>{
      console.log(data);
      this.getAllProducts();
    });
  }

After deleting the product, we are fetching all the products again by calling this.getAllProducts() method.

6. Creating Component For Updating Product

In order to update any product, we are going to create another component. Create component edit-product in your app. Add creating your component, we have to define the route as well in your app. Open the app-routing.module.tsfile and import this component there.

app-routing.module.ts

baseurl: string = “http://localhost:3000/”;
Add the route in routes constant.
baseurl: string = “http://localhost:3000/”;#### edit-product.component.html

<div class="col-md-3">
  <h2 class="text-center">Edit Product</h2>
  <form [formGroup]="editForm" (ngSubmit)="onSubmit()">
    <div class="form-group">
      <label>Title:</label>
      <input type="text" formControlName="title" placeholder="Title" class="form-control" [ngClass]="{ 'is-invalid': submitted && f.title.errors }">
      <div *ngIf="submitted && f.title.errors">
        <div *ngIf="f.title.errors.required">Title is required</div>
      </div>
    </div>

    <div class="form-group">
      <label>Description:</label>
      <input type="text" formControlName="description" placeholder="Description" class="form-control" [ngClass]="{ 'is-invalid': submitted && f.description.errors }">
      <div *ngIf="submitted && f.description.errors">
        <div *ngIf="f.description.errors.required">Description is required</div>
      </div>
    </div>

    <div class="form-group">
      <label>Price ($):</label>
      <input type="text" formControlName="price" placeholder="Price" class="form-control" [ngClass]="{ 'is-invalid': submitted && f.price.errors }">
      <div *ngIf="submitted && f.price.errors">
        <div *ngIf="f.price.errors.required">Price is required</div>
      </div>
    </div>

    <div class="form-group">
        <label>Company:</label>
        <input type="text" formControlName="company" placeholder="Company" class="form-control" [ngClass]="{ 'is-invalid': submitted && f.company.errors }">
        <div *ngIf="submitted && f.company.errors">
          <div *ngIf="f.company.errors.required">Company is required</div>
        </div>
    </div>

    <button type="submit"  class="btn btn-info">Update</button>
  </form>
</div>

Our edit-product component html file is similar to add-product component html file. So, you can simply copy paste the same html here.

edit-product.component.ts

import { Component, OnInit } from '@angular/core';
import { FormBuilder, FormGroup, FormControl, Validators } from "@angular/forms";
import { ProductService } from '../product.service';
import { first } from "rxjs/operators";
import { Router } from "@angular/router";
import { ProductModel } from '../ProductModel';

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

  product: ProductModel;
  editForm: FormGroup;
  submitted = false;

  constructor(private formBuilder: FormBuilder, private router: Router, private productService: ProductService) { }

  ngOnInit() {
    let productId = localStorage.getItem("productId");
    if(!productId){
      alert("Something wrong!");
      this.router.navigate(['']);
      return;
    }

    this.editForm = this.formBuilder.group({
      _id: [],
      title: ['', Validators.required],
      description: ['', Validators.required],
      price: ['', Validators.required],
      company: ['', Validators.required]
    });

    this.productService.getProductById(productId).subscribe(data=>{
      console.log(data);
      this.editForm.patchValue(data); //Don't use editForm.setValue() as it will throw console error
    });
  }

  // get the form short name to access the form fields
  get f() { return this.editForm.controls; }

  onSubmit(){
    this.submitted = true;
    
    if(this.editForm.valid){
      this.productService.updateProduct(this.editForm.value)
      .subscribe( data => {
        console.log(data);
        this.router.navigate(['']);
      });
    }
  }

}

Here, we are using localStorage.getItem() to get the productId, which was stored in our list-product.component.ts file using localStorage.setItem(). So, let’s first get the productId first from localStorage() in ngOnInit() method. See below code, how we are setting productId in localStorage(). After that, we are navigating to our edit component page.

list-product.component.ts

updateProduct(product: ProductModel){
    localStorage.removeItem("productId");
    localStorage.setItem("productId", product._id);
    this.router.navigate(['edit-product']);
  }

After retrieving the productId from localStorage, we are going to get the product using this productId in ngOnInit() function.

edit-product.component.ts

this.productService.getProductById(productId).subscribe(data=>{
      console.log(data);
      this.editForm.patchValue(data); //Don't use editForm.setValue() as it will throw console error
    });

On successful, we are setting the form values with the response data using patchValue() method.

                                                ![](https://i2.wp.com/www.zeptobook.com/wp-content/uploads/2019/01/Edit-Product.png?resize=387%2C504&ssl=1)

Update the values in the form and click on Update button. It will call the onSubmit() function of edit-product.component.ts class file. After updating the values in database, it will be redirected to product listing component page.

So, we have done with our CRUD operation on our product model. Let me show you complete app-routing and app.module files.

Angular 7 Routing: app-routing.module.ts
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { CommonModule } from '@angular/common';
import { ListProductsComponent } from './list-products/list-products.component';
import { AddProductComponent } from './add-product/add-product.component';
import { EditProductComponent } from './edit-product/edit-product.component';

const routes: Routes = [
  { path: 'add-product', component: AddProductComponent },
  { path: 'edit-product', component: EditProductComponent},
  { path: '', component: ListProductsComponent, pathMatch: 'full' }
];

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

Angular 7 Module: app.module.ts
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { HttpClientModule } from '@angular/common/http';
import { ReactiveFormsModule } from "@angular/forms"; 

import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { ProductService } from './product.service';
import { ListProductsComponent } from './list-products/list-products.component';
import { AddProductComponent } from './add-product/add-product.component';
import { EditProductComponent } from './edit-product/edit-product.component';

@NgModule({
  declarations: [
    AppComponent,
    ListProductsComponent,
    AddProductComponent,
    EditProductComponent
  ],
  imports: [
    BrowserModule,
    AppRoutingModule,
    HttpClientModule,
    ReactiveFormsModule
  ],
  providers: [ProductService],
  bootstrap: [AppComponent]
})
export class AppModule { }

Install Bootstrap and FontAwesome in Angular 7

Use this npm command to install the Bootstrap and FontAwesome in your project.

baseurl: string = “http://localhost:3000/”;## Importing the Bootstrap and FontAwesome

After installation, you can add these css files in your styles.css file.

/* You can add global styles to this file, and also import other style files */
@import "~bootstrap/dist/css/bootstrap.css";
@import "~font-awesome/css/font-awesome.css";

Angular 7 Project Structure

This will be our app structure.

Download

You can download all the project files from GitHub.

Download Project Files

Summary

In this blog, we learned about how to perform CRUD operation on our MongoDB database using Node.Js Api. I also shared the project files on Github, from where you can view and download it.

By : Adesh

Angular 7 Tutorial - Learn Angular 7 by Example

Angular 7 Tutorial - Learn Angular 7 by Example

Watch or read this full crash course on learning Angular 7, tutorial is perfectly suited towards a beginner with no prior Angular experience.

Watch or read this full crash course on learning Angular 7, tutorial is perfectly suited towards a beginner with no prior Angular experience.

Ever since the release of Angular 2, I have created a full course for each new iteration. Today is no different, as Angular 7 just released!

With this beginner’s crash course, I make the assumption that you have never worked with Angular before. Therefore, this tutorial is perfectly suited towards a beginner with no prior Angular experience. The only thing you should be familiar with is HTML, CSS and JavaScript.

In this course, you’re going to discover just how powerful Angular 7 is when it comes to creating frontend web apps. Let’s get started!

If you prefer watching a video…

Be sure to Subscribe to the Official Coursetro Youtube Channel for more videos.

Installation

You’re first going to need to install the Angular CLI (Command Line Interface) tool, which helps you start new Angular 7 projects as well as assist you during development. In order to install the Angular CLI, you will need Nodejs. Make sure you install this with the default options and reload your command line or console after doing so.

In your command line, type:

> npm install -g @angular/cli

Once complete, you can now access the CLI by simply starting any commands with ng.

Hop into whichever folder you want to store your projects, and run the following command to install a new Angular 7 project:

> ng new ng7-pre

It’s going to present you with a couple questions before beginning:

? Would you like to add Angular routing? Yes
? Which stylesheet format would you like to use? SCSS   [ http://sass-lang.com   ]

It will take a minute or two and once completed, you can now hop into the new project folder by typing:

> cd ng7

Open up this project in your preferred code editor (I use Visual Studio Code, and you can launch it automatically by typing code . in the current folder), and then run this command to run a development server using the Angular CLI:

> ng serve -o

-o is for open, this flag will open your default browser at http://localhost:4200. Tip: You can type ng to get a list of all available commands, and ng [command] --help to discover all their flags.

Awesome! If all went smooth, you should be presented with the standard landing page template for your new Angular 7 project:

Angular 7 Components

The most basic building block of your Angular 7 application (and this is a concept that’s not new) is the component. A component consists of three primary elements:

  • The HTML template
  • The logic
  • The styling (CSS, Sass, Stylus, etc…)

When we use the Angular CLI to start a new project, it generates a single component, which is found in /src/app/:

/app.component.html
/app.component.scss
/app.component.ts

While we have three files here that represent the three elements above, the .ts (TypeScript) is the heart of the component. Let’s take a look at that file:

import { Component } from '@angular/core';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss']
})
export class AppComponent {
  title = 'ng7-pre';
}


Here, because this is a component, we’re importing Component from the @angular/core library, and then it defines what’s called a @Component decorator, which provides configuration options for that particular component.

As you can see, it’s referencing the location of the HTML template file and the CSS file with the templateUrl property and the styleUrls property.

The logic of the component resides in the class at the bottom. As you can see, the CLI starter template simply defines a single property called title.

Let’s use the Angular CLI to create our own components that we’ll need going forward. In the console, issue the following commands:

> ng generate component nav
// output

> ng g c about
// output

> ng g c contact
// output

> ng g c home
// output

Notice we first use the full syntax to generate a component, and then we use a shorthand syntax, which makes life a little bit easier. The commands do the same thing: generate components.

When we generate components this way, it will create a new folder in the /src/ folder based on the name we gave it, along with the respective template, CSS, .ts and .spec (for testing) files.

Angular 7 Templating

You may have noticed that one of the components we generated was called nav. Let’s implement a header bar with a navigation in our app!

The first step is to visit the app.component.html file and specify the following contents:

<app-nav></app-nav>

<section>
  <router-outlet></router-outlet>
</section>

So, we’ve removed a bunch of templating and placed in , what does this do and where does it come from?

Well, if you visit /src/app/nav/nav.component.ts you will see that in the component decorator, there’s a selector property bound to the value of app-nav. When you reference the selector of a given component in the form of a custom HTML element, it will nest that component inside of the component it’s that’s referencing it.

If you save the file you just updated, you will see in the browser we have a simple, nav works! And that’s because the nav.component.html file consists of a simple paragraph stating as much.

At this point, let’s specify the following HTML to create a simple navigation:

<header>
  <div class="container">
    <a routerLink="/" class="logo">apptitle</a>
    <nav>
      <ul>
        <li><a routerLink="/">Home</a></li>
        <li><a routerLink="/about">About</a></li>
        <li><a routerLink="/contact">Contact us</a></li>
      </ul>
    </nav>
  </div>
</header>

The only thing that might look a little strange is routerLink. This is an Angular 7 specific attribute that allows you to direct the browser to different routed components. The standard href element will not work.

While we’re here on the subject of templating, what if we wanted to display properties that are coming from our component? We use what’s called interpolation.

Make the following adjustment to our template:

<!-- From: -->
<a routerLink="/">myapp</a>

<!-- To: -->
<a routerLink="/">{{ appTitle }}</a>


Interpolation is executed by wrapping the name of a property that’s defined in the component between {{ }}.

Let’s define that property in nav.component.ts:

export class NavComponent implements OnInit {

  appTitle: string = 'myapp';
  // OR (either will work)
  appTitle = 'myapp';

  constructor() { }

  ngOnInit() {
  }

}

You can use the TypeScript way of defining properties or standard JavaScript. Save the file and you will see myapp is back in the template.

There’s a lot more to templating, but we will touch on those topics as we continue. For now, let’s apply style to our header.

First, let’s visit the global stylesheet by opening /src/styles.scss and define the following rulesets:

@import url('https://fonts.googleapis.com/css?family=Montserrat:400,700');

body, html {
    height: 100%;
    margin: 0 auto;
}

body {
    font-family: 'Montserrat';
    font-size: 18px;
}

a {
    text-decoration: none;
}

.container {
    width: 80%;
    margin: 0 auto;
    padding: 1.3em;
    display: grid;
    grid-template-columns: 30% auto;

    a {
        color: white;
    }
}

section {
    width: 80%;
    margin: 0 auto;
    padding: 2em;
}

Visit nav/component.scss and paste the following contents:

header {
    background: #7700FF;

    .logo {
        font-weight: bold;
    }

    nav {
        justify-self: right;

        ul {
            list-style-type: none;
            margin: 0; padding: 0;

            li {
                float: left;

                a {
                    padding: 1.5em;
                    text-transform: uppercase;
                    font-size: .8em;

                    &:hover {
                        background: #8E2BFF;
                    }
                }
            }
        }
    }
}

If you save and refresh, this should be the result in the browser:

Awesome!

Angular 7 Routing

Now that we have a navigation, let’s make our little app actually navigation between our components as needed.

Open up /src/app/app-routing.module.ts and specify the following contents:

// Other imports removed for brevity

import { HomeComponent } from './home/home.component';
import { AboutComponent } from './about/about.component';
import { ContactComponent } from './contact/contact.component';

const routes: Routes = [
  { path: '', component: HomeComponent },
  { path: 'about', component: AboutComponent },
  { path: 'contact', component: ContactComponent },
];

// Other code removed for brevity

As we can see here, we’re defining importing our components and defining an object for each route inside of the routes constant. These route objects also accept other properties, which allow you to define URL parameters, but because our app is simple, we won’t be doing any of that.

Save this file and try clicking on the links above. You will see that each of the respective component’s HTML templating shows up in the  defined in app.component.html.

This is what the result should look like in the browser at this point:

You now know enough about Angular 7 to create a very simple website with routing! But let’s learn more than that.

Angular 7 Event Binding

In the next several sections, we’re going to use our /src/app/home component as a playground of sorts to learn features specific to Angular 7.

One of the most used forms of event binding is the click event. You often need to make your app respond when a user clicks something, so let’s do that!

Visit the /src/app/home/home.component.html template file and specify the following:

<h1>Home</h1>

<button (click)="firstClick()">Click me</button>

You define an event binding by wrapping the event between (), and calling a method. You define the method in the home.component.ts file as such:

export class HomeComponent implements OnInit {

  constructor() { }

  ngOnInit() {
  }

  firstClick() {
    console.log('clicked');
  }

}

Save it, get out the browser console (CTRL+SHIFT+i) and click on the button. The output should show clicked. Great!

You can experiment with the other event types by replacing (click) with the names below:

(focus)="myMethod()"
(blur)="myMethod()" 
(submit)="myMethod()"  
(scroll)="myMethod()"

(cut)="myMethod()"
(copy)="myMethod()"
(paste)="myMethod()"

(keydown)="myMethod()"
(keypress)="myMethod()"
(keyup)="myMethod()"

(mouseenter)="myMethod()"
(mousedown)="myMethod()"
(mouseup)="myMethod()"

(click)="myMethod()"
(dblclick)="myMethod()"

(drag)="myMethod()"
(dragover)="myMethod()"
(drop)="myMethod()"

Angular 7 Class & Style Binding

Sometimes you may need to change the appearance of your UI from your component logic. There are two ways to do this, through class and style binding.

There are a lot of different methods you can use to control class binding, so we won’t cover them all. But I will cover some of the most common use cases.

Let’s say that you want to control whether or not a CSS class is applied to a given element. Update the h1 element in home.component.html to the following:

<h1 [class.gray]="h1Style">Home</h1>

Here, we’re saying that the CSS class of .gray should only be attached to the h1 element if the property h1Style results to true. Let’s define that in the home.component.ts file:

  h1Style: boolean = false;

  constructor() { }

  ngOnInit() {
  }

  firstClick() {
    this.h1Style = true;
  }

Let’s also define the .gray class in this component’s scss file:

.gray {
    color: gray;
}

Save it, and you can now click on the click me button to change the color of the Home title.

What if you wanted to control multiple classes on a given element? You can use ngClass. Modify the home component’s template file to the following:

<h1 [ngClass]="{
  'gray': h1Style,
  'large': !h1Style
}">Home</h1>

Then, add the large ruleset to the .scss file:

.large {
    font-size: 4em;
}

Now give it a shot in the browser. Home will appear large, but shrink down to the regular size when you click the button. Great!

You can also control appearance by changing the styles directly from within the template. Modify the template as such:

<h1 [style.color]="h1Style ? 'gray': 'black'">Home</h1>

Refresh and give this a shot by clicking the button.

Like ngClass() there’s also an ngStyle() that works the same way:

<h1 [ngStyle]="{
  'color': h1Style ? 'gray' : 'black',
  'font-size': !h1Style ? '1em' : '4em'
}">Home</h1>

Give this a go! Awesome!

Angular 7 Services

Services in Angular 7 allow you to define code that’s accessible and reusable throughout multiple components. A common use case for services is when you need to communicate with a backend of some sort to send and receive data.

> ng generate service data

Open up the new service file /src/app/data.service.ts and let’s create the following method:

// Other code removed for brevity

export class DataService {

  constructor() { }

  firstClick() {
    return console.log('clicked');
  }
}

To use this in a component, visit /src/app/home/home.component.ts and update the code to the following:

import { Component, OnInit } from '@angular/core';
import { DataService } from '../data.service';

@Component({
  selector: 'app-home',
  templateUrl: './home.component.html',
  styleUrls: ['./home.component.scss']
})
export class HomeComponent implements OnInit {

  constructor(private data: DataService) { }

  ngOnInit() {
  }

  firstClick() {
    this.data.firstClick();
  }

}


There are 3 things happening here:

  • The HTML template
  • The logic
  • The styling (CSS, Sass, Stylus, etc…)

If you try this, you will see that it works as clicked will be printed to the console. Awesome! This means that you now know how to create methods that are accessible from any component in your Angular 7 app.

Angular 7 HTTP Client

Angular comes with its own HTTP library that we will use to communicate with a fake API to grab some data and display it on our home template. This will take place within the data.service file that we generated with the CLI.

In order to gain access to the HTTP client library, we have to visit the /src/app/app.module.ts file and make a couple changes. Up until this point, we haven’t touched this file, but the CLI has been modifying it based on the generate commands we’ve issued to it.

Add the following to the imports section at the top:

// Other imports
import { HttpClientModule } from '@angular/common/http';

Next, add it to the imports array:

  imports: [
    BrowserModule,
    AppRoutingModule,
    HttpClientModule,    // <-- Right here
  ],

Now we can use it in our /src/app/data.service.ts file:

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';  // Import it up here

@Injectable({
  providedIn: 'root'
})
export class DataService {

  constructor(private http: HttpClient) { }

  getUsers() {
    return this.http.get('https://reqres.in/api/users')
  }
}


reqres.in is a free public API that we can use to grab data.

Open up our home.component.ts file and modify the following:

export class HomeComponent implements OnInit {

  users: Object;

  constructor(private data: DataService) { }

  ngOnInit() {
    this.data.getUsers().subscribe(data => {
        this.users = data
        console.log(this.users);
      }
    );
  }

}

The first thing you might notice is that we’re placing the code inside of the ngOnInit() function, which is a lifecycle hook for Angular. Any code placed in here will run when the component is loaded.

We’re defining a users property, and then we’re calling the .getUsers() method and subscribing to it. Once the data is received, we’re binding it to our users object and also console.logging it.

Give it a try in the browser and you will see the console shows an object that’s returned. Let’s display it on our home template!

Open up home.component.html and specify the following:

<h1>Users</h1>

<ul *ngIf="users">
  <li *ngFor="let user of users.data">
    <img [src]="user.avatar">
    <p>{{ user.first_name }} {{ user.last_name }}</p>
  </li>
</ul>

Great! Let’s specify some CSS to make this look better in home.component.scss:

ul {
    list-style-type: none;
    margin: 0;padding: 0;

    li {
        background: rgb(238, 238, 238);
        padding: 2em;
        border-radius: 4px;
        margin-bottom: 7px;
        display: grid;
        grid-template-columns: 60px auto;

        p {
            font-weight: bold;
            margin-left: 20px;
        }

        img {
            border-radius: 50%;
            width: 100%;
        }
    }
}

View the result in the browser:

Awesome!

Angular 7 Forms

If you recall, we generated a component called contact. Let’s create a contact form so that you can learn how to use forms in Angular 7.

Angular 7 provides you with two different approaches to dealing with forms: template driven and reactive forms. I’m not going to go into the differences between these two approaches, but reactive forms generally provide you with more control andform validation can be unit tested as opposed to template driven forms.

To get started, we have to visit the app.module.ts file and import the Reactive Forms Module:

// other imports
import { ReactiveFormsModule } from '@angular/forms';

// other code
  imports: [
    BrowserModule,
    AppRoutingModule,
    HttpClientModule,
    ReactiveFormsModule  // <- Add here
  ],

Next, visit the contact.component.ts file and specify the following

import { Component, OnInit } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';

@Component({
  selector: 'app-contact',
  templateUrl: './contact.component.html',
  styleUrls: ['./contact.component.scss']
})
export class ContactComponent implements OnInit {

  messageForm: FormGroup;
  submitted = false;
  success = false;

  constructor(private formBuilder: FormBuilder) { }

  ngOnInit() {
    this.messageForm = this.formBuilder.group({
      name: ['', Validators.required],
      message: ['', Validators.required]
    });
  }

  onSubmit() {
    this.submitted = true;

    if (this.messageForm.invalid) {
        return;
    }

    this.success = true;
}

}


First, we’re importing FormBuilder, FormGroup, Validators from @angular/forms.

Then we’re setting a few boolean properties that will help us determine when the form has been submitted and if it validation is successful.

Then we’re creating an instance of the formBuilder in the constructor. We then use this form building to construct our form properties in the ngOnInit() lifecycle hook.

We have two properties, name and message.

Then we created an onSubmit() method that will be called when the user submits the form. This is typically where you would call upon a method in the service to communicate with a mail service of sorts.

Next, visit contact.component.html:

<h1>Contact us</h1>

<form [formGroup]="messageForm" (ngSubmit)="onSubmit()">

    <h5 *ngIf="success">Your form is valid!</h5>

    <label>
      Name:
      <input type="text" formControlName="name">
      <div *ngIf="submitted && messageForm.controls.name.errors" class="error">
        <div *ngIf="messageForm.controls.name.errors.required">Your name is required</div>
      </div>
    </label>

    <label>
      Message:
      <textarea formControlName="message"></textarea>
      <div *ngIf="submitted && messageForm.controls.message.errors" class="error">
        <div *ngIf="messageForm.controls.message.errors.required">A message is required</div>
      </div>
    </label>

    <input type="submit" value="Send message" class="cta">

  </form>

  <div *ngIf="submitted" class="results">
    <strong>Name:</strong> 
    <span>{{ messageForm.controls.name.value }}</span>

    <strong>Message:</strong> 
    <span>{{ messageForm.controls.message.value }}</span>
  </div>


Baked in here is a full form with validation. It also prints out the form values beneath it when the form has been submitted.

Let’s update the css for the component to make it look decent:

label {
    display: block;

    input, textarea {
        display: block;
        width: 50%;
        margin-bottom: 20px;
        padding: 1em;
    }

    .error {
        margin-top: -20px;
        background: yellow;
        padding: .5em;
        display: inline-block;
        font-size: .9em;
        margin-bottom: 20px;
    }
}

.cta {
    background: #7700FF;
    border: none;
    color: white;

    text-transform: uppercase;
    border-radius: 4px;
    padding: 1em;
    cursor: pointer;
    font-family: 'Montserrat';
}

.results {
    margin-top: 50px;

    strong {
        display: block;
    }
    span {
        margin-bottom: 20px;
        display: block;
    }
}

Save it, and the result in the browser should look like this!

Awesome!

Conclusion

As you can see, Angular 7 is quite powerful but we’ve only just scratched the surface. If you’re a beginner, I recommend trying to create a really simple project using what you’ve learned so far and then take it from there.

Learn More

Angular 7 (formerly Angular 2) - The Complete Guide

Learn and Understand AngularJS

Angular Crash Course for Busy Developers

The Complete Angular Course: Beginner to Advanced

Angular (Angular 2+) & NodeJS - The MEAN Stack Guide

Become a JavaScript developer - Learn (React, Node,Angular)

Angular (Full App) with Angular Material, Angularfire & NgRx

The Web Developer Bootcamp

Build a Basic CRUD App with Laravel and Angular

Build a Basic CRUD App with Laravel and Angular

This tutorial teaches you how to build a simple CRUD application with a Laravel backend API and an Angular frontend.

This tutorial teaches you how to build a simple CRUD application with a Laravel backend API and an Angular frontend.

Laravel is a popular PHP framework for Web application development and it’s a pretty good choice if you’re starting a new project today for multiple reasons:

Laravel is a well-architectured framework that’s easy to pick up and write elegant code, but it’s powerful as well. It contains many advanced features out-of-the-box: Eloquent ORM, support for unit/feature/browser tests, job queues, and many more. There’s an abundance of great learning resources and it boasts one of the largest communities on the net, so it’s easy to find developers who are familiar with it.

Laravel includes a decent templating engine if you’re going old school (by generating HTML on the server side) and it also comes out of the box with a great frontend build tool (the webpack-based Laravel Mix) and Vue.js for building single-page applications. Because of this, the combination of Laravel + Vue.js has turned into the de-facto standard. However, Laravel might be choosing Vue.js but it doesn’t mean you are limited to it!

One of the best ‘hidden’ features of Laravel is that it’s very easy to use it to create a REST-ful API that can drive a frontend built in your preferred framework. Of course, you can also go with the light-weight version of Laravel, Lumen, if you need a high-performance API with minimum overhead and bootstrapping time of less than 40 ms, but for most purposes, the full-featured Laravel will do just fine. Today, I’m going to show you how easy it is to set up a Laravel API that is consumed by an Angular 6 application. We’ll use Okta for user authentication and authorization in our app, which will allow us to implement security the right way without any hassle.

Before you start, you’ll need to set up a development environment with PHP 7 and Node.js 8+/npm. You will also need an Okta developer account.

Why Use Okta for Authentication?

Well, we might be biased, but we think Okta makes identity management easier, more secure, and more scalable than what you’re used to. Okta is an API service that allows you to create, edit, and securely store user accounts and user account data, and connect them with one or more applications. Our API enables you to:

Register for a forever-free developer account, and when you’re done, come back to learn more about building a secure CRUD app with Laravel and Angular.

Angular and Laravel CRUD Application

Today we’ll build a simple trivia game interface that will allow you to run trivia games for your friends. Here’s what the finished app will look like:

Here’s one possible way to run the game as the host:

You can, of course, make up your own rules as well.

Create a Free Okta Developer Account

Let’s set up our Okta account so it’s ready when we need it.

Before you proceed, you need to log into your Okta account (or create a new one for free) and set up a new OIDC app. You’ll mostly use the default settings. Make sure to take note of your Okta domain and the Client ID generated for the app.

Here are the step-by-step instructions:

Go to the Applications menu item and click the ‘Add Application’ button:

Select ‘Single Page Application’ and click ‘Next’.

Set a descriptive application name, add http://localhost:3000/login as a Login redirect URI, and click Done. You can leave the rest of the settings as they are.

Install and Configure the Laravel Application

We will follow the installation instructions from the official Laravel documentation (https://laravel.com/docs/5.7/installation)

First, let’s install the ‘laravel’ command globally on our system through composer. Then we’ll create a new Laravel project, navigate to it and start the development PHP server:

composer global require laravel/installer
laravel new trivia-web-service
cd trivia-web-service
php artisan serve


Next, we’ll create a MySQL database and user for our app (you are free to use a different database engine if you prefer):

mysql -uroot -p
CREATE DATABASE trivia CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER 'trivia'@'localhost' identified by 'trivia';
GRANT ALL on trivia.* to 'trivia'@'localhost';
quit


Now let’s edit our .env file and enter our database credentials:

.env

DB_DATABASE=trivia
DB_USERNAME=trivia
DB_PASSWORD=trivia


Create a Backend API with Laravel

We’ll start by creating a model and a migration for our Player entity.

php artisan make:model Player -m
Model created successfully.
Created Migration: 2018_10_08_094351_create_players_table


The -m option is short for –migration and it tells Artisan to create one for our model.

Edit the migration file and update the up() method:

database/migrations/2018_10_08_094351_create_players_table.php

public function up()
{
    Schema::create('players', function (Blueprint $table) {
        $table->increments('id');
        $table->string('name');
        $table->integer('answers')->default(0);
        $table->integer('points')->default(0);
        $table->timestamps();
    });
}


Then run the migration to create the table:

php artisan migrate


Edit the model file and add a $fillable attribute to the class so we can define which fields we can mass-assign in create() and update() operations on the model:

app/Player.php

class Player extends Model
{
    protected $fillable = ['name', 'answers', 'points'];
}


Now we’ll create two API resources: Player (dealing with an individual player) and PlayerCollection (dealing with a collection of players).

php artisan make:resource Player
php artisan make:resource PlayerCollection


Modify the transformation functions toArray() of the resources:

app/Http/Resources/Player.php

public function toArray($request)
{
    return [
        'id'         => $this->id,
        'name'       => $this->name,
        'answers'    => (int) $this->answers,
        'points'     => (int) $this->points,
        'created_at' => $this->created_at,
        'updated_at' => $this->updated_at,
    ];
}


app/Http/Resources/PlayerCollection.php

public function toArray($request)
{
    return [
        'data' => $this->collection
    ];
}


We can now create the routes and controller for our REST API.

php artisan make:controller PlayerController


routes/api.php

Route::get('/players', '[email protected]');
Route::get('/players/{id}', '[email protected]');
Route::post('/players', '[email protected]');
Route::post('/players/{id}/answers', '[email protected]');
Route::delete('/players/{id}', '[email protected]');
Route::delete('/players/{id}/answers', '[email protected]');


app/Http/Controllers/PlayerController.php

...
use App\Player;
use App\Http\Resources\Player as PlayerResource;
use App\Http\Resources\PlayerCollection;
...

class PlayerController extends Controller
{
    public function index()
    {
        return new PlayerCollection(Player::all());
    }

    public function show($id)
    {
        return new PlayerResource(Player::findOrFail($id));
    }

    public function store(Request $request)
    {
        $request->validate([
            'name' => 'required|max:255',
        ]);

        $player = Player::create($request->all());

        return (new PlayerResource($player))
                ->response()
                ->setStatusCode(201);
    }

    public function answer($id, Request $request)
    {
        $request->merge(['correct' => (bool) json_decode($request->get('correct'))]);
        $request->validate([
            'correct' => 'required|boolean'
        ]);

        $player = Player::findOrFail($id);
        $player->answers++;
        $player->points = ($request->get('correct')
                           ? $player->points + 1
                           : $player->points - 1);
        $player->save();

        return new PlayerResource($player);
    }

    public function delete($id)
    {
        $player = Player::findOrFail($id);
        $player->delete();

        return response()->json(null, 204);
    }

    public function resetAnswers($id)
    {
        $player = Player::findOrFail($id);
        $player->answers = 0;
        $player->points = 0;

        return new PlayerResource($player);
    }
}


The API allows us to get the list of all players or an individual player’s data, add/delete players, mark correct/incorrect answers by a player and reset the answers and points data of a player. There’s validation of the requests and the code generates JSON responses with the appropriate status codes - not bad at all for such a small amount of code.

To test the get methods, add some dummy data to the database (you can, of course, use the Laravel factories with the Faker library to generate data) and then access these URLs:

If you want to test the post/put/delete requests (for example with Postman), make sure to set the following header for each request:

Accept: "application/json"

so the validation errors will be returned as JSON.

Install Angular and Create the Frontend Application

We will follow the official Angular documentation (https://angular.io/guide/quickstart) and install the Angular CLI globally, then we’ll create a new project and start the server.

sudo npm install -g @angular/[email protected]^6.1
ng new trivia-web-client-angular
cd trivia-web-client-angular
ng --version
Angular CLI: 6.1.5
Angular: 6.1.9
ng serve


Navigating to http://127.0.0.1:4200/ now opens the default Angular application.

Next, we’ll add the Bulma framework (because everyone else is using Bootstrap, but we like to mix things up a bit, right):

npm install --save bulma


Add to .angular.json:

"styles": [
  ...,
  "node_modules/bulma/css/bulma.min.css"
]


Run the server again because changes to angular.json are not picked up automatically:

ng serve


Customize the Main Layout in Angular

Replace the default HTML layout:

src/app/app.component.html

<div style="text-align:center">
    <section class="section">
        <div class="container">
            <nav class="navbar" role="navigation" aria-label="main navigation">
                <div class="navbar-menu is-active buttons">
                    <button class="button is-link">Home</button>
                    <button class="button is-link">Trivia Game</button>
                </div>
            </nav>
        </div>
    </section>
</div>


Let’s create our two main components:

ng generate component Home
ng generate component TriviaGame


Adding routing:

src/app/app.module.ts

import { Routes, RouterModule } from '@angular/router';

const routes: Routes = [
    { path: '', component: HomeComponent, pathMatch: 'full' },
    { path: 'trivia', component: TriviaGameComponent },
    { path: '**', redirectTo: '', pathMatch: 'full' }
];


In the imports section:

imports: [
    BrowserModule,
    RouterModule.forRoot(routes)
],


Adding routing links and the router outlet (Replace the section):

src/app/app.component.html

<nav class="navbar" role="navigation" aria-label="main navigation">
    <div class="navbar-menu is-active buttons">
        <button class="button is-link" [routerLink]="['']">Home</button>
        <button class="button is-link" [routerLink]="['/trivia']">Trivia Game</button>
    </div>
</nav>
<router-outlet></router-outlet>


Add Authentication with Okta

We need to install the Okta Angular package first:

npm install @okta/okta-angular [email protected] --save


src/app/app.module.ts

import { OktaAuthModule, OktaCallbackComponent } from '@okta/okta-angular';

const oktaConfig = {
  issuer: '{YourIssuerURL}',
  redirectUri: 'http://localhost:4200/implicit/callback',
  clientId: '{yourClientId}'
};


Don’t forget to replace your URL and Client ID!

src/app/app.module.ts

imports: [
    BrowserModule,
    RouterModule.forRoot(routes),
    OktaAuthModule.initAuth(oktaConfig)
],


Update the routes as well:

const routes: Routes = [
    { path: '', component: HomeComponent, pathMatch: 'full' },
    { path: 'trivia', component: TriviaGameComponent },
    { path: 'implicit/callback', component: OktaCallbackComponent },
    { path: '**', redirectTo: '', pathMatch: 'full' },
];


Add the authentication code:

src/app/app.component.ts

import { Component } from '@angular/core';
import { OktaAuthService } from '@okta/okta-angular';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})

export class AppComponent {

    isAuthenticated: boolean;

    constructor(public oktaAuth: OktaAuthService) {
        this.oktaAuth.$authenticationState.subscribe(
            (isAuthenticated: boolean) => (this.isAuthenticated = isAuthenticated)
        );
    }

    async ngOnInit() {
        this.isAuthenticated = await this.oktaAuth.isAuthenticated();
    }

    login() {
        this.oktaAuth.loginRedirect('/');
    }

    logout() {
        this.oktaAuth.logout('/');
    }
}


Modify the links in the navbar:

src/app/app.component.html

<button class="button is-link" *ngIf="isAuthenticated" [routerLink]="['/trivia']">Trivia Game</button>
<button class="button is-link" *ngIf="!isAuthenticated" (click)="login()"> Login </button>
<button class="button is-link" *ngIf="isAuthenticated" (click)="logout()"> Logout </button>


src/app/app.module.ts

import { HttpModule } from '@angular/http';
...
imports: [
    BrowserModule,
    HttpModule,
    RouterModule.forRoot(routes),
    OktaAuthModule.initAuth(oktaConfig)
],


Create a Service in Angular

We’ll use the CLI tool to create the service:

ng generate service player


src/app/player.service.ts

import { Injectable } from '@angular/core';
import { OktaAuthService } from '@okta/okta-angular';
import { Http, Headers, RequestOptions } from '@angular/http';
import { Observable } from 'rxjs';

export interface Player {
    id: Number,
    name: String,
    answers: Number,
  points: number
}

const API_URL: string = 'http://localhost:8000';

@Injectable({
  providedIn: 'root'
})
export class PlayerService {

    private accessToken;
    private headers;

    constructor(private oktaAuth: OktaAuthService, private http: Http) {
        this.init();
    }

    async init() {
        this.accessToken = await this.oktaAuth.getAccessToken();
        this.headers = new Headers({
            Authorization: 'Bearer ' + this.accessToken
        });
    }

    getPlayers(): Observable<Player[]> {
        return this.http.get(API_URL + '/players',
            new RequestOptions({ headers: this.headers })
        )
        .map(res => res.json().data);
    }
}


Now we can add the code for fetching the Players data to the OnInit lifecycle hook of the TriviaGameComponent:

src/app/trivia-game/trivia-game.component.ts

import { Component, OnInit } from '@angular/core';
import { Player, PlayerService } from '../player.service';
import 'rxjs/Rx';

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

  players: Player[];
  errorMessage: string;

  constructor(private playerService: PlayerService) { }

    ngOnInit() {
        this.getPlayers();
    }

    getPlayers() {
        this.playerService
            .getPlayers()
            .subscribe(
                players => this.players = players,
                error => this.errorMessage = <any>error
            );
    }
}


We’ll get a CORS error now so let’s enable CORS for our API (switch back to our backend API directory for the next commands):

composer require barryvdh/laravel-cors


app/Http/Kernel.php

protected $middlewareGroups = [
    'web' => [
    ...
    \Barryvdh\Cors\HandleCors::class,
    ],

    'api' => [
        ...
    \Barryvdh\Cors\HandleCors::class,
    ],
];


Display List of Players in Angular

src/app/trivia-game/trivia-game.component.html

<div>
    <span class="help is-info" *ngIf="isLoading">Loading...</span>
    <span class="help is-error" *ngIf="errorMessage">{{ errorMessage }}</span>
    <table class="table" *ngIf="!isLoading && !errorMessage">
        <thead>
            <tr>
                <th>ID</th>
                <th>Name</th>
                <th>Answers</th>
                <th>Points</th>
                <th>Actions</th>
            </tr>
        </thead>
        <tbody>
            <tr *ngFor="let player of players">
                <td>{{ player.id }}</td>
                <td>{{ player.name }}</td>
                <td>{{ player.answers }}</td>
                <td>{{ player.points }}</td>
                <td>
                    <button class="button is-primary">Delete Player</button>
                </td>
            </tr>
        </tbody>
    </table>
</div>


We’ll also modify our TriviaGameComponent class to include the isLoading property (initialized to true and set to false after the list of players is retrieved from the server), and our Player model and service to include an isUpdating flag for every player (initialized to false):

src/app/trivia-game/trivia-game.component.ts

...
import { OktaAuthService } from '@okta/okta-angular';
import { Player, PlayerService } from '../player.service';
...

export class TriviaGameComponent implements OnInit {

    players: Player[];
    errorMessage: string;
    isLoading: boolean = true;

    constructor(private playerService: PlayerService,
                private oktaAuth: OktaAuthService) { }

    async ngOnInit() {
        await this.oktaAuth.getAccessToken();
        this.getPlayers();
    }

    getPlayers() {
        this.playerService
            .getPlayers()
            .subscribe(
                players => {
                    this.players = players
                    this.isLoading = false
                },
                error => {
                    this.errorMessage = <any>error
                    this.isLoading = false
                }
            );
    }


src/app/player.service.ts

export interface Player {
  ...
    isUpdating: boolean,
}
...

    getPlayers(): Observable<Player[]> {
        return this.http.get(API_URL + '/api/players',
            new RequestOptions({ headers: this.headers })
        )
        .map(res => {
          let modifiedResult = res.json().data
                modifiedResult = modifiedResult.map(function(player) {
            player.isUpdating = false;
            return player;
          });
          return modifiedResult;
        });
    }


src/app/trivia-game/trivia-game.component.ts

findPlayer(id): Player {
    return this.players.find(player => player.id === id);
}

isUpdating(id): boolean {
    return this.findPlayer(id).isUpdating;
}


Secure the Backend API with Okta

We need to secure our backend API so it authenticates the Okta token and only allows authorized requests. Let’s install the packages we need to be able to verify tokens and create a custom middleware:

composer require okta/jwt-verifier spomky-labs/jose guzzlehttp/psr7
php artisan make:middleware AuthenticateWithOkta


app/Http/Middleware/AuthenticateWithOkta.php

<?php
namespace App\Http\Middleware;

use Closure;

class AuthenticateWithOkta
{
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle($request, Closure $next)
    {
        if ($this->isAuthorized($request)) {
            return $next($request);
        } else {
            return response('Unauthorized.', 401);
        }
    }

    public function isAuthorized($request)
    {
        if (! $request->header('Authorization')) {
            return false;
        }

        $authType = null;
        $authData = null;

        // Extract the auth type and the data from the Authorization header.
        @list($authType, $authData) = explode(" ", $request->header('Authorization'), 2);

        // If the Authorization Header is not a bearer type, return a 401.
        if ($authType != 'Bearer') {
            return false;
        }

        // Attempt authorization with the provided token
        try {

            // Setup the JWT Verifier
            $jwtVerifier = (new \Okta\JwtVerifier\JwtVerifierBuilder())
                            ->setAdaptor(new \Okta\JwtVerifier\Adaptors\SpomkyLabsJose())
                            ->setAudience('api://default')
                            ->setClientId('{YOUR_CLIENT_ID}')
                            ->setIssuer('{YOUR_ISSUER_URL}')
                            ->build();

            // Verify the JWT from the Authorization Header.
            $jwt = $jwtVerifier->verify($authData);
        } catch (\Exception $e) {

            // We encountered an error, return a 401.
            return false;
        }

        return true;
    }

}


app/Http/Kernel.php

    protected $middlewareGroups = [
        'web' => [
      ...
        ],

        'api' => [
            ...
      \App\Http\Middleware\AuthenticateWithOkta::class,
        ],
    ];


Do not forget to put your own client ID and issuer URL! It also won’t hurt to extract these variables into the .env file.

Create a New Player Form in Angular

We’ll create a new component (PlayerForm) and display it below the list of players.

ng generate component PlayerForm


src/app/trivia-game/trivia-game.component.html

...
<app-player-form (playerAdded)="appendPlayer($event)"></app-player-form>


src/app/player.service.ts

...
addPlayer(player): Observable<Player> {
    return this.http.post(API_URL + '/api/players', player,
        new RequestOptions({ headers: this.headers })
    ).map(res => res.json().data);
}


src/app/player-form/player-form.component.html

<span class="help is-danger">{{ errors }}</span>
<div class="field">
    <div class="control">
        <input class="input" #playerName (keydown)="errors = ''">
    </div>
</div>
<button type="button" class="button is-primary" [class.is-loading]="isLoading" (click)="addPlayer(playerName.value)">Add Player</button>


src/app/player-form/player-form.component.ts

import { Component, OnInit, EventEmitter, Output  } from '@angular/core';
import { Player, PlayerService } from '../player.service';
import 'rxjs/Rx';

@Component({
    selector: 'app-player-form',
    templateUrl: './player-form.component.html',
    styleUrls: ['./player-form.component.css']
})

export class PlayerFormComponent implements OnInit {

    errors: string = '';
    isLoading: boolean = false;

    constructor(private playerService: PlayerService) { }

    @Output()
    playerAdded: EventEmitter<Player> = new EventEmitter<Player>();

    ngOnInit() {
    }

    addPlayer(name) {
        this.isLoading = true;
        this.playerService
            .addPlayer({
                name: name
            })
            .subscribe(
                player => {
                    this.isLoading = false;
                    player.isUpdating = false;
                    this.playerAdded.emit(player);
                },
                error => {
                    this.errors = error.json().errors;
                    this.isLoading = false;
                }
            );
    }
}


src/app/trivia-game/trivia-game.component.ts

...
appendPlayer(player: Player) {
    this.players.push(player);
}


Add Angular Functionality to Delete Player

We’ll add a button on each player row in the list:

src/app/trivia-game/trivia-game.component.html

...
<button class="button is-primary" [class.is-loading]="isUpdating(player.id)" (click)="deletePlayer(player.id)">Delete Player</button>
...


src/app/player.service.ts

...
deletePlayer(id): Observable<Player> {
    return this.http.delete(API_URL + '/api/players/' + id,
        new RequestOptions({ headers: this.headers })
    );
}
...


src/app/trivia-game/trivia-game.component.ts

...
deletePlayer(id) {
    let player = this.findPlayer(id)
    player.isUpdating = true
    this.playerService
        .deletePlayer(id)
        .subscribe(
            response => {
                let index = this.players.findIndex(player => player.id === id)
                this.players.splice(index, 1)
                player.isUpdating = false
            },
            error => {
                this.errorMessage = <any>error
                player.isUpdating = false
            }
        );
}
...


Build Trivia Service in Angular

We’ll create a new Trivia service and link it to a public API for trivia questions.

ng generate service trivia


src/app/trivia.service.ts

import { Injectable } from '@angular/core';
import { Http } from '@angular/http';

const TRIVIA_ENDPOINT: string = 'http://localhost:8000/question';

@Injectable({
    providedIn: 'root'
})
export class TriviaService {

    constructor(private http: Http) { }

    getQuestion() {
        return this.http.get(TRIVIA_ENDPOINT)
        .map(res => res.json()[0]);
    }
}


Modify the trivia game component html to include a card with the question and answer, and buttons to indicate correct and wrong answer for each player:

src/app/trivia-game/trivia-game.component.html

<div class="columns">
    <div class="column">
    <span class="help is-info" *ngIf="isLoading">Loading...</span>
    <span class="help is-error" *ngIf="errorMessage">{{ errorMessage }}</span>
    <table class="table" *ngIf="!isLoading && !errorMessage">
        <thead>
            <tr>
                <th>ID</th>
                <th>Name</th>
                <th>Answers</th>
                <th>Points</th>
                <th>Actions</th>
            </tr>
        </thead>
        <tbody>
            <tr *ngFor="let player of players">
                <td>{{ player.id }}</td>
                <td>{{ player.name }}</td>
                <td>{{ player.answers }}</td>
                <td>{{ player.points }}</td>
                <td>
                    <button class="button is-primary" [class.is-loading]="isUpdating(player.id)" (click)="rightAnswer(player.id)">Right (+1)</button>&nbsp;
                    <button class="button is-primary" [class.is-loading]="isUpdating(player.id)" (click)="wrongAnswer(player.id)">Wrong (-1)</button>&nbsp;
                    <button class="button is-primary" [class.is-loading]="isUpdating(player.id)" (click)="deletePlayer(player.id)">Delete Player</button>
                </td>
            </tr>
        </tbody>
    </table>
    <app-player-form (playerAdded)="appendPlayer($event)"></app-player-form>
    </div>
    <div class="column">
        <div class="card" *ngIf="question?.question">
          <div class="card-content">
            <button class="button is-primary" (click)="getQuestion()">Refresh Question</button>
            <p class="title">
              {{ question.question }}
            </p>
            <p class="subtitle">
              {{ question.category.title }}
            </p>
          </div>
          <footer class="card-footer">
            <p class="card-footer-item">
                <span>
                    Correct answer: {{ question.answer }}
                </span>
            </p>
          </footer>
        </div>
    </div>
</div>


src/app/trivia-game/trivia-game.component.ts

...
import { TriviaService } from '../trivia.service';
...
export class TriviaGameComponent implements OnInit {
    ...
    question: any;
    ...
    constructor(private playerService: PlayerService,
            private triviaService: TriviaService,
            private oktaAuth: OktaAuthService) { }

    async ngOnInit() {
        await this.oktaAuth.getAccessToken();
        this.getPlayers();
        this.getQuestion();
    }
    ...
    getQuestion() {
        this.triviaService
            .getQuestion()
            .subscribe(
                question => this.question = question,
                error => this.errorMessage = <any>error
            );
    }
...


We’ll add a quick unauthenticated endpoint to our API to return a random trivia question. In reality, you’d probably want to connect a real trivia database to your app, but this will do for this quick demo.

In the server project, add the following route:

routes/web.php

Route::get('/question', function(Request $request) {
    return response()->json(json_decode(file_get_contents('http://jservice.io/api/random?count=1')));
});


Add Buttons to Mark Answers Right and Wrong

src/app/player.service.ts

...
    answer(id, data): Observable<Player> {
        return this.http.post(API_URL + '/api/players/' + id + '/answers', data,
            new RequestOptions({ headers: this.headers })
        ).map(res => res.json().data);
    }
...


src/app/trivia-game/trivia-game.component.ts

...
    rightAnswer(id) {
        let data = {
            correct: true
        }
        this.answer(id, data)
    }

    wrongAnswer(id) {
        let data = {
            correct: false
        }
        this.answer(id, data)
    }

    answer(id, data) {
        let player = this.findPlayer(id)
        player.isUpdating = true
        this.playerService
            .answer(id, data)
            .subscribe(
                response => {
                    player.answers = response.answers
                    player.points = response.points
                    player.isUpdating = false
                },
                error => {
                    this.errorMessage = <any>error
                    player.isUpdating = false
                }
            );
    }
...


That’s it! You’re ready to play a trivia game.

Hopefully this article was helpful for you and you now realize how easy it is to add authentication to Laravel APIs and Angular applications.

You can find the source code for the complete application at https://github.com/oktadeveloper/okta-php-trivia-angular.

If you want to read more about Okta, Angular, or Laravel, check out the

Angular 7 (formerly Angular 2) - The Complete Guide

Learn and Understand AngularJS

Angular Crash Course for Busy Developers

The Complete Angular Course: Beginner to Advanced

Angular (Angular 2+) & NodeJS - The MEAN Stack Guide

Become a JavaScript developer - Learn (React, Node,Angular)

Angular (Full App) with Angular Material, Angularfire & NgRx

The Web Developer Bootcamp

PHP with Laravel for beginners - Become a Master in Laravel

Laravel 5 Starter Course

Laravel for RESTful: Build Your RESTful API with Laravel

Ultimate Laravel Course 2018

MEAN Stack Tutorial MongoDB, ExpressJS, AngularJS and NodeJS

MEAN Stack Tutorial MongoDB, ExpressJS, AngularJS and NodeJS

We are going to build a full stack Todo App using the MEAN (MongoDB, ExpressJS, AngularJS and NodeJS). This is the last part of three-post series tutorial

We are going to build a full stack Todo App using the MEAN (MongoDB, ExpressJS, AngularJS and NodeJS). This is the last part of three-post series tutorial

MEAN Stack tutorial series:

  1. AngularJS tutorial for beginners (Part I)
  2. Creating RESTful APIs with NodeJS and MongoDB Tutorial (Part II)
  3. MEAN Stack Tutorial: MongoDB, ExpressJS, AngularJS and NodeJS (Part III) 👈 you are here

Before completing the app, let’s cover some background about the this stack. If you rather jump to the hands-on part click here to get started.

1. Why MEAN stack?

TL; DR: NodeJS has been built from bottom up a non-blocking I/O paradigm, which gives you more efficiency per CPU core than using threads in other languages like Java.

LAMP (Linux-Apache-MySQL-PHP) has dominated web application stack for many years now. Well-known platforms such as Wikipedia, Wordpress, and even Facebook uses it or started with it. Enterprise, usually, used go down the Java path: Hibernate, Spring, Struts, JBoss. More agile frameworks also have been used such as Ruby on Rails and for Python Django and Pylon.

Ubiquitous

Well, it turns out, that JavaScript it is everywhere. It used to be limited to browsers. But, now you can found it in smartphones, servers, robots, Arduino, RaspberryPi… Thus, it does not matter what technology you use to build web applications, you need to be familiar with Javascript. In that case, then, it is a time saver to use wherever it fits, especially for building web applications. MEAN stack is embracing that, using Javascript to create end-to-end web applications. ​ Non-blocking architecture

JavaScript is a dynamic, object-oriented, and functional scripting language. One of the features that make it win over Java Applets decades ago, it was its lightness and non-blocking event loop. Blocking means that when one line of code is executing, the rest of it is locked waiting to finish. On the other hand, non-blocking gives to each line of code a shot and then through callbacks it can come back when an event happens. Programming languages that are blocking (Java, Ruby, Python, PHP, …) overcomes concurrency using many threads of execution while JavaScript handles it using non-blocking event loop in a single thread.

As you can see, a single thread of execution in Node can handle perform multiple tasks vs a non-blocking style that execute each one sequentially. You can read more about it in NodeJS faster than Java article.

Some companies like Paypal moved from Java backend to NodeJS and reported a increased performance, lower average response times, and development speed gains. Similarly happens to Groupon that came from Java/Rails monoliths.

Agile and vibrant community

The community behind Javascript is quite vibrant. It has permeated in almost all the fields of technology: data visualization, server-side, databases, robotics, building tools and many more.

2. TODO app with MEAN

In this section are going to put together everything that we learnt in the two previous tutorials.

2.1 MEAN Backend with MongoDB, ExpressJS and NodeJS

In the previous post, we have gone through the process of building a RESTful API and we are going to be building on top of that. Repository here.

git clone https://github.com/amejiarosario/todoAPIjs.git

2.2 MEAN stack front-end with AngularJS

Similarly, we have build a very lean todoApp in the first part of this tutorial. You can download the file to follow along and see it in action here. You might notice the angularJS app is very simple and even it is entirely in one file for simplicity sake. In further tutorials, we are going to make it more modular, split in files, add tests and stylesheets.

Let’s go first to the ExpressJS app (todoAPIjs) and review the default routing system:

  1. AngularJS tutorial for beginners (Part I)
  2. Creating RESTful APIs with NodeJS and MongoDB Tutorial (Part II)
  3. MEAN Stack Tutorial: MongoDB, ExpressJS, AngularJS and NodeJS (Part III) 👈 you are here
// app.js
var routes = require('./routes/index');
app.use('/', routes);

// ./routes/index.js
router.get('/', function(req, res) {
  res.render('index', { title: 'Express' });
});

// ./views/index.ejs
    <h1><%= title %></h1>
    <p>Welcome to <%= title %></p>

The best place to load our ./views/index.ejs. So let’s copy the body content from ngTodo.html content in there and change in ./routes/index.js title to “ngTodo App”. Don’t forget to add ng-app on the top. <html ng-app="app">.

diff

3. Wiring up the App 3.1 AngularJS Read with $http

As you might notice, in the factory, we have a fixed array. We need to change it to communicate with the API that we just build.

$http is Anguar core sevice that allow to make XMLHttpRequest or jsonp request. You can either pass an object with http verb and url or call call $http.verb ($http.get, $http.post).

$http returns a promise which has a success and error function.

$http({method: 'GET', url: '/todos'}).
  success(function(data, status, headers, config) {
    // this callback will be called asynchronously
    // when the response is available.
    console.log('todos: ', data );
  }).
  error(function(data, status, headers, config) {
    // called asynchronously if an error occurs
    // or server returns response with an error status.
    console.log('Oops and error', data);
  });

Let’s try it out in our app. Go to views/index.ejs and do this changes:

// Service
.factory('Todos', ['$http', function($http){
  return $http.get('/todos');
}])

// Controller
.controller('TodoController', ['$scope', 'Todos', function ($scope, Todos) {
  Todos.success(function(data){
    $scope.todos = data;
  }).error(function(data, status){
    console.log(data, status);
    $scope.todos = [];
  });
}])

diff

$http.get will request data using the GET method.

Try it in your browser!s If you have data from the previous tutorial you should be able to see it.
To start the server, you can use

npm start

or if you have it installed

nodemon

3.2 AngularJS Read with $resource

If you click in one of the Todo elements and get redirected to the detail page, you will not see anything yet. We need to update the TodoDetailCtrl first. Even though we already have the GET verb working. We have a different URL requirement for /todos/:id for the other methods. There’s an Angular service that has a higher level of abstraction of $http to deal with RESTful requests. It is called $resource.

Initialize as: $resource(url, [paramDefaults], [actions], options);

It comes with the following actions already defined; it is missing one though… Can you tell?

{ 'get':    {method:'GET'},  // get individual record
  'save':   {method:'POST'}, // create record
  'query':  {method:'GET', isArray:true}, // get list all records
  'remove': {method:'DELETE'}, // remove record
  'delete': {method:'DELETE'} }; // same, remove record

The instances are used in the following way (examples will come later):

  • GET: Resource.get([parameters], [success], [error])
  • Non-GET: Resource.action([parameters], postData, [success], [error])
  • Non-GET: resourceInstance.$action([parameters], [success], [error])

$resource is not part of the Angular core, so it requires to ngResource and the dependency. We can get it from the CDN:

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

This is what need to set it up:

  // add ngResource dependency
  angular.module('app', ['ngRoute', 'ngResource'])

  // ...

        .factory('Todos', ['$resource', function($resource){
          return $resource('/todos/:id', null, {
            'update': { method:'PUT' }
          });
        }])
// ...
        .controller('TodoController', ['$scope', 'Todos', function ($scope, Todos) {
          $scope.todos = Todos.query();
        }])

Angular will render an empty $scope.todos. but, when Todos.query() comes with the data from the server it will re-render the UI.

diff

3.3 AngularJS Create

We will need to create a new text box, a button to send a POST request to server and add it to the $scope.

Try it in your browser!s If you have data from the previous tutorial you should be able to see it.
Add this code at the bottom of the id="/todos.html" template:

New task <input type="text" ng-model="newTodo"><button ng-click="save()">Create</button>

Notice that we are using a new directive ng-click, this one executes a function when it clicked. Angular makes sure that the behaviour is consistent across different browsers.

.controller('TodoController', ['$scope', 'Todos', function ($scope, Todos) {
  $scope.todos = Todos.query();

  $scope.save = function(){
    if(!$scope.newTodo || $scope.newTodo.length < 1) return;
    var todo = new Todos({ name: $scope.newTodo, completed: false });

    todo.$save(function(){
      $scope.todos.push(todo);
      $scope.newTodo = ''; // clear textbox
    });
  }
}])

diff

3.4 Show Todo details

Every time you click a todo link, it is showing an empty fields. Let’s fix that. First we need set the real _id to the links instead of $index.

<li ng-repeat="todo in todos | filter: search">
  <input type="checkbox" ng-model="todo.completed">
  <a href="#/{{todo._id}}">{{todo.name}}</a>
</li>
.controller('TodoDetailCtrl', ['$scope', '$routeParams', 'Todos', function ($scope, $routeParams, Todos) {
  $scope.todo = Todos.get({id: $routeParams.id });
}])

Notice the change from $scope.todo = Todos[$routeParams.id]; to $scope.todo = Todos.get({id: $routeParams.id });

Now you should be able to see the details :)

diff

3.5 AngularJS Update (in-line editing)

This is going to be a very cool feature. Let’s meet these new directives:

  • GET: Resource.get([parameters], [success], [error])
  • Non-GET: Resource.action([parameters], postData, [success], [error])
  • Non-GET: resourceInstance.$action([parameters], [success], [error])

Replace the template with id="/todos.html" with the following:

<!-- Template -->
<script type="text/ng-template" id="/todos.html">
  Search: <input type="text" ng-model="search.name">
  <ul>
    <li ng-repeat="todo in todos | filter: search">
      <input type="checkbox" ng-model="todo.completed" ng-change="update($index)">
      <a ng-show="!editing[$index]" href="#/{{todo._id}}">{{todo.name}}</a>
      <button ng-show="!editing[$index]" ng-click="edit($index)">edit</button>

      <input ng-show="editing[$index]" type="text" ng-model="todo.name">
      <button ng-show="editing[$index]" ng-click="update($index)">Update</button>
      <button ng-show="editing[$index]" ng-click="cancel($index)">Cancel</button>
    </li>
  </ul>
  New task <input type="text" ng-model="newTodo"><button ng-click="save()">Create</button>
</script>

Now let’s change the controller to handle the inline editing:

.controller('TodoController', ['$scope', 'Todos', function ($scope, Todos) {
  $scope.editing = [];
  $scope.todos = Todos.query();

  $scope.save = function(){
    if(!$scope.newTodo || $scope.newTodo.length < 1) return;
    var todo = new Todos({ name: $scope.newTodo, completed: false });

    todo.$save(function(){
      $scope.todos.push(todo);
      $scope.newTodo = ''; // clear textbox
    });
  }

  $scope.update = function(index){
    var todo = $scope.todos[index];
    Todos.update({id: todo._id}, todo);
    $scope.editing[index] = false;
  }

  $scope.edit = function(index){
    $scope.editing[index] = angular.copy($scope.todos[index]);
  }

  $scope.cancel = function(index){
    $scope.todos[index] = angular.copy($scope.editing[index]);
    $scope.editing[index] = false;
  }
}])

We added a new variable $scope.editing which shows or hides the form to edit the values. Furthermore, notice ng-click functions: edit, update and cancel.

Try it in your browser!s If you have data from the previous tutorial you should be able to see it.
While were are editing notice that we copy the original todo task into the editing variable. This server for two purposes:

  1. AngularJS tutorial for beginners (Part I)
  2. Creating RESTful APIs with NodeJS and MongoDB Tutorial (Part II)
  3. MEAN Stack Tutorial: MongoDB, ExpressJS, AngularJS and NodeJS (Part III) 👈 you are here

Now, going to the Todo Details. We would like that to be updated as well and add notes.

<script type="text/ng-template" id="/todoDetails.html">
  <h1>{{ todo.name }}</h1>
  completed: <input type="checkbox" ng-model="todo.completed"><br>
  note: <textarea ng-model="todo.note"></textarea><br><br>

  <button ng-click="update()">Update</button>
  <a href="/">Cancel</a>
</script>

Similarly, we added an update method. However, this time we do not need to pass any index, since it is just one todo at a time. After it has been saved, it goes back to root path /.

.controller('TodoDetailCtrl', ['$scope', '$routeParams', 'Todos', '$location', function ($scope, $routeParams, Todos, $location) {
  $scope.todo = Todos.get({id: $routeParams.id });

  $scope.update = function(){
    Todos.update({id: $scope.todo._id}, $scope.todo, function(){
      $location.url('/');
    });
  }
}])

Try it in your browser!s If you have data from the previous tutorial you should be able to see it.
$location.url([url]) is a getter/setter method that allows us to change url, thus routing/view.

diff

3.6 AngularJS Delete

These are the changes added to perform the remove functionality:

A. Add removes button in the li element:

<button ng-show="!editing[$index]" ng-click="remove($index)">remove</button>

Do the same for the details Template

<button ng-click="remove()">Remove</button>

B. Add remove functionality in the controllers

$scope.remove = function(index){
  var todo = $scope.todos[index];
  Todos.remove({id: todo._id}, function(){
    $scope.todos.splice(index, 1);
  });
}

And also in the details controllers

$scope.remove = function(){
  Todos.remove({id: $scope.todo._id}, function(){
    $location.url('/');
  });
}

When we remove elements from the todos array $scope.todos.splice(index, 1) they also disappear from the DOM. Very cool, huh?

diff

Try it in your browser!s If you have data from the previous tutorial you should be able to see it.

*Originally published at *adrianmejia.com

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

Thanks for reading ❤

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

Follow me on Facebook | Twitter

Learn More

☞ The Complete Node.js Developer Course (3rd Edition)

☞ Angular & NodeJS - The MEAN Stack Guide

☞ MERN Stack Front To Back: Full Stack React, Redux & Node.js

☞ Node, Express, Angular 7, GraphQL and MongoDB CRUD Web App

☞ Angular 7 (formerly Angular 2) - The Complete Guide

☞ MongoDB - The Complete Developer’s Guide

☞ What is the MERN stack and how do I use it?

☞ Node.js, ExpressJs, MongoDB and Vue.js (MEVN Stack) Application Tutorial

☞ MEAN Stack Tutorial MongoDB, ExpressJS, AngularJS and NodeJS

☞ Full Stack Developers: Everything You Need to Know

Create a cryptocurrency dashboard with Angular

Create a cryptocurrency dashboard with Angular

In this tutorial , we will be creating a cryptocurrency dashboard using Angular 4 and Angular IDE.&nbsp;Cryptocurrency&nbsp;is a digital asset designed to work as a medium of exchange using cryptography to secure the transactions and to control the creation of additional units of the currency. Bitcoin is the most popular digital currency, however there are numerous cryptocurrencies in existence.

In this tutorial , we will be creating a cryptocurrency dashboard using Angular 4 and Angular IDE. Cryptocurrency is a digital asset designed to work as a medium of exchange using cryptography to secure the transactions and to control the creation of additional units of the currency. Bitcoin is the most popular digital currency, however there are numerous cryptocurrencies in existence.

On our dashboard, we will display the price, percentage change in 24 hour price, and percentage change in 7 day price of selected cryptocurrencies.

What You will Learn

In this article you will learn about Angular application development using Angular IDE. You will also learn how to use the Subject class from RxJS, a ReactiveX library for javascript.

Prerequisites

In order to follow along with this tutorial, please ensure you have installed Angular IDE.

UI Design

Below is a picture of how the cryptocurrency dashboard will look like on mobile.

Step 1 — Create new Angular project

Open Angular IDE, click File in the top menu, then select, New then click.Angular Project

In the new Angular project wizard, type crypto-dashboard as the project name, select node version 6.11.4, and npm version 3.10.10. Then click the finish button.

Angular IDE will take a few minutes to set up the project, you can see the activities output in Terminal+. Once completed we see an output like in the image below

Step 2 — Create application components

Taking a look at our UI design, we can identify two visible components, the search-filter box, and the list card. There is also an invisible component which is the list itself. The components are highlighted in the picture below:

Now, let’s create the components. In the file explorer view, right click on crypto-dashboard, select new, and click.Component

In the New Angular CLI dialog, enter search-filter as element name, and click the finish button. See image below for reference:

Repeat the steps to create the list-card, and list component. Also create a dashboard component, so we use it to organize our components. Your directory structure, under the src/app folder should be as in the image below:

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.

In the file explorer view, right click on crypto-dashboard, select new, and click Service. In the new Angular CLI Service wizard, enter App a the element name, and click finish.

Add Bootstrap CSS and Font Awesome

Let us 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

<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" />

Install angular-2-dropdown-multiselect

AngularX Dropdown Multiselect for Bootstrap CSS is used to create customizable dropdown multiselect in AngularX, TypeScript with bootstrap css. We will be needing multiselect in this project, so let’s install it by typing the command below in Terminal+ and hitting enter

npm install --save angular-2-dropdown-multiselect

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

...
import {MultiselectDropdownModule} from 'angular-2-dropdown-multiselect';

@NgModule({
...
imports: [
...
MultiselectDropdownModule,
],

Here is a screenshot of how the multi-select plugin will appear in our final result.

The API

We will be using the api provided by coinmarketcap.com. Using the sample data returned, let us create a type script shape file 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;
}

App Service

AppService is a general purpose class we will use to fetch data from the api, and pass that data on to components. We also use AppService to filter the data to display based on the options selected in the app. We could split this service into multiple services so as to abide by the single responsibility principle.

The Subject class from RxJs is very important in this code section and it’s important to touch on what they are. Firstly let us talk about RxJs, it is a reactive programming framework for Javascript. Core to understanding how to use RxJs is having a mental model of the Observer Pattern. The observer pattern is a software design pattern in which an object, called the subject, maintains a list of its dependents, called observers, and notifies them automatically of any state changes, usually by calling one of their methods.

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

Now we export class AppService, making sure we decorate it using @Injectable(), then we define three private variables allCoins: Coin[], filteredCoins: Coin[], and filter: number[]. We also define four subjects

  • coinsSubject: Which notifies it’s observers of coin data fetched from the API
  • filteredCoinsSubject: Which notifies it’s observers the filtered collection of crypto currency data. So if in the multiselect filter, only two crypto coins are selected then, the most recent value emitted by subscribing to filteredCoinsSubject will be an array of length two.
  • apiSubject : notifies it’s observers with a string message based on api actions
  • fiatSubject :notifies it’s observers about the value of the selected fiat currency when it is updated.

The full code for AppService is below, explanations are in the comments

// Imports
import {Injectable} from '@angular/core';
import {Subject} from 'rxjs/Subject';
import {HttpClient, HttpParams} from '@angular/common/http';
// Import Coin Shape file
import {Coin} from './coin';
const API_BASE_URL = 'https://api.coinmarketcap.com/v1/';

@Injectable()
export class AppService {
private allCoins: Coin[]; // will hold unmodified data returned by the api
private filteredCoins: Coin[]; // will hold data filtered from this.allCoins
private filter: number[]; // will hold the array index of data contained in this.
allCoins that should not be filtered out

// A couple of RxJs Subjects very important for communicating across Angular Components
coinsSubject: Subject<Coin[]>;
filteredCoinsSubject: Subject<Coin[]>;
apiSubject: Subject;
fiatSubject: Subject;
constructor(private http: HttpClient) {
this.filter = [];
// we initialize our subjects
this.coinsSubject = new Subject();
this.filteredCoinsSubject = new Subject();
this.apiSubject = new Subject();
this.fiatSubject = new Subject();
}
// this method loads market cap data from the API
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; // store returned data
this.announceCoins(); // trigger announcements
this.filterMarketCaps();
}
);
}

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();
}
}

App Component

Update the content of src/app/app.component.html with the content below. We display the title in a h2 element, and we set the dashboard component as a child view. If our project needed a router, we would have set it up here in App component.

<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-filter></app-search-filter>
<app-list></app-list>

Since the scope of the service instance is the parent component and its children, we will create an instance of AppService in the dashboard component which will be shared by the search-filter component and the list component. To do this we 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} from '@angular/core';
// import AppService
import {AppService} from '../app.service';
@Component({
selector: 'app-dashboard',
templateUrl: './dashboard.component.html',
styleUrls: ['./dashboard.component.css'],
providers: [AppService] // provider setup
})
export class DashboardComponent {}

SearchFilter Component

The search-filter component is made up of a form containing two elements.

The first is a select element for choosing a fiat currency used in displaying the current worth of each crypto currency. The selected currency will be stored in a model aptly named SelectedCurrency , an onChange event listener will notify the app service to load data from the api.

The second element is is a multiselect dropdown, that will enable us filter our choice of crypto currencies to display.

The code below goes in src/app/search-filter/search-filter.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-filter/search-filter.component.ts

// Imports
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-filter',
templateUrl: './search-filter.component.html',
styleUrls: ['./search-filter.component.css'],
providers: []
})
export class SearchFilterComponent implements OnInit {
currencies: string[];
cryptoCurrOptions: IMultiSelectOption[];
selectedCurrency: string;
optionsModel: number[];
// Settings configuration for the multiselect plugin
mySettings: IMultiSelectSettings = {
enableSearch: true,
checkedStyle: 'fontawesome',
buttonClasses: 'btn btn-default btn-block',
dynamicTitleMaxItems: 5,
displayAllSelectedText: true
};
// Text configuration for the multiselect plugin
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']; // fiat currency options
this.selectedCurrency = ''; // model to store selected fiat
// array to hold names of cryptos to be used in filtering
this.cryptoCurrOptions = [];
// coinsSubject is a RxJs subject in our service that will notify us when the api has gotten data about crypto coins
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);
}
// This method creates an array of valid options for the multiselect plugin from an array of crypto coins
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 to 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>

The content of src/app/list/list.component.css is

.list-cards {
display: flex;
flex-direction: column;
align-items: center;
}

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

  • filteredCoinsSubject: Which sends updates on the filtered collection of crypto currency data. So if in the multiselect filter, only two crypto coins are selected then, the most recent value emitted by subscribing to filteredCoinsSubject will be an array of length two.
  • apiSubject : emits a string message based on api actions
  • fiatSubject : emits the value of the selected fiat currency when it is updated.
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.css']
})
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.css']
})
export class ListCardComponent {
@Input() coin;
@Input() fiat;
}

Run application

Run the application from the server tab of Angular IDE.

Conclusion

I hope you learnt a few things about Angular and Angular IDE. Thanks!