Learning Management Platform for Written Tutorial Series.

Laravel 6 AngularJS Tutorial

Laravel 6 AngularJS Tutorial

In this tutorial, we are going to create a simple CREATE, READ, UPDATE, and DELETE CRUD application. We will use Laravel 6 for the backend and AngularJS 1.7.8 for the front end. In accordance with the laws of beauty, we will use twitter bootstrap to add beauty to our simple application.

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. We will soon publish tutorial series on AngularJS. Subscribe to our free newsletter to get updates delivered to your mailbox when we publish the tutorial series on AngularJS.

This tutorial assumes you are familiar with basics of Laravel 6, MySQL, PHP, and Composer.

Topics to be covered

We will cover the following topics in this tutorial.

  • Laravel 6 AngularJS backend (REST API)
  • AngularJS application structure
  • AngularJS app.js
  • AngularJS controllers employees.js
  • Displaying data from the REST API using AngularJS
  • AngularJS form validation

Laravel 6 AngularJS backend (REST API)

In this section, we will create a Laravel 6 application, database table using migrations and a simple REST API that will perform the CRUD operations. Let's get our hands dirty.

Step 1: Create new Laravel 6 Application

Run the following command to create a new Laravel project using composer.

composer create-project laravel/laravel angulara 6.0.*

HERE,

  • The above command creates a new Laravel 6.0 application

Step 2: Database migrations

We first need to set the database configuration for our application

Open .env file in the project root

Set the database configurations as shown below

DB_HOST=localhost
DB_DATABASE=angulara
DB_USERNAME=root
DB_PASSWORD=melody

Save the changes

Note: use the database name, username and password that match the ones you have on your machine.

Run the following script in MySQL to create angulara database

CREATE DATABASE `angulara`;

We will now use the artisan command to create a migration file that will create a table for employee records.

Run the following artisan command to create a migration file

php artisan make:migration create_employees_table

You will get the following message

Created migration: timestamp_create_employees_table

Let's now modify the newly created migration file

Open the migration file /database/migrations/timestamp_create_employees_table

Modify the contents to the following

<?php

use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;

class CreateEmployeesTable extends Migration
{
    public function up()
    {
        Schema::create('employees', function (Blueprint $table) {
            $table->bigIncrements('id');
            $table->string('name');
            $table->string('email')->unique();
            $table->string('contact_number');
            $table->string('position');
            $table->timestamps();
        });
    }

    public function down()
    {
        Schema::dropIfExists('employees');
    }
}

Save the changes.

Run the following artisan command to run the migration

php artisan migrate

You will get the following messages

Migrating: 2014_10_12_000000_create_users_table
Migrated:  2014_10_12_000000_create_users_table (1.36 seconds)
Migrating: 2014_10_12_100000_create_password_resets_table
Migrated:  2014_10_12_100000_create_password_resets_table (1.6 seconds)
Migrating: 2019_08_19_000000_create_failed_jobs_table
Migrated:  2019_08_19_000000_create_failed_jobs_table (1.31 seconds)
Migrating: 2019_09_16_134349_create_employees_table
Migrated:  2019_09_16_134349_create_employees_table (1.06 seconds)

Check your database in MySQL. You should have an employees table created.

Step 3: Simple REST API

Let's now create a controller for our REST API.

Run the following artisan command

php artisan make:controller API/EmployeeController --api

HERE,

  • The above command creates an API resource controller.

You will get the following message

Controller created successfully.

Let's now create an Eloquent ORM model for our REST API

php artisan make:model Employee

You will get the following message

Model created successfully.

Let's now add a fillable array to our model

Open Employee.php controller located in /app/Employee

Modify the code to the following

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Employee extends Model
{
    protected $fillable = [
        'id', 
        'name', 
        'email',
        'contact_number',
        'position'
    ];
}

Let's now modify the controller code

Open EmployeeController.php in /app/Http/Controllers/API

Update the code to the following

<?php

namespace App\Http\Controllers\API;

use App\Employee;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\Validator;
use Illuminate\Database\Eloquent\ModelNotFoundException;

class EmployeeController extends Controller
{
    public function index()
    {
        return response()->json([
            'error' => false,
            'employees'  => Employee::all(),
        ], 200);
    }

    public function store(Request $request)
    {
        $validation = Validator::make($request->all(),[ 
            'name' => 'required',
            'email' => 'required|email|unique:employees,email',
            'contact_number' => 'required',
            'position' => 'required',
        ]);

        if($validation->fails()){
            return response()->json([
                'error' => true,
                'messages'  => $validation->errors(),
            ], 200);
        }
        else
        {
            $employee = new Employee;
            $employee->name = $request->input('name');
            $employee->email = $request->input('email');
            $employee->contact_number = $request->input('contact_number');
            $employee->position = $request->input('position');
            $employee->save();
    
            return response()->json([
                'error' => false,
                'employee'  => $employee,
            ], 200);
        }
    }

    public function show($id)
    {
        $employee = Employee::find($id);

        if(is_null($employee)){
            return response()->json([
                'error' => true,
                'message'  => "Record with id # $id not found",
            ], 404);
        }

        return response()->json([
            'error' => false,
            'employee'  => $employee,
        ], 200);
    }

    public function update(Request $request, $id)
    {
        $validation = Validator::make($request->all(),[ 
            'name' => 'required',
            'email' => 'required|email',
            'contact_number' => 'required',
            'position' => 'required',
        ]);

        if($validation->fails()){
            return response()->json([
                'error' => true,
                'messages'  => $validation->errors(),
            ], 200);
        }
        else
        {
            $employee = Employee::find($id);
            $employee->name = $request->input('name');
            $employee->email = $request->input('email');
            $employee->contact_number = $request->input('contact_number');
            $employee->position = $request->input('position');
            $employee->save();
    
            return response()->json([
                'error' => false,
                'employee'  => $employee,
            ], 200);
        }
    }

    public function destroy($id)
    {
        $employee = Employee::find($id);

        if(is_null($employee)){
            return response()->json([
                'error' => true,
                'message'  => "Record with id # $id not found",
            ], 404);
        }

        $employee->delete();
    
        return response()->json([
            'error' => false,
            'message'  => "Employee record successfully deleted id # $id",
        ], 200);
    }
}

HERE,

  • The above methods respond to our Create, Read, Update, and Delete operations. These concepts are fully covered in Laravel 6 Tutorial series so we wont go into more details explaining them here. Our API also does validation

We now need to define the routes for our REST API

Let's start with the API routes.

Open /routes/api.php

Add the following route definition

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

HERE,

  • Route::group(['prefix' => 'v1','namespace' => 'API'], function(){...} we define a group route that we prefix with v1 and define the namespace for our API which is API.
  • Route::apiResource('employees', 'EmployeeController'); defines an API resource route. This maps all the methods that we have in the controller EmployeeController to the resource employees and automatically maps the HTTP verbs GET, POST, PUT, and DELETE to the appropriate methods.

Run the following command to see the routes that have been registered

php artisan route:list

You should be able to see the following results

Laravel 6 AngularJS Routes

That was it for our REST API. Let's now create the frontend using AngularJS

AngularJS application structure

Our application will have the following structure

Laravel 6 AngularJS App 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.

Create the directories as shown in the above image

AngularJS app.js

This file will be used to define our application

Create a new file app.js in the directory /public/app

Add the following code to it

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

HERE,

  • var app = angular.module('employeeRecords', []) 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/'); defines a constant variable with the API URL.

AngularJS controllers employees.js

This is the file that will be responsible for interacting with our API. Let's look at the $http module and how it works before we code our application.

$http({
    method: 'METHOD', //i.e. GET, POST, PUT, DELETE
    url: API_URL //url to the backend API
}).then(function (response) { // anonymous function called when request succeeded
    console.log(response);
}, function (error) { // anonymous function called when request fails
    console.log(error);
});

HERE,

  • $http({...}); service is responsible for communicating with the backend API.
  • .then(function (response) {...} then is executed if the request is successful. The parameter response contains the response from the backend server.
  • , function (error) {...} is executed if an error occurs. The parameter error contains the error information.

Create a new file employees.js in the directory /public/app/controllers

Add the following code to it

app.controller('employeesController', function ($scope, $http, API_URL) {
    //retrieve employees listing from 

    $http({
        method: 'GET',
        url: API_URL + "employees"
    }).then(function (response) {
        $scope.employees = response.data.employees;
        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.employee = null;

        switch (modalstate) {
            case 'add':
                $scope.form_title = "Add New Employee";
                break;
            case 'edit':
                $scope.form_title = "Employee Detail";
                $scope.id = id;
                $http.get(API_URL + 'employees/' + id)
                    .then(function (response) {
                        console.log(response);
                        $scope.employee = response.data.employee;
                    });
                break;
            default:
                break;
        }
        
        console.log(id);
        $('#myModal').modal('show');
    }

    //save new record / update existing record
    $scope.save = function (modalstate, id) {
        var url = API_URL + "employees";
        var method = "POST";

        //append employee 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.employee),
            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 + 'employees/' + id
            }).then(function (response) {
                console.log(response);
                location.reload();
            }, function (error) {
                console.log(error);
                alert('Unable to delete');
            });
        } else {
            return false;
        }
    }
});

HERE,

  • app.controller('employeesController', 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
  • $http({method: 'GET',url: API_URL + "employees"}).then(...),function(...) uses Angular $http service to call the API. API_URL + "employees") is passed as an argument to the get method of $http service. If the call is successful, then response is passed as a parameter to .then anonymous function. The anonymous function assigns response.employees to $scope.employees variable. The $scope.employees variable 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

Displaying data from the REST API using AngularJS

We will now create a view that displays the data from the REST API. Both blade template and AngularJS use double curly braces to display data. In order to avoid conflicts between the two, we will not save the view as a blade template. It will be a regular view.

Create a new file index.php in the directory /resources/views

Add the following code

<!doctype html>
<html lang="en" ng-app="employeeRecords">
    <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 AngularJS Tutorial</title>
    </head>
    <body>
        <div class="container" ng-controller="employeesController">
            <header>
                <h2>Employees Database</h2>
            </header>
            <div>
                <table class="table">
                    <thead>
                        <tr>
                            <th>ID</th>
                            <th>Name</th>
                            <th>Email</th>
                            <th>Contact No</th>
                            <th>Position</th>
                            <th><button id="btn-add" class="btn btn-primary
                                    btn-xs"
                                    ng-click="toggle('add', 0)">Add New Employee</button></th>
                        </tr>
                    </thead>
                    <tbody>
                        <tr ng-repeat="employee in employees">
                            <td>{{ employee.id }}</td>
                            <td>{{ employee.name }}</td>
                            <td>{{ employee.email }}</td>
                            <td>{{ employee.contact_number }}</td>
                            <td>{{ employee.position }}</td>
                            <td>
                                <button class="btn btn-default btn-xs
                                    btn-detail"
                                    ng-click="toggle('edit', employee.id)">Edit</button>
                                <button class="btn btn-danger btn-xs btn-delete"
                                    ng-click="confirmDelete(employee.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">&times;</span>
                            </button>
                        </div>
                        <div class="modal-body">
                            <form name="frmEmployees" 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="employee.name"
                                            ng-required="true">
                                        <span class="help-inline"
                                            ng-show="frmEmployees.name.$invalid
                                            && frmEmployees.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="employee.email"
                                            ng-required="true">
                                        <span class="help-inline"
                                            ng-show="frmEmployees.email.$invalid
                                            && frmEmployees.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="employee.contact_number"
                                            ng-required="true">
                                        <span class="help-inline"
                                            ng-show="frmEmployees.contact_number.$invalid
                                            &&
                                            frmEmployees.contact_number.$touched">Contact
                                            number field is required</span>
                                    </div>
                                </div>

                                <div class="form-group">
                                    <label for="inputEmail3" class="col-sm-12
                                        control-label">Position</label>
                                    <div class="col-sm-12">
                                        <input type="text" class="form-control"
                                            id="position" name="position"
                                            placeholder="Position"
                                            value="{{position}}"
                                            ng-model="employee.position"
                                            ng-required="true">
                                        <span class="help-inline"
                                            ng-show="frmEmployees.position.$invalid
                                            && frmEmployees.position.$touched">Position
                                            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="frmEmployees.$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/employees.js') ?>"></script>
    </body>
</html>

HERE,

  • <html lang="en-US" ng-app="employeeRecords"> attaches our AngularJS application employeeRecords to the html tag. This will give AngularJS control over all elements in html tag.
  • <div ng-controller="employeesController"> links the div to the employeesController. This will make available all of the functions under employeesController to the div element.
  • <tr ng-repeat="employee in employees"> uses the AngularJS directive ng-repeat to loop through the results of the collection variable employees. The directive ng-repeat is similar to the loop foreach.
  • The CSS styles and JavaScript files are loaded from CDN servers.

Run the following command to start the built-in server.

php artisan serve

Load the following URL in your web browser

http://localhost:8000/

You will get the following

Laravel AngularJS Records Listing

Click on Add New Employee button

You will get the following modal form

Laravel 6 AngularJS Insert Record

Click on Save changes

You will list the following list

AngularJS Laravel 6

Add more employees, edit existing record and even delete some

Use the comments section below if you get any errors. Our team will respond to you.

AngularJS form validation

AngularJS simplifies the process of validating forms.

Locate the code for the form and have a look at it

HERE,

  • <form name="frmEmployees" class="form-horizontal" novalidate=""> defines a form named frmEmployees and adds the novalidate attribute to stop HTML5 from validating our form
  • <input type... ng-model="employee.name" ng-required="true"> ng-model is used for data binding. For example, anything entered in name text box is made available to employee.name variable. When AngularJS changes the value of employee.name, it is made available to the textbox too. ng-required= "true" validates our form and checks if a value has been supplied. If no value is supplied, the a class of $invalid is added to our form
  • <span class="help-inline" ng-show="frmEmployees.name.$invalid && frmEmployees.name.$touched"> tells AngularJS that the name field is required. ng-show only displays this element if the name text box has an invalid class
  • ng-disabled="frmEmployees.$invalid" disables the submit button if the form has an invalid class. If the user enters all required details in the correct format, the submit button is enabled.

Summary

AngularJS is a powerful client-side MVC framework that simplifies developing frontend parts of a web applications. Subscribe to our free newsletter and we will let you know when we publish tutorial series on AngularJS.

What's next?

If you found this tutorial useful, use the social media buttons below to spread the word. If you didn't find it useful, use the comments section below to let us know how we can improve it. Thanks for your support in advance.


...