How to create CRUD Application in Laravel 6 using Angular.js?

How to create CRUD Application in Laravel 6 using Angular.js?

Laravel 6 Angular.js CRUD tutorial. In this Laravel 6 CRUD Application tutorial, you'll learn how to to create CRUD Application in Laravel 6 using Angular.js

Laravel 6 Angular.js CRUD tutorial. In this tutorial, we would love to share with you how you can create a CRUD Application in Laravel 6 using the Angular js.

Angular js

AngularJS is a powerful JavaScript client-side Model-View-Controller (MVC) framework. It is especially popular for building single page applications that behavior like AJAX application.

Laravel 6 Angular JS CRUD Tutorial

  • Install Fresh Laravel Setup
  • Setup Database Credentials in .env
  • Create REST API Controller
  • Create a Model
  • Define Route
  • AngularJS application structure
    • AngularJS app.js
    • Create Angular js controller
  • Create blade view
  • Start development server

1). Install Fresh Laravel Setup

First of all, we need to install the laravel fresh application. Use the below command and download fresh new laravel setup :

composer create-project --prefer-dist laravel/laravel lara-angular

2). Setup Database Credentials in .env

In this step, go to your project directory and open .env file and set your database credentials like this:

 DB_CONNECTION=mysql
 DB_HOST=127.0.0.1
 DB_PORT=3306
 DB_DATABASE=databasename
 DB_USERNAME=db_username
 DB_PASSWORD=db_password

3). Create REST API Controller

In this step, we need to create a new controller name CustomerController. So open your command prompt and go to your project root directory than run the below-given command for creating a new CustomerController.

php artisan make:controller API/CustomerController --api

Now go to app/controller/API/CustomerController.php and update the below methods in your controller file:

<?php
 
namespace App\Http\Controllers\API;
 
use App\Customer;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\Validator;
use Illuminate\Database\Eloquent\ModelNotFoundException;
 
class CustomerController extends Controller
{
    public function index()
    {
        return response()->json([
            'error' => false,
            'customers'  => Customer::all(),
        ], 200);
    }
 
    public function store(Request $request)
    {
        $validation = Validator::make($request->all(),[ 
            'name' => 'required',
            'email' => 'required|email|unique:customers,email',
            'contact_number' => 'required',
            'position' => 'required',
        ]);
 
        if($validation->fails()){
            return response()->json([
                'error' => true,
                'messages'  => $validation->errors(),
            ], 200);
        }
        else
        {
            $customer = new Customer;
            $customer->name = $request->input('name');
            $customer->email = $request->input('email');
            $customer->contact_number = $request->input('contact_number');
            $customer->save();
     
            return response()->json([
                'error' => false,
                'customer'  => $customer,
            ], 200);
        }
    }
 
    public function show($id)
    {
        $customer = Customer::find($id);
 
        if(is_null($customer)){
            return response()->json([
                'error' => true,
                'message'  => "Record with id # $id not found",
            ], 404);
        }
 
        return response()->json([
            'error' => false,
            'customer'  => $customer,
        ], 200);
    }
 
    public function update(Request $request, $id)
    {
        $validation = Validator::make($request->all(),[ 
            'name' => 'required',
            'email' => 'required|email',
            'contact_number' => 'required',
        ]);
 
        if($validation->fails()){
            return response()->json([
                'error' => true,
                'messages'  => $validation->errors(),
            ], 200);
        }
        else
        {
            $customer = Customer::find($id);
            $customer->name = $request->input('name');
            $customer->email = $request->input('email');
            $customer->contact_number = $request->input('contact_number');
            $customer->save();
     
            return response()->json([
                'error' => false,
                'customer'  => $customer,
            ], 200);
        }
    }
 
    public function destroy($id)
    {
        $customer = Customer::find($id);
 
        if(is_null($customer)){
            return response()->json([
                'error' => true,
                'message'  => "Record with id # $id not found",
            ], 404);
        }
 
        $customer->delete();
     
        return response()->json([
            'error' => false,
            'message'  => "Customer record successfully deleted id # $id",
        ], 200);
    }
}

4). Create a Model

Now we will create one eloquent model. So go to your command prompt and run the below command here:

php artisan make:model Customer

Next, go to app/customer.php and update the below code into your file:

<?php
 
namespace App;
 
use Illuminate\Database\Eloquent\Model;
 
class Customer extends Model
{
    protected $fillable = [
        'id', 
        'name', 
        'email',
        'contact_number'
    ];
}

5). Define Route

In this step, we will define the route in routes/api.php file. So go to routes/api.php and update the below route into your file:

Route::group(['prefix' => 'v1','namespace' => 'API'], function(){      
Route::apiResource('customers', 'CustomerController');  });

6). AngularJS application structure

Our application will have the following structure
Here :

  • app – contains all AngularJS related JavaScript files
  • app/controllers – contains all AngularJS controllers
  • css – contains all CSS files
  • js – contains all regular JavaScript files for our UI.

AngularJS app.js

In this step, we will create a new file name app.js. So go to /public/app, create a new file name app.js and update the below code into your app.js file:

This file will be used to define our application

var app = angular.module('customerRecords', [])
        .constant('API_URL', 'http://localhost:8000/api/v1/');

Here,

  • var app = angular.module('customerRecords', []) creates an AngularJS module and assigns the object to the variable app. All AngularJS files will be reference the variable app
  • .constant('API_URL', '[http://localhost:8000/api/v1/](http://localhost:8000//api/v1/)'); defines a constant variable with the API URL.

Create AngularJS controllers

Now /public/app/controllers and create a controller name customers.js controller. And update the below code into your customers.js file:

app.controller('customersController', function ($scope, $http, API_URL) {
     
    //fetch customers listing from 
 
    $http({
        method: 'GET',
        url: API_URL + "customers"
    }).then(function (response) {
        $scope.customers = response.data.customers;
        console.log(response);
    }, function (error) {
        console.log(error);
        alert('This is embarassing. An error has occurred. Please check the log for details');
    });
 
    //show modal form
 
    $scope.toggle = function (modalstate, id) {
        $scope.modalstate = modalstate;
        $scope.customer = null;
 
        switch (modalstate) {
            case 'add':
                $scope.form_title = "Add New Customer";
                break;
            case 'edit':
                $scope.form_title = "Customer Detail";
                $scope.id = id;
                $http.get(API_URL + 'customers/' + id)
                    .then(function (response) {
                        console.log(response);
                        $scope.customer = response.data.customer;
                    });
                break;
            default:
                break;
        }
         
        console.log(id);
        $('#myModal').modal('show');
    }
 
    //save new record and update existing record
    $scope.save = function (modalstate, id) {
        var url = API_URL + "customers";
        var method = "POST";
 
        //append customer id to the URL if the form is in edit mode
        if (modalstate === 'edit') {
            url += "/" + id;
 
            method = "PUT";
        }
 
        $http({
            method: method,
            url: url,
            data: $.param($scope.customer),
            headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
        }).then(function (response) {
            console.log(response);
            location.reload();
        }), (function (error) {
            console.log(error);
            alert('This is embarassing. An error has occurred. Please check the log for details');
        });
    }
 
    //delete record
    $scope.confirmDelete = function (id) {
        var isConfirmDelete = confirm('Are you sure you want this record?');
        if (isConfirmDelete) {
 
            $http({
                method: 'DELETE',
                url: API_URL + 'customers/' + id
            }).then(function (response) {
                console.log(response);
                location.reload();
            }, function (error) {
                console.log(error);
                alert('Unable to delete');
            });
        } else {
            return false;
        }
    }
});

HERE,

  • app.controller('customersController', function($scope, $http, API_URL) {...} defines a controller employeesController in the app variable that we created in /app/app.js. We have injected $scope, $http, and a constant API_URL as dependencies
  • assigns response.employees to $scope.customersvariable. The $scope.customersvariable will be available in our view.
  • $scope.toggle = function(modalstate, id) {...} displays the modal form
  • $scope.save = function(modalstate, id){...} saves a new record or updates an existing record depending on whether the variable id has a value or not.
  • $scope.confirmDelete = function(id){...} deletes an existing record

7). Create a blade view

Now go to /resources/views and create a new file name index.blade.php and update the below code into your file:

<!doctype html>
<html lang="en" ng-app="customerRecords">
    <head>
        <!-- Required meta tags -->
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1,
            shrink-to-fit=no">
 
        <!-- Bootstrap CSS -->
        <link rel="stylesheet"
            href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css"
            integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm"
            crossorigin="anonymous">
 
        <title>Laravel 6 Crud application Angular JS Tutorial</title>
    </head>
    <body>
        <div class="container" ng-controller="customersController">
            <header>
                <h2>customers Database</h2>
            </header>
            <div>
                <table class="table">
                    <thead>
                        <tr>
                            <th>ID</th>
                            <th>Name</th>
                            <th>Email</th>
                            <th>Contact No</th>
                            <th><button id="btn-add" class="btn btn-primary
                                    btn-xs"
                                    ng-click="toggle('add', 0)">Add New customer</button></th>
                        </tr>
                    </thead>
                    <tbody>
                        <tr ng-repeat="customer in customers">
                            <td>{{ customer.id }}</td>
                            <td>{{ customer.name }}</td>
                            <td>{{ customer.email }}</td>
                            <td>{{ customer.contact_number }}</td>
                            <td>
                                <button class="btn btn-default btn-xs
                                    btn-detail"
                                    ng-click="toggle('edit', customer.id)">Edit</button>
                                <button class="btn btn-danger btn-xs btn-delete"
                                    ng-click="confirmDelete(customer.id)">Delete</button>
                            </td>
                        </tr>
                    </tbody>
                </table>
            </div>
            <!-- Modal -->
            <div class="modal fade" id="myModal" tabindex="-1"
                role="dialog" aria-labelledby="exampleModalLabel"
                aria-hidden="true">
                <div class="modal-dialog" role="document">
                    <div class="modal-content">
                        <div class="modal-header">
                            <h5 class="modal-title" id="exampleModalLabel">{{form_title}}</h5>
                            <button type="button" class="close"
                                data-dismiss="modal" aria-label="Close">
                                <span aria-hidden="true">×</span>
                            </button>
                        </div>
                        <div class="modal-body">
                            <form name="frmcustomers" class="form-horizontal"
                                novalidate="">
 
                                <div class="form-group error">
                                    <label for="inputEmail3" class="col-sm-12
                                        control-label">Name</label>
                                    <div class="col-sm-12">
                                        <input type="text" class="form-control
                                            has-error" id="name" name="name"
                                            placeholder="Fullname"
                                            value="{{name}}"
                                            ng-model="customer.name"
                                            ng-required="true">
                                        <span class="help-inline"
                                            ng-show="frmcustomers.name.$invalid
                                            &amp;&amp; frmcustomers.name.$touched">Name
                                            field is required</span>
                                    </div>
                                </div>
 
                                <div class="form-group">
                                    <label for="inputEmail3" class="col-sm-12
                                        control-label">Email</label>
                                    <div class="col-sm-12">
                                        <input type="email" class="form-control"
                                            id="email" name="email"
                                            placeholder="Email Address"
                                            value="{{email}}"
                                            ng-model="customer.email"
                                            ng-required="true">
                                        <span class="help-inline"
                                            ng-show="frmcustomers.email.$invalid
                                            &amp;&amp; frmcustomers.email.$touched">Valid
                                            Email field is required</span>
                                    </div>
                                </div>
 
                                <div class="form-group">
                                    <label for="inputEmail3" class="col-sm-12
                                        control-label">Contact No</label>
                                    <div class="col-sm-12">
                                        <input type="text" class="form-control"
                                            id="contact_number"
                                            name="contact_number"
                                            placeholder="Contact Number"
                                            value="{{contact_number}}"
                                            ng-model="customer.contact_number"
                                            ng-required="true">
                                        <span class="help-inline"
                                            ng-show="frmcustomers.contact_number.$invalid
                                            &amp;&amp;
                                            frmcustomers.contact_number.$touched">Contact
                                            number field is required</span>
                                    </div>
                                </div>
                                 
                            </form>
                        </div>
                        <div class="modal-footer">
                            <button type="button" class="btn btn-secondary"
                                data-dismiss="modal">Close</button>
                            <button type="button" class="btn btn-primary"
                                id="btn-save" ng-click="save(modalstate, id)"
                                ng-disabled="frmcustomers.$invalid">Save changes</button>
                        </div>
                    </div>
                </div>
            </div>
        </div>
 
        <!-- jQuery first, then Popper.js, then Bootstrap JS -->
        <script src="https://code.jquery.com/jquery-3.2.1.slim.min.js"
            integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN"
            crossorigin="anonymous"></script>
        <script
            src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js"
            integrity="sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q"
            crossorigin="anonymous"></script>
        <script
            src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js"
            integrity="sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl"
            crossorigin="anonymous"></script>
 
        <!-- Load Javascript Libraries (AngularJS, JQuery, Bootstrap) -->
        <script
            src="https://ajax.googleapis.com/ajax/libs/angularjs/1.7.8/angular.min.js"></script>
        <script
            src="https://ajax.googleapis.com/ajax/libs/angularjs/1.7.8/angular-animate.min.js"></script>
        <script
            src="https://ajax.googleapis.com/ajax/libs/angularjs/1.7.8/angular-route.min.js"></script>
        <!-- AngularJS Application Scripts -->
        <script src="<?= asset('app/app.js') ?>"></script>
        <script src="<?= asset('app/controllers/customers.js') ?>"></script>
    </body>
</html>

Next, go to routes/web.php file and change welcome to index, like this:

Route::get('/', function () {
    return view('index');
});

7). Start Development Server

We need to start the development server. Use the PHP artisan serve command and start your server :

 php artisan serve

Load the following URL in your web browser

http://localhost:8000/

Extract and Display Laravel Validation Errors in Angular

Extract and Display Laravel Validation Errors in Angular

In this post, we are going to be more concrete and provide more extensive information on the Extract and Display Laravel Validation Errors in Angular

In this post, we are going to be more concrete and provide more extensive information on the Extract and Display Laravel Validation Errors in Angular

While working on Xamcademy, I've had the need to extract and display form validation errors returned by the Xamcademy API which is powered by Laravel. I wrote a utility function a while back which I forgot about, and remembered while trying to write something similar today.

1. Extracting the Validation Errors

For reference, here's a sample error response:

Here's the utility function:

 import {HttpErrorResponse} from '@angular/common/http';

export const extractErrorMessagesFromErrorResponse = (errorResponse: HttpErrorResponse) => {
  // 1 - Create empty array to store errors
  const errors = [];

  // 2 - check if the error object is present in the response
  if (errorResponse.error) {

    // 3 - Push the main error message to the array of errors
    errors.push(errorResponse.error.message);

    // 4 - Check for Laravel form validation error messages object
    if (errorResponse.error.errors) {

      // 5 - For each error property (which is a form field)
      for (const property in errorResponse.error.errors) {

        if (errorResponse.error.errors.hasOwnProperty(property)) {

          // 6 - Extract it's array of errors
          const propertyErrors: Array<string> = errorResponse.error.errors[property];

          // 7 - Push all errors in the array to the errors array
          propertyErrors.forEach(error => errors.push(error));
        }

      }

    }

  }

  return errors;
};

This function can then be called in the error callback of the HTTPClient request to your API. Let's use a hypothetical UserFormComponent as an example.

import {Component, OnInit} from '@angular/core';
import {FormBuilder, Validators} from '@angular/forms';
import {IUser} from '../../state/user.model';
import {UserService} from '../../state/users.service';
import {take} from 'rxjs/operators';
import {HttpErrorResponse} from '@angular/common/http';
import {
  extractErrorMessagesFromErrorResponse,
} from '../../../@core/helper-functions/extract-error-messages-from-error-response';

@Component({
  selector: 'xc-user-form',
  templateUrl: './user-form.component.html',
  styleUrls: ['./user-form.component.scss'],
})
export class UserFormComponent implements OnInit {

  form: FormGroup;

  constructor(private fb: FormBuilder, private userService: UserService) {}

  ngOnInit() {
    this.initForm();
  }

  private initForm() {

    this.form = this.fb.group({
      id: [],
      email: [{value: '', disabled: true}, Validators.required],
      name: ['', Validators.required],
      // ...other fields
    });
  }

  submit(formData: IUser) {
    this.userService.create(formData)
      .pipe(
        take(1),
      )
      .subscribe(
        (response) => {
          // do something with success response
        },
        (errorResponse: HttpErrorResponse) => {
          // Extract form error messages from API  <------ HERE!!!
          const messages = extractErrorMessagesFromErrorResponse(errorResponse);
        },
      );
  }
}

Our interest is in the submit() function, specifically the error callback of the HTTPClient response observable.

We call the UserService's create method which is in charge of making the request to the API through Angular's HTTPClient and returning the response.

We then proceed to call our extractErrorMessagesFromErrorResponse function defined above and store the returned errors in the messages variable. You can place that function where it makes sense for you in your Angular project, I often use a helper-functions folder.

2. Displaying the Validation Errors

You can choose to display the errors however you like, given it's an array of strings you can loop over in the component template. Having written such functionality a good number of times across projects already, I decided to write something I could re-use, the FormStatus class.

interface IShowMessages {
  error?: boolean;
  success?: boolean;
}

export class FormStatus {
  submitted: boolean;
  showMessages: IShowMessages;
  errors: string[];
  messages: string[];

  constructor(params?: {submitted?: boolean, showMessages?: IShowMessages, errors?: string[], messages?: string[]}) {
    this.submitted = (params && params.submitted) || false;
    this.showMessages = (params && params.showMessages) || {};
    this.errors = (params && params.errors) || [];
    this.messages = (params && params.messages) || [];
  }

  /**
   * Determines if error messages can be shown
   */
  canShowErrors() {
    return this.showMessages.error && this.errors && this.errors.length > 0 && !this.submitted;
  }

  /**
   * Determines if success messages can be shown
   */
  canShowSuccess() {
    return this.showMessages.success && this.messages && this.messages.length > 0 && !this.submitted;
  }

  /**
   * Called when the form it's attached to is being submitted
   * Resets the `errors` and `messages` arrays and sets `submitted` to true
   */
  onFormSubmitting() {
    this.errors = this.messages = [];
    this.submitted = true;
  }

  /**
   * Called when the form it's attached to has received a response for it's submit action
   * Sets `submitted` to false, sets and displays error or success messages
   *
   * @param params
   */
  onFormSubmitResponse(params: {success: boolean, messages: string[]}) {
    this.submitted = false;
    if (params.success) {
      this.showMessages.success = true;
      this.messages = params.messages;
    } else {
      this.showMessages.error = true;
      this.errors = params.messages;
    }
  }
}

We're going to use this class in the UserFormComponent's ts file and template to automatically handle repetitve aspects of the form submission process like setting the form as submitted, clearing the messages arrays on submit, and determining whether to show the messages as success or error messages. If you've had to deal with these over and over in your forms, then you'll understand where am coming from.

Let's update the UserFormComponent to make use of our new class.

 import {Component, OnInit} from '@angular/core';
import {FormBuilder, Validators} from '@angular/forms';
import {IUser} from '../../state/user.model';
import {UserService} from '../../state/users.service';
import {take} from 'rxjs/operators';
import {HttpErrorResponse} from '@angular/common/http';
import {
  extractErrorMessagesFromErrorResponse,
} from '../../../@core/helper-functions/extract-error-messages-from-error-response';
import {FormStatus} from '../../../@core/form-helpers/form-status';

@Component({
  selector: 'xc-user-form',
  templateUrl: './user-form.component.html',
  styleUrls: ['./user-form.component.scss'],
})
export class UserFormComponent implements OnInit {

  form: FormGroup;

  // 1 - Initialize a form status object for the component
  formStatus = new FormStatus();

  constructor(private fb: FormBuilder, private userService: UserService) {}

  ngOnInit() {
    this.initForm();
  }

  private initForm() {

    this.form = this.fb.group({
      id: [],
      email: [{value: '', disabled: true}, Validators.required],
      name: ['', Validators.required],
      // ...other fields
    });
  }

  submit(formData: IUser) {
    // 2 - Call onFormSubmitting to handle setting the form as submitted and
    //     clearing the error and success messages array
    this.formStatus.onFormSubmitting();

    this.userService.create(formData)
      .pipe(
        take(1),
      )
      .subscribe(
        (response) => {
          // do something with success response
        },
        (errorResponse: HttpErrorResponse) => {
          const messages = extractErrorMessagesFromErrorResponse(errorResponse);
          // 3 - call onFormSubmitResponse with the submission success status (false) and the array of messages
          this.formStatus.onFormSubmitResponse({success: false, messages: messages});
        },
      );
  }
}

The additions to the component's ts file are marked as comments 1, 2, and 3.

Let's now see what the template for this component can look like.

<form novalidate [formGroup]="form" (submit)="submit(form.value)">

  <!-- OUR AREA OF INTEREST -->
  <ng-container *ngIf="formStatus">

    <div *ngIf="formStatus.canShowErrors()" class="alert alert-danger" role="alert">
      <div><strong>Oops!</strong></div>
      <div *ngFor="let error of formStatus.errors">{{ error }}</div>
    </div>

    <div *ngIf="formStatus.canShowSuccess()" class="alert alert-success" role="alert">
      <div><strong>Success!</strong></div>
      <div *ngFor="let message of formStatus.messages">{{ message }}</div>
    </div>

  </ng-container>

  <div class="row">

    <div class="col-md-12">
      <div class="form-group">
        <!-- Input field for email goes here -->
      </div>

      <div class="form-group">
        <!-- Input field for name goes here -->
      </div>
    </div>

    <div class="col-md-12 text-right">
      <button type="submit"
              [class.btn-pulse]="formStatus.submitted"
              [disabled]="!form?.valid || formStatus.submitted">
        Save
      </button>
    </div>

  </div>

</form>

FYI, using Bootstrap 4 as CSS framework. Here's a preview for the errors in the error response image at the start of the post.

With that we've come to the end of this blog post, and hope this is useful to you in some way. Thank for reading !

Integrate PHP Laravel 5.8 APIs in Angular 7

Integrate PHP Laravel 5.8 APIs in Angular 7

For PHP Laravel APIs to integrate in Angular 7, we have to write APIs in api.php file in Laravel which is in the routes folder in Laravel project structure. For the sake of this article, we are using example of User API.

we commonly use PHP Laravel 5 for database integration and Angular for frontend single page applications. So it makes sense to write about integrating PHP Laravel APIs with Angular frontend.

1. Develop Laravel API

Lets setup the database first. We follow proper Laravel structure through commands and code.

1.1 Create Laravel API migration

For PHP Laravel APIs to integrate in Angular 7, we have to write APIs in api.php file in Laravel which is in the routes folder in Laravel project structure. For the sake of this article, we are using example of User API. For User API to fetch and save data, you have to first setup database table. For that you have to write the migration for the table the command to create migration is written below.

php artisan make:migration create_user_table

After creating a migration, you have to make a model for the table that you have created. Which can be made by command written below.

1.2 Laravel model command

php artisan make:model User

short hand for creating migration and model both you will use this command

php artisan make:model User -m

After using this command the migration and model is made.

1.3 Laravel migration code example

Now you can write table in the migration. Below is the example to write table in migration.

public function up()
 {
 Schema::create('users', function (Blueprint $table) {
 $table-&gt;increments('id');
 $table-&gt;string('email')-&gt;unique();
 $table-&gt;string('first_name');
 $table-&gt;string('last_name');
 $table-&gt;string('password');
 $table-&gt;enum('account_status',['active','pending','rejected'])-&gt;default('pending');
 $table-&gt;enum('gender',['male','female']);
 $table-&gt;timestamps();
 });
 }

You can see the more about how to write table in the migration go to this link:

https://laravel.com/docs/5.7/migrations. After writing the table you have to run this command to migrate the table into the database

php artisan migrate

After migration, you have to write a method into the model that was created to get, update, or add data into the table. For an API to run you have to make,

Route::post('allUsers','[email protected]');

1. 4 Laravel controller command

php artisan make:controller UserController

1.4 Laravel controller code example

The controller function will look like this.

public function getAllUsers(){
 $count = User::All()-&gt;count();
 if($count &gt; 0){
 $users = User::role(['user','admin','dealer'])-&gt;get();
 return response()-&gt;json($users);
 }else{
 return response()-&gt;json("Users Not Found");
 }
 }

You can also make resource and collection to return the response. You can go this link and check the eloquent resources and collection https://laravel.com/docs/5.7/eloquent-resources.

So in this api the api is using UserController Method getllAllUsers to get the users from the database. After that you have to serve the laravel project to use these apis into angular project.

2. Integrate Laravel API in Angular

First setup your angular project using this command. Using Angular Cli.

ng new project_name

After that make your first component. For making component use this command.

ng generate component Users
// Or short hand:
ng g c Users

Four files will be generated after running that command. After that design your page in the html file

and in the ts file your code goes there. After that you have to make service to integrate the api calls that has been written in the Laravel so to create a service run this command.

ng generate service User
// Or short hand:
ng g s User

After running this command 2 file will be generated you will write the code in the ts file name users.service.ts there you will first import the HttpClient first. Then go to the environment folder and write the the api served url of the laravel project for local the url will look like this.

http://127.0.0.1:8000

The Laravel project will always serve on the 8000 port.

After importing the HttpClient then import the environment.ts file in the users service and get the api url from there. For importing just import the HttpClient into the constructor of the service.ts file.

After importing our service constructor will look like this.

 apiUrl: any;
constructor(private http: HttpClinet){
this.apiUrl = environment.apiUrl;
}

After that write a function to to call the API of the Laravel and we will use the HttpClient in the function an example is written below.

getAllUsers() {
 return this.http.get(this.apiUrl + 'allUsers/')
 .pipe(map((response: any) =&gt; {
 return response.json();
 }));
 }

This function is returning the data of all the users and parse then into the JSON response and return from where this function will get called.

After creating this function to get the data from the Laravel API now we will use this function into the the component that we have created first. To use the users service function import the users.service.ts file into the constructor of the users component. So this is the example to use the this service into the component.

The constructor will look like this after importing

constructor(private usersService: UsersService){
}

Here you can change the variable name for the user Service. You can set the variable name as you like. There is an ngOnInit() function in every component this is the first function to get called when ever the project gets served or this component is called. To get all the users from the service function that is calling Laravel API call to get all users the code will look like this.

allusers: any;
getUsers(){
this.usersService. getAllUsers()
.subscribe({
response =&gt; {
this.allusers=response;
},
 });
}

Here we have made first a global variable that is storing the response from the call into itself so that the response can be used the users.component.html file and anywhere in the users.component.ts file.

The getUsers function is calling the service function gellAllUsers to get the user and save it into the local variable. Now you can use this variable in whatever the way you like into your html page to show it on the webpage.

In Angular, you can also make the model to map the response also so for that you need to make a file names users.model.ts there is no specific command for that. So the model is made for the easiness to use the response of the API. Example is given below.

export class User{
 id: number;
 first_name: string;
 last_name: string;
 email: string;
 gender: string;
 status: string;
}

For mapping the response on to the model we will use Observables. Observable provide the support for passing the messages between creators of the observables and the subscribers in our application. Here the subscriber will be our getUsers function in the users.component.ts file and the Observable will be defined in the users.service.ts.

For using observers we will import the rxjs Observable into the users.service.ts file. Then after that the Observable will be used like this as shown in example below.

getUsers(): Observable&lt;{data: Users[]}&gt;{
 const headers = new HttpHeaders({'Content-Type': 'application/json'});
 return this.http.get&lt;{status_code: number, data: Users[], message: string}&gt;(this.apiUrl +
'allUsers', {headers: headers});
 }

Here the observable is parsing the response into the user model which we have created above as we are getting all the users from the API response so we are use Users[] array. And to get its parsed over here you have to format the response also in the Laravel API function. After this we also need to change the users.component.ts file so it will look like this.

 

allusers: Users[];

First we need to make your allUsers variable type to Users[] as the model and as we have done it in the service file the the gellUsers function will able to map response now.

getUsers(){
this.usersService. getAllUsers()
.subscribe({
response =&gt; {
this.allusers=response;
},
 });
}

Now it has made it easy for you to get the variable easily that are in the users model if you want to see the firstname of all the users you can easily do this

this.allusers.foreach(element =&gt;{
console.log('First Name',element.firstname);
})

as firstname is the variable that is in the model of Users.

If you follow this detailed tutorial, you can learn to write the APIs in Laravel and to integrate them in Angular project.

Thanks for reading. If you liked this post, share it with all of your programming buddies!

This post was originally published here

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