Node.js & Angular CRUD Tutorial – Step By Step Guide!


Previously, we learned how to create, read, update and delete database records with our AngularJS CRUD Tutorial. Today we will learn how to code CRUD with Angular 2.

Contents of this post include:

1.0 Prerequisite
2.0 Tutorial Overview
3.0 Output Preview
4.0 Setup The REST API

5.0 Angular Development Environment
5.1 Node.js
5.2 Node Package Manager (NPM)
5.3 Angular CLI
5.4 Angular Project

6.0 How To Read Records in Angular?
6.1 Generate “Read Products” Component
6.2 “Read Products” HTML Template
6.3 Create “Product” Service
6.4 Create Product Class
6.5 “Read Products” Component
6.6 AppComponent HTML Template
6.7 The App Component
6.8 Add Custom CSS
6.9 Output

7.0 How To Create or Insert Record in Angular?
7.1 Generate “Create Product” Component
7.2 “Create Product” HTML Template
7.3 The CreateProductComponent
7.4 Generate Category Service
7.5 Generate Category Class
7.6 Add createProduct() method in ProductService
7.7 Add ReactiveFormsModule
7.8 Change ReadProductsComponent
7.9 Change AppComponent HTML Template
7.10 Change AppComponent
7.11 Output

8.0 How To Read One Record in Angular?
8.1 Generate “Read One Product” Component
8.2 “Read One Product” HTML Template
8.3 Change ReadOneProductComponent
8.4 Add readOneProduct() method in Product Service
8.5 Change AppComponent HTML Template
8.6 Change AppComponent
8.7 Change ReadProductsComponent
8.8 Output

9.0 How To Update Record in Angular?
9.1 Generate “Update Product” Component
9.2 Build “Update Product” HTML Template
9.3 The UpdateProductComponent
9.4 Add updateProduct() method in ProductService
9.5 Change ReadProductsComponent
9.6 Change AppComponent HTML Template
9.7 Change AppComponent
9.8 Output

10.0 How To Delete Record in Angular?
10.1 Generate “Delete Product” Component
10.2 Build “Delete Product” HTML Template
10.3 The DeleteProductComponent
10.4 Add deleteProduct() method in ProductService
10.5 Change ReadProductsComponent
10.6 Change AppComponent HTML Template
10.7 Change AppComponent
10.8 Output

11.0 How To Run The Source Code?
12.0 Download LEVEL 1 Source Code
13.0 Download LEVEL 2 Source Code
14.0 Download ALL LEVELS

15.0 What’s Next?
16.0 Notes

1.0 Prerequisite

You must have some coding knowledge and experience with HTML, CSS and JavaScript because we will deal mostly with the client side of the web. PHP & MySQL or MariaDB for the REST API. And of course, Coffee (optional).

Angular is a framework for developing large JavaScript applications. It is used for building SPAs or Single-Page Applications.

Aside from the above definition, I require you to have at least a basic understanding of the following topics.

You don’t have to learn coding yet. For now, just understand what they are, why use them and who uses them. The links above should be helpful for you.

For additional learning about the topics above, you can always do your own Google search as well.

2.0 Tutorial Overview

If these platforms, frameworks, language and concepts are new to you, please do not get overwhelmed. Take one step at a time.

I’m going to show you exactly what you should learn next. You can ask me using the comments section below as well.

The key to learning here is to follow our tutorial, study, practice and ASK for help IF you really can’t understand something after you did your research.

You don’t have to learn all of it in one day. Downloading our source codes is your huge advantage as well.

In this post, we are going to learn how to coding using the following tools.

  • Angular / Angular2 – a development platform for building mobile and desktop web applications. Read more about this on section 2.0 of this post.
  • TypeScript – a superset of JavaScript. It is developed and maintained by Microsoft. For this tutorial, TypeScript will allow us to use classes in JavaScript.
  • Node.js – For this tutorial, this will allow us to run our app anywhere on our machine and install open source libraries easily.
  • REST API – To learn how to build a REST API using PHP & MySQL, see section 4.0 below. REST uses HTTP for all four CRUD (Create/Read/Update/Delete) operations.
  • Bootstrap – is in charge of making our app’s user interface look better.

I’m very excited to show you how to use the tools I mentioned above. Please continue below.

3.0 Output Preview

At the end of this tutorial, we will achieve the following output.

3.1 LEVEL 1 Source Code Output


3.2 LEVEL 2 Source Code Output


The LEVEL 2 source code output proves that you can add and customize more features. It’s easier and faster if you will learn by following our tutorial below.

Downloading our source codes is your huge advantage as well. For now, let’s proceed to the step by step tutorial of our LEVEL 1 source code. Enjoy!

4.0 Setup The REST API

In this tutorial, we are going to use a REST API built with PHP.

We did not include REST API source code because we want you to focus on learning how to code with Angular 2, not PHP.

But the good news is, we made a separate tutorial about how to build a simple REST API with PHP. Click here to learn the step-by-step PHP REST API tutorial.

I highly recommend learning our REST API tutorial first. This is because we are going to use that API for the rest of this tutorial.

But if you already have your own REST API that will work with this tutorial, that’s okay as well.

In my case, one example where I can access the REST API is: http://localhost/api/product/read.php

That link will show me the list of products from the database, in JSON format. It looks like the following screenshot.

The data above will be consumed by our Angular app. The list of products will be displayed in Bootstrap table with buttons like “Read One”, “Update” and “Delete”. You will see it in the “How To Read Records in Angular?” section of this tutorial.

By the way, I’m using a Chrome extension called JSONView to make the JSON data readable in the browser.

5.0 Angular Development Environment

First, we will set up the Angular development environment.

5.1 Node.js

1. Download & Install Node.js

  • Go to Node.js website and download the Node.js installer.
  • Run the installer and follow the installation process.

2. Open Your Command Prompt or Command Line. I’m using Windows, so I have:

3. Check Node.js Version by typing the following in the command prompt and pressing ‘Enter’ on the keyboard.

node --version 

If you’re using using Mac, I think you should always add ‘sudo’, so it will look like.

sudo node --version 

4. Update Node.js if your version is older than what’s on the Node.js homepage.

5.2 Node Package Manager (NPM)

1. How to Install NPM? NPM is bundled with node so you don’t have to do this.

2. Check NPM Version by typing the following in your command line.

npm --version

3. Update NPM because it gets updated more frequently than Node does, so you’ll want to make sure it’s the latest version.

If you’re version is older than what is in the NPM change log, update NPM using the following command.

npm install npm@latest -g

5.3 Angular CLI

1. Install Angular CLI, it usually takes several minutes. Type the following in your command line.

npm install -g angular-cli 

2. Check Angular CLI Version by typing the following in your command line.

ng --version 

3. Update Angular CLI if your version is older than what is in the Angular CLI change log.

This part assumes that you already created your project with Angular CLI. So, go to your project directory first. For example:

cd c:\users\ninjazhai\angular-crud-level-one

To update Angular CLI, you must update both the global package and your project’s local package.

Global package:

npm uninstall -g angular-cli @angular/cli
npm cache clean
npm install -g @angular/cli@latest

Local project package:

rm -rf node_modules dist # use rmdir on Windows
npm install --save-dev @angular/cli@latest
npm install
ng update

5.4 Angular Project

1. Create new Angular project with Angular CLI. This might take several minutes because it will create and install several files and packages.

Type the following in your command line and press enter.

ng new angular-crud-level-one

If you’re working on a LEVEL 2 source code, type the following instead.

ng new angular-crud-level-two

2. Go to your Angular project directory

cd angular-crud-level-one

3. Your command line should look like the following.

4. Run your project using “ng serve” in the command line. It will look like the following.

5. View the project in your browser. Use the following URL: http://localhost:4200/

6. If you will go to your project directory, in my case, its “C:\Users\ninjazhai\angular-crud-level-one” you will see the files and folders that looks like the following.

You don’t have to open or read each files and folders above because they are auto-generated by the Angular CLI. What you have to focus on are the steps we are taking in this tutorial.

7. To make our CRUD app look good, we are going to use Bootstrap. So, open “src” folder. Open “index.html” file and put the following Bootstrap CSS file before the closing “head” tag.

<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">

You can install Bootstrap via npm, but I prefer the method above.

8. Let’s verify if Bootstrap is working. Add a bootstrap button on page. Open “src” folder. Open “app” folder. Open “app.component.html” file. Put the following code.

<button class='btn btn-success'>click me</button>

9. Save the file. See your browser. The URL http://localhost:4200 is automatically reloaded. You should see something like the following.

6.0 How To Read Records in Angular?

Before we start with the coding, I need to give you some important pointers to have more success in following this tutorial.

In the next sections, I will put helpful links for you to have deeper understanding about the “modules” or “imports” we are using. I will place some helpful tips as well.

So, let’s start coding!

Using Angular 2, let’s start by reading records from the REST API. We will display the data on an HTML table.

6.1 Generate “Read Products” Component

Open another command prompt. Type the following and press enter.

ng g component read-products

To explain the above command:

  • ng – Angular CLI
  • g – generate
  • component – angular component
  • read-products – name of the component

It will generate the following folder and files inside the “app” folder.

├─ read-products/
├─── read-products.component.css
├─── read-products.component.html
├─── read-products.component.spec.ts
├─── read-products.component.ts

Learn more about what is an Angular component here.

TIP: If you need a component to be inside a parent component, you can type a command that looks like the following.

ng g component products/read-products

To explain the “products/read-products” above, “products” is the parent component, and “read-products” is the child component.

Here are some more examples. See the commands pointed by the arrows below.

6.2 “Read Products” HTML Template

On our “read products” page, we will have a “create product” button and HTML table that will hold our list of products from the API.

The HTML table will hold the “Read One”, “Update” and “Delete” buttons as well.

Under “/app/read-products” folder, open “read-products.component.html” file. Remove its current content and put the following code.

<div class="row m-b-18px">
    <div class="col-md-12">
        <!-- button to create new product -->
        <a (click)="createProduct()" class='btn btn-primary pull-right'>
        	<span class='glyphicon glyphicon-plus'></span> Create Product
        </a>
    </div>
</div>

<div class="row">
    <div class="col-md-12">

        <!-- HTML table for our list of product records -->
        <table class='table table-hover table-responsive table-bordered'>

            <tr>
                <th>Product</th>
                <th>Price</th>
                <th>Description</th>
                <th>Category</th>
                <th>Actions</th>
            </tr>

            <!-- Use *ngFor directive to loop throught our list of products. -->
            <tr *ngFor="let product of products">
                <td>{{product.name}}</td>
                <td>{{product.price}}</td>
                <td>{{product.description}}</td>
                <td>{{product.category_name}}</td>
                <td>
                    <!-- read one product button -->
                    <a (click)="readOneProduct(product.id)" class='btn btn-primary m-r-5px'>
                        <span class='glyphicon glyphicon-eye-open'></span> Read
                    </a>

                    <!-- edit product button -->
                    <a (click)="updateProduct(product.id)" class='btn btn-info m-r-5px'>
                        <span class='glyphicon glyphicon-edit'></span> Edit
                    </a>

                    <!-- delete product button -->
                    <a (click)="deleteProduct(product.id)" class='btn btn-danger m-r-5px'>
                        <span class='glyphicon glyphicon-remove'></span> Delete
                    </a>
                </td>
            </tr>
        </table>
    </div>
</div>

To explain some of the code above:

(click)="createProduct()"

The above code means that when the element that holds this attribute (a button or a link) was “clicked”, the createProduct() method in the component will be triggered.

This attribute is called event binding. Learn more about Angular event binding here.

<tr *ngFor="let product of products">

The above code is the “for loop” in Angular. Our code loops through the list of products from the API so that each record can be displayed as a table row.

The “*ngFor” is called a directive. It changed the appearance and behavior of our HTML table. Learn more about Angular Directives here.

6.3 Create “Product” Service

The “product” service is where we will put the code that allows as to contact the remote server or API. In this case, we are going to get the list for products.

Use the following in the command line to generate the “product” service.

ng g service product

You will see a new file inside the “app” folder. The file name is “product.service.ts”, open that file and put the following code.

import { Injectable } from '@angular/core';
import { Http, Response, Headers, RequestOptions } from '@angular/http';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/catch';
import { Product } from './product';

@Injectable()

// Service for products data.
export class ProductService {

    // We need Http to talk to a remote server.
    constructor(private _http : Http){ }

    // Get list of products from remote server.
    readProducts(): Observable<Product[]>{
        return this._http
            .get("http://localhost/api/product/read.php")
            .map(res => res.json());
    }

}

Our code above is an example of a Service. A service can be used by many components. You can think of it as a way to manage our data calls. Learn more about Angular Services here.

Our Services returns Observables. In this tutorial, we use Observables to handle multiple values. Learn more about Angular Observables here.

We used the HTTP Client as well to talk to a remote server. Learn more about Angular HTTP client here.

6.4 Create Product Class

The previous section’s code will not work without the “product” class. This is because we imported it as seen in the “imports” section.

We need the product class for the “create” and “update” features later. So, let’s create the “product” class using Angular CLI. Use the following command in your command line.

ng g class product

The above command will generate a new file under the “app” folder. The file name is “product.ts”, open that file and put the following code.

// Product class to define this object's properties.
export class Product {
    constructor(
        public id: number,
        public name: string,
        public price: number,
        public description: string,
        public category_id: number,
        public category_name: string
    ){}
}

6.5 “Read Products” Component

Under the “app” folder, open “read-products.component.ts”. Change the code to the following.

import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core';
import { ProductService } from '../product.service';
import { Observable } from 'rxjs/Observable';
import { Product } from '../product';

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

export class ReadProductsComponent implements OnInit {

    // store list of products
    products: Product[];

    // initialize productService to retrieve list products in the ngOnInit()
    constructor(private productService: ProductService){}

    // methods that we will use later
    createProduct(){}
    readOneProduct(id){}
    updateProduct(id){}
    deleteProduct(id){}

    // Read products from API.
    ngOnInit(){
        this.productService.readProducts()
            .subscribe(products =>
                this.products=products
            );
    }
}

6.6 AppComponent HTML Template

You can think of this as the “parent” HTML template because it will be the wrapper for smaller component which include the “ReadProductsComponent” and other component we will work on later.

Under the “app” folder, open “app.component.html” and change the code to the following.

<!-- container -->
<div class="container">

    <!-- show page header -->
    <div class="row">
        <div class="col-md-12">
            <div class='page-header'><h1>{{title}}</h1></div>
        </div>
    </div>

    <!-- Show this view if "show_read_products_html" property of AppComponent is true. -->
    <app-read-products
        *ngIf="show_read_products_html">
    </app-read-products>

</div>
<!-- /container -->

6.7 The App Component

In this tutorial, the “AppComponent” is the “parent” component of all our component. You can think of it as the “consumer” of our sub components like “ReadProductsComponent”.

Under the “app” folder, open “app.component.ts” file and change the code to the following.

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

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

export class AppComponent {
    // properties for child components
    title="Read Products";

    // properties used to identify what views to show
    show_read_products_html=true;
}

6.8 Add Custom CSS

We are going to fix some UI spacing using custom CSS. Under the “src” folder, open styles.css file and put the following CSS code.

/* You can add global styles to this file, and also import other style files */
.m-b-18px{ margin-bottom: 18px; }
.m-r-5px{ margin-right: 5px; }
.w-40-pct{ width: 40%; }
.text-align-center{ text-align: center; }

6.9 Output

After doing all the steps above, the output will look like the following.

7.0 How To Create or Insert Record in Angular?

7.1 Generate “Create Product” Component

Use the following command in the commmand line.

ng g component create-product

It will generate the following folder and files inside the “app” folder.

├─ create-product/
├─── create-product.component.css
├─── create-product.component.html
├─── create-product.component.spec.ts
├─── create-product.component.ts

7.2 “Create Product” HTML Template

We will build the HTML form where the user can enter product information. This HTML form contains:

  • Product input fields – name, price, description, category
  • Category drop-down list
  • Submit button – disabled if form is invalid
  • Validation messages as well – if not error message, submit button will be enabled
  • The “Read Products” button is used to go back to product list view.

Under “/app/create-product/” folder, open create-product.component.html and change the code to the following.

<div class="row m-b-18px">
	<div class="col-md-12">
		<a (click)="readProducts()" class='btn btn-primary pull-right'>
			<span class='glyphicon glyphicon-list'></span> Read Products
		</a>
	</div>
</div>

<!-- HTML form for creating a product -->
<div class="row">
<div class="col-md-12">

<!--
	1. Bind our HTML form to Angular using [formGroup]
	2. If form was submitted, call createProduct() method of CreateProductComponent
-->
<form [formGroup]="create_product_form" (ngSubmit)="createProduct()">
<table class="table table-hover table-responsive table-bordered">
	<tr>
		<td>Name</td>
		<td>
			<!-- Bind this input field to our 'angular form field' using formControlName -->
			<input
				formControlName="name"
				name="name" type="text"
				class="form-control" required />

			<!-- show this error if the field is 'empty' and 'touched' -->
			<div
				*ngIf="create_product_form.get('name').touched && create_product_form.get('name').hasError('required')"
				class="alert alert-danger">
				Name is required.
			</div>
		</td>
	</tr>

	<tr>
		<td>Price</td>
		<td>
			<input
				formControlName="price"
				type="number" name="price"
				class="form-control" required />

			<!-- show this error if the field is 'empty' and 'touched' -->
			<div
				*ngIf="create_product_form.get('price').touched && create_product_form.get('price').hasError('required')"
				class="alert alert-danger">
			Price is required.
			</div>
		</td>
	</tr>

	<tr>
		<td>Description</td>
		<td>
			<textarea
				formControlName="description"
				name="description" class="form-control" required></textarea>

				<!-- show this error if the field is 'empty' and 'touched' -->
				<div
					*ngIf="create_product_form.get('description').touched && create_product_form.get('description').hasError('required')"
					class="alert alert-danger">
					Description is required.
				</div>
		</td>
	</tr>

	<tr>
		<td>Category</td>
		<td>
			<select formControlName="category_id" name="category_id" class="form-control">
				<option *ngFor="let category of categories" value="{{category.id}}">
					{{category.name}}
				</option>
			</select>
			<!-- show this error if the field is 'empty' and 'touched' -->
			<div
				*ngIf="create_product_form.get('category_id').touched && create_product_form.get('category_id').hasError('required')"
				class="alert alert-danger">
				Category is required.
			</div>
		</td>
	</tr>

	<tr>
		<td></td>
		<td>
			<!-- disable the submit button if the form is 'invalid' -->
			<button class="btn btn-primary" type="submit" [disabled]="create_product_form.invalid">
				<span class="glyphicon glyphicon-plus"></span> Create
			</button>
		</td>
	</tr>
</table>
</form>

</div>
</div>

7.3 The CreateProductComponent

This component is coded based on what our HTML template contains. As you can see, this CreateProductComponent have the:

  • Angular form builder based on the HTML form fields.
  • CategoryService to read list of categories from the API to fill out the category drop-down. We will create this service later.
  • Category class because we are using the CategoryService.
  • ProductService to send form data to the API.
  • EventEmitter to go back to product list view.

Please read the code comments below to understand more of it. I will provide helpful links after the code as well.

import { Component, Input, Output, EventEmitter } from '@angular/core';
import { FormGroup, FormControl, Validators, FormBuilder } from '@angular/forms';
import { ProductService } from '../product.service';
import { CategoryService } from '../category.service';
import { Observable } from 'rxjs/Observable';
import { Product } from '../product';
import { Category } from '../category';

@Component({
    selector: 'app-create-product',
    templateUrl: './create-product.component.html',
    styleUrls: ['./create-product.component.css'],
    providers: [ProductService, CategoryService]
})

// component for creating a product record
export class CreateProductComponent {

    // our angular form
    create_product_form: FormGroup;

    // @Output will tell the parent component (AppComponent) that an event happened in this component
    @Output() show_read_products_event = new EventEmitter();

    // list of categories
    categories: Category[];

    // initialize 'product service', 'category service' and 'form builder'
    constructor(
        private productService: ProductService,
        private categoryService: CategoryService,
        formBuilder: FormBuilder
    ){
        // based on our html form, build our angular form
        this.create_product_form = formBuilder.group({
            name: ["", Validators.required],
            price: ["", Validators.required],
            description: ["", Validators.required],
            category_id: ["", Validators.required]
        });
    }

    // user clicks 'create' button
    createProduct(){

        // send data to server
        this.productService.createProduct(this.create_product_form.value)
            .subscribe(
                 product => {
                    // show an alert to tell the user if product was created or not
                    console.log(product);

                    // go back to list of products
                    this.readProducts();
                 },
                 error => console.log(error)
             );
    }

    // user clicks the 'read products' button
    readProducts(){
        this.show_read_products_event.emit({ title: "Read Products" });
    }

    // what to do when this component were initialized
    ngOnInit(){
        // read categories from database
        this.categoryService.readCategories()
            .subscribe(categories => this.categories=categories);
    }
}

7.4 Generate Category Service

Use the following in the command line.

ng g service category

It will generate a file under the “app” folder. The file name is “category.service.ts”, open that file and put the following code.

import { Injectable } from '@angular/core';
import { Http, Response } from '@angular/http';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/catch';
import { Category } from './category';

@Injectable()

// Service for categories data.
export class CategoryService {

    // We need Http to talk to a remote server.
    constructor(private _http: Http) { }

    // Get list of categories from database via api.
    readCategories(): Observable<Category[]>{
        return this._http
            .get("http://localhost/api/category/read.php")
            .map(res => res.json());
    }

}

7.5 Generate Category Class

Execute the following in the command line.

ng g class category

It will create a file under the “app” folder. The file name is “category.ts”, open that file and put the following code.

// Category class to define this object's properties.
export class Category {
    id: number;
    name: string;
    description: string;
}

7.6 Add createProduct() method in ProductService

Under the “app” folder, open “product.service.ts” file and put the following code after the readProducts() method.

// Send product data to remote server to create it.
createProduct(product): Observable<Product>{

    let headers = new Headers({ 'Content-Type': 'application/json' });
    let options = new RequestOptions({ headers: headers });

    return this._http.post(
        "http://localhost/api/product/create.php",
        product,
        options
    ).map(res => res.json());
}

7.7 Add ReactiveFormsModule

ReactiveFormsModule is needed for our Angular form to work. Under the “app” folder, open “app.module.ts” and find this line:

import { FormsModule } from '@angular/forms';

and change it to this:

import { FormsModule, ReactiveFormsModule } from '@angular/forms';

Add it in the “imports” as well, so find:

imports: [
    BrowserModule,
    FormsModule,
    HttpModule
],

and change it to this:

imports: [
    BrowserModule,
    FormsModule,
    HttpModule,
    ReactiveFormsModule
],

To learn more about ReactiveFormsModule, click here and here.

7.8 Change ReadProductsComponent

The “Create Product” form must be show when the “Create Product” button (located in ReadProductsComponent) was clicked.

Under “/app/read-products” folder, open “read-products.component.ts” file. Add the following output property before the “products” property.

/*
    * Needed to notify the 'consumer of this component', which is the 'AppComponent',
      that an 'event' happened in this component.
*/
@Output() show_create_product_event=new EventEmitter();

still in read-products.component.ts, find:

createProduct(){}

and change it to:

// when user clicks the 'create' button
createProduct(){
    // tell the parent component (AppComponent)
    this.show_create_product_event.emit({
        title: "Create Product"
    });
}

7.9 Change AppComponent HTML Template

Under “app” folder, open “app.component.html” and find:

<app-read-products
    *ngIf="show_read_products_html">

and change it to:

<app-read-products
    *ngIf="show_read_products_html"
    (show_create_product_event)="showCreateProduct($event)">

The “app-create-product” tag is the “selector” of our “CreateProductComponent”, so this tag will render our “Create Product” HTML form. Add the following code in “app.component.html” as well.

<!--
    * Show this view if "show_create_product_html" is true.

    * If (show_read_products_event) was emitted in CreateProductComponent,
      trigger showReadProducts() method in AppComponent.
-->
<app-create-product
    *ngIf="show_create_product_html"
    (show_read_products_event)="showReadProducts($event)">
</app-create-product>

7.10 Change AppComponent

We will change the AppComponent by adding the properties and methods required by the HTML template on the previous section.

Under “app” folder, open “app.component.html” file and find:

show_read_products_html=true;

and add the following properties after it:

show_create_product_html=false;
show_read_one_product_html=false;
show_update_product_html=false;
show_delete_product_html=false;

Add the following methods as well.

// show the 'create product form'
showCreateProduct($event){

    // set title
    this.title=$event.title;

    // hide all html then show only one html
    this.hideAll_Html();
    this.show_create_product_html=true;
}

// show products list
showReadProducts($event){
    // set title
    this.title=$event.title;

    // hide all html then show only one html
    this.hideAll_Html();
    this.show_read_products_html=true;
}

// hide all html views
hideAll_Html(){
    this.show_read_products_html=false;
    this.show_read_one_product_html=false;
    this.show_create_product_html=false;
    this.show_update_product_html=false;
    this.show_delete_product_html=false;
}

7.11 Output

The output of doing this section should look like the following.

8.0 How To Read One Record in Angular?

8.1 Generate “Read One Product” Component

Use the following in the commmand line.

ng g component create-product

It will generate the following folder and files inside the “app” folder.

├─ read-one-product/
├─── read-one-product.component.css
├─── read-one-product.component.html
├─── read-one-product.component.spec.ts
├─── read-one-product.component.ts

8.2 “Read One Product” HTML Template

When a user clicks on the “Read One” button, our app will show a product information in HTML table.

Under “/app/read-one-product/” folder, open read-one-product.component.html and change the code to the following.

<div class="row m-b-18px">
	<div class="col-md-12">
		<a (click)="readProducts()" class='btn btn-primary pull-right'>
			<span class='glyphicon glyphicon-list'></span> Read Products
		</a>
	</div>
</div>

<div class="row">
	<div class="col-md-12">
		<!-- HTML table for displaying a product details -->
		<table class='table table-hover table-responsive table-bordered'>

			<tr>
				<td class="w-40-pct">Name</td>
				<td>{{product?.name}}</td>
			</tr>

			<tr>
				<td>Price</td>
				<td>&#36;{{product?.price}}</td>
			</tr>

			<tr>
				<td>Description</td>
				<td>{{product?.description}}</td>
			</tr>

			<tr>
				<td>Category</td>
				<td>{{product?.category_name}}</td>
			</tr>

		</table>
	</div>
</div>

8.3 Change ReadOneProductComponent

Under “/app/read-one-product/” folder, open “read-one-product.component.ts” file. Change the code to the following.

import { Component, OnInit, Input, Output, OnChanges, EventEmitter } from '@angular/core';
import { ProductService } from '../product.service';
import { Observable } from 'rxjs/Observable';
import { Product } from '../product';

@Component({
    selector: 'app-read-one-product',
    templateUrl: './read-one-product.component.html',
    styleUrls: ['./read-one-product.component.css'],
    providers: [ProductService]
})

export class ReadOneProductComponent implements OnChanges {

    /*
        @Output will tell the parent component (AppComponent)
        that an event has happened (via .emit(), see readProducts() method below)
    */
    @Output() show_read_products_event = new EventEmitter();

    // @Input means it will accept value from parent component (AppComponent)
    @Input() product_id;

    product: Product;

    // initialize product service
    constructor(private productService: ProductService){}

    // user clicks the 'read products' button
    readProducts(){
        this.show_read_products_event.emit({ title: "Read Products" });
    }

    // call the record when 'product_id' was changed
    ngOnChanges(){
        this.productService.readOneProduct(this.product_id)
            .subscribe(product => this.product=product);
    }

}

8.4 Add readOneProduct() method in Product Service

Under the “app” folder, open “product.service.ts” file and put the following code after the createProduct() method.

// Get a product details from remote server.
readOneProduct(product_id): Observable<Product>{
    return this._http
        .get("http://localhost/api/product/read_one.php?id="+product_id)
        .map(res => res.json());
}

8.5 Change AppComponent HTML Template

Open app.component.html file and find:

<app-read-products
    *ngIf="show_read_products_html"
    (show_create_product_event)="showCreateProduct($event)">

and change it to:

<app-read-products
    *ngIf="show_read_products_html"
    (show_create_product_event)="showCreateProduct($event)"
    (show_read_one_product_event)="showReadOneProduct($event)">

The “app-read-one-product” tag is the “selector” of our “ReadOneProductComponent”. This tag will render our “Read One Product” HTML table with the product data.

Add the following code in “app.component.html” as well.

<!--
    * Show this view if "show_read_one_product_html" is true.

    * If (show_read_products_event) was emmited in ReadOneProductComponent,
      trigger showReadProducts() method in AppComponent.

    * Pass "product_id" property value of AppComponent
      to [product_id] property of ReadOneProductComponent.
-->
<app-read-one-product
    *ngIf="show_read_one_product_html"
    (show_read_products_event)="showReadProducts($event)"
    [product_id]="product_id">
</app-read-one-product>

8.6 Change AppComponent

Open “app.component.html” file and add the following property under “title” property.

product_id;

add the following method.

// show details of a product
showReadOneProduct($event){

    // set title and product ID
    this.title=$event.title;
    this.product_id=$event.product_id;

    // hide all html then show only one html
    this.hideAll_Html();
    this.show_read_one_product_html=true;
}

8.7 Change ReadProductsComponent

Under “/app/read-products/” folder, open read-products.component.ts file and find:

@Output() show_create_product_event=new EventEmitter();

…then add the following properties below it.

@Output() show_read_one_product_event=new EventEmitter();
@Output() show_update_product_event=new EventEmitter();
@Output() show_delete_product_event=new EventEmitter();

Still in “read-products.component.ts” file, find:

readOneProduct(id){}

…then change it to:

// when user clicks the 'read' button
readOneProduct(id){
    console.log('rp comp readOneProduct');
    // tell the parent component (AppComponent)
    this.show_read_one_product_event.emit({
        product_id: id,
        title: "Read One Product"
    });
}

8.8 Output

When you click the “Read One” button, you should see something like the following.

9.0 How To Update Record in Angular?

9.1 Generate “Update Product” Component

Use the following in the commmand line.

ng g component update-product

It will generate the following folder and files inside the “app” folder.

├─ update-product/
├─── update-product.component.css
├─── update-product.component.html
├─── update-product.component.spec.ts
├─── update-product.component.ts

9.2 Build “Update Product” HTML Template

We will build the HTML form where the user can update product information. This HTML form contains:

  • Product input fields – name, price, description, category
  • Category drop-down list
  • Submit button – disabled if form is invalid
  • Validation messages – if no error message, submit button will be enabled
  • The “Read Products” button is used to go back to product list view.

Under “/app/update-product/” folder, open “update-product.component.html” file and change the code to the following.

<div class="row m-b-18px">
	<div class="col-md-12">
		<a (click)="readProducts()" class='btn btn-primary pull-right'>
			<span class='glyphicon glyphicon-list'></span> Read Products
		</a>
	</div>
</div>

<div class="row">
<div class="col-md-12">

<!-- HTML form for creating a product -->
<form [formGroup]="update_product_form" (ngSubmit)="updateProduct()">
<table class='table table-hover table-responsive table-bordered'>

	<tr>
		<td>Name</td>
		<td>
			<input formControlName="name"
				type='text' name='name'
				class='form-control' required />

			<!-- show this error if the field is 'touched' and 'empty' -->
			<div
				*ngIf="update_product_form.get('name').touched && update_product_form.get('name').hasError('required')"
				class="alert alert-danger">
				Name is required.
			</div>
		</td>
	</tr>

	<tr>
		<td>Price</td>
		<td>
			<input
				formControlName="price"
				type='number' name='price'
				class='form-control' required />

			<!-- show this error if the field is 'touched' and 'empty' -->
			<div
				*ngIf="update_product_form.get('price').touched && update_product_form.get('price').hasError('required')"
				class="alert alert-danger">
			Price is required.
			</div>
		</td>
	</tr>

	<tr>
		<td>Description</td>
		<td>
			<textarea
				formControlName="description"
				name='description' class='form-control'></textarea>

			<!-- show this error if the field is 'touched' and 'empty' -->
			<div
				*ngIf="update_product_form.get('description').touched && update_product_form.get('description').hasError('required')"
				class="alert alert-danger">
			Description is required.
			</div>
		</td>
	</tr>

	<tr>
		<td>Category</td>
		<td>
			<select formControlName="category_id" name='category_id' class='form-control'>
				<option
					*ngFor="let category of categories"
					[selected]="category_id==category.id"
					value='{{category.id}}'>
					{{category.name}}
				</option>
			</select>

			<!-- show this error if the field is 'touched' and 'empty' -->
			<div
				*ngIf="update_product_form.get('category_id').touched && update_product_form.get('category_id').hasError('required')"
				class="alert alert-danger">
				Category is required.
			</div>
		</td>
	</tr>

	<tr>
		<td></td>
		<td>
			<!-- Submit buttons is [disabled] if form is invalid. -->
			<button class="btn btn-primary" type="submit" [disabled]="update_product_form.invalid">
				<span class="glyphicon glyphicon-edit"></span> Update
			</button>
		</td>
	</tr>

</table>
</form>

</div>
</div>

9.3 The UpdateProductComponent

This component is coded based on our HTML template above. As you will see, this UpdateProductComponent have the:

  • Angular form builder based on the HTML form fields.
  • CategoryService to read list of categories from the API to fill out the category drop-down.
  • Category class because we are using the CategoryService.
  • ProductService to send form data to the API.
  • EventEmitter to go back to product list view.
import { Component, OnChanges, Input, Output, EventEmitter } from '@angular/core';
import { FormGroup, FormControl, Validators, FormBuilder } from '@angular/forms';
import { ProductService } from '../product.service';
import { CategoryService } from '../category.service';
import { Observable } from 'rxjs/Observable';
import { Category } from '../category';

@Component({
    selector: 'app-update-product',
    templateUrl: './update-product.component.html',
    styleUrls: ['./update-product.component.css'],
    providers: [ProductService, CategoryService]
})
export class UpdateProductComponent implements OnChanges {

    // our angular form
    update_product_form: FormGroup;

    @Output() show_read_products_event = new EventEmitter();
    @Input() product_id;

    categories: Category[];

    // initialize product & category services
    constructor(
        private productService: ProductService,
        private categoryService: CategoryService,
        private formBuilder: FormBuilder
    ){

        // build angular form
        this.update_product_form = this.formBuilder.group({
            name: ["", Validators.required],
            price: ["", Validators.required],
            description: ["", Validators.required],
            category_id: ["", Validators.required]
        });
    }

    // user clicks 'create' button
    updateProduct(){

        // add product_id in the object so it can be updated
        this.update_product_form.value.id = this.product_id;

        // send data to server
        this.productService.updateProduct(this.update_product_form.value)
            .subscribe(
                 product => {
                    // go back to list of products
                    this.readProducts();
                 },
                 error => console.log(error)
             );
    }

    // user clicks the 'read products' button
    readProducts(){
        this.show_read_products_event.emit({ title: "Read Products" });
    }

    // call the record when 'product_id' was changed
    ngOnChanges(){

        // read one product record
        this.productService.readOneProduct(this.product_id)
            .subscribe(product => {

                // put values in the form
                this.update_product_form.patchValue({
                    name: product.name,
                    price: product.price,
                    description: product.description,
                    category_id: product.category_id
                });
            });
    }

    // read categories from database
    ngOnInit(){
        this.categoryService.readCategories()
            .subscribe(categories => this.categories=categories);
    }
}

9.4 Add updateProduct() method in ProductService

Under “app” folder, open “product.service.ts” file and add the following code after “readOneProduct()” method. The updateProduct() will post updated product information to API.

// Send product data to remote server to update it.
updateProduct(product): Observable<Product>{

    let headers = new Headers({ 'Content-Type': 'application/json' });
    let options = new RequestOptions({ headers: headers });

    return this._http.post(
        "http://localhost/api/product/update.php",
        product,
        options
    ).map(res => res.json());
}

9.5 Change ReadProductsComponent

The “Update Product” form must be show when the “Update” button (located in ReadProductsComponent) was clicked.

Under “/app/read-products/” folder, open “read-products.component.ts” file and find:

updateProduct(id){}

…then change the code to:

// when user clicks the 'update' button
updateProduct(id){
    // tell the parent component (AppComponent)
    this.show_update_product_event.emit({
        product_id: id,
        title: "Update Product"
    });
}

9.6 Change AppComponent HTML Template

We have to tell the parent component (AppComponent) to show the “Update Product” form on the event of “Update Product” button click.

Under “app” folder, open “app.component.html” and find “app-read-products” tag and add the following as attribute.

(show_update_product_event)="showUpdateProduct($event)"

Next, the “app-update-product” tag is the “selector” of our “UpdateProductComponent”, so this tag will render our “Update Product” HTML form. Add the following code in “app.component.html” as well.

<!--
    * Show this view if "show_update_product_html" is true.

    * Pass "product_id" property value of AppComponent
      to [product_id] property of UpdateProductComponent.

    * If (show_read_products_event) was emitted in UpdateProductComponent,
      trigger showReadProducts() method in AppComponent.
-->
<app-update-product
    *ngIf="show_update_product_html"
    [product_id]="product_id"
    (show_read_products_event)="showReadProducts($event)">
</app-update-product>

9.7 Change AppComponent

We used showUpdateProduct($event) method in the previous section. We have to add it on our AppComponent. Under “app” folder, open “app.component.html” file and add the following code inside the class.

// show the 'update product form'
showUpdateProduct($event){

    // set title and product ID
    this.title=$event.title;
    this.product_id=$event.product_id;

    // hide all html then show only one html
    this.hideAll_Html();
    this.show_update_product_html=true;
}

9.8 Output

When you click the “Update” button, you should see something like the following.

10.0 How To Delete Record in Angular?

10.1 Generate “Delete Product” Component

Use the following in the commmand line.

ng g component delete-product

It will generate the following folder and files inside the “app” folder.

├─ delete-product/
├─── delete-product.component.css
├─── delete-product.component.html
├─── delete-product.component.spec.ts
├─── delete-product.component.ts

At this moment, your command prompt should look like the following.

10.2 Build “Delete Product” HTML Template

Our “Delete Product” HTML template will contain the “Are you sure?” message with “Yes” and “No” button.

Under “/app/delete-product/” folder, open “delete-product.component.html” and change the code to the following.

<div class="row">
    <div class="col-md-3"></div>

    <div class="col-md-6">
        <div class="panel panel-default">
            <div class="panel-body text-align-center">Are you sure?</div>
            <div class="panel-footer clearfix">
                <div class="text-align-center">

                    <!-- if 'Yes' button was clicked, delete the record and go back to records list -->
                    <button (click)="deleteProduct()" class="btn btn-danger m-r-5px">
                        <span class="glyphicon glyphicon-ok-sign"></span> Yes
                    </button>

                    <!-- if 'No' button was clicked, go back to records list -->
                    <button (click)="readProducts()" class="btn btn-primary">
                        <span class="glyphicon glyphicon-remove-sign"></span> No
                    </button>
                </div>
            </div>
        </div>
    </div>

    <div class="col-md-3"></div>
</div>

10.3 The DeleteProductComponent

When user click the “Yes” button, our app will send the product ID to the API for deletion. If the user click the “No” button, our app will show the list of products.

Under “/app/delete-product/” folder, open “delete-product.component.html” and change the code to the following.

import { Component, Input, Output, EventEmitter } from '@angular/core';
import { ProductService } from '../product.service';
import { Observable } from 'rxjs/Observable';
import { Product } from '../product';

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

export class DeleteProductComponent {

    /*
        @Output will tell the parent component (AppComponent)
        that an event has happened (via .emit(), see readProducts() method below)
    */
    @Output() show_read_products_event = new EventEmitter();

    // @Input enable getting the product_id from parent component (AppComponent)
    @Input() product_id;

    // initialize product service
    constructor(private productService: ProductService){}

    // user clicks 'yes' button
    deleteProduct(){

        // delete data from database
        this.productService.deleteProduct(this.product_id)
            .subscribe(
                 product => {

                    // show an alert to tell the user if product was created or not
                    console.log(product);

                    // go back to list of products
                    this.readProducts();
                 },
                 error => console.log(error)
             );
    }

    // user clicks the 'read products' button
    readProducts(){
        this.show_read_products_event.emit({ title: "Read Products" });
    }

}

10.4 Add deleteProduct() method in ProductService

Using the “ProductService”, we are going to send an HTTP request to delete a product. A product record will be deleted based on given product_id parameter.

Under “app” folder, open “product.service.ts” file and add the following method inside the class.

// Send product ID to remote server to delete it.
deleteProduct(product_id){

    let headers = new Headers({ 'Content-Type': 'application/json' });
    let options = new RequestOptions({ headers: headers });

    return this._http.post(
        "http://localhost/api/product/delete.php",
        { id: product_id },
        options
    ).map(res => res.json());
}

10.5 Change ReadProductsComponent

The “Delete Product” prompt (Are you sure? delete confirmation message) must be shown when the “Delete” button (located in ReadProductsComponent) was clicked.

Under “/app/read-products/” folder, open “read-products.component.ts” file and find:

deleteProduct(id){}

…then change the code to:

// when user clicks the 'delete' button
deleteProduct(id){
    // tell the parent component (AppComponent)
    this.show_delete_product_event.emit({
        product_id: id,
        title: "Delete Product"
    });
}

10.6 Change AppComponent HTML Template

Under “app” folder, open “app.component.html” and find the “app-read-products” tag. Add the following attribute to it.

(show_delete_product_event)="showDeleteProduct($event)">

The “app-delete-product” tag is the “selector” of our “DeleteProductComponent”, so this tag will render our “Delete Product” confirmation prompt with “Yes” and “No” button. Add the following code in “app.component.html” as well.

<!--
    * Show this view if "show_delete_product_html" is true.

    * Pass "product_id" property value of AppComponent
      to [product_id] property of UpdateProductComponent.

    * If (show_read_products_event) was emitted in DeleteProductComponent,
      trigger showReadProducts() method in AppComponent.
-->
<app-delete-product
    *ngIf="show_delete_product_html"
    [product_id]="product_id"
    (show_read_products_event)="showReadProducts($event)">
</app-delete-product>

10.7 Change AppComponent

We used the showDeleteProduct($event) method on the previous section so we need to add it in our AppComponent.

Under “app” folder, open “app.component.ts” and add the following method inside the class.

// show 'are you sure?' prompt to confirm deletion of a record
showDeleteProduct($event){

    // set title and product ID
    this.title=$event.title;
    this.product_id=$event.product_id;

    // hide all html then show only one html
    this.hideAll_Html();
    this.show_delete_product_html=true;
}

10.8 Output

When you click the “Delete” button, you should see something like the following.

11.0 How To Run The Source Code?

We highly recommend for you to follow and study our well-detailed, step-by-step tutorial above first. Nothing beats experience when it comes to learning.

But we believe you will learn faster if you’ll see the final source code as well. We consider it as your additional guide.

Imagine the value or skill upgrade it can bring you. The additional income you can get from your work, projects or business. The precious time you save. Isn’t that what you want?

By now, you need to download our source codes. To do it, use any download buttons in the next few sections below.

Once you downloaded the source codes, here’s how you can run it.

  1. Extract the ZIP file you downloaded.
  2. Copy the “api” folder to root directory of your localhost.
  3. Setup the database using PhpMyAdmin. Import “api_db.sql” which is inside the “api” folder.
  4. Set up your Angular development environment. Follow section 5.0 above.
  5. Go to your development directory, open the “src” folder.
  6. Copy the “app” folder and “styles.css” file to the “src” folder of your development directory
  7. Open your command prompt. Go to your project directory. Run: ng serve
  8. Open your browser and go to http://localhost:4200/

12.0 Download LEVEL 1 Source Code

FEATURESLEVEL 1
Create productYES
Read productYES
Read one productYES
Update productYES
Delete productYES
Bootstrap UIYES
PHP REST API source codeYES
FREE email support for 3 monthsYES
Source code updates via emailYES
LEVEL 1: BUY AND DOWNLOAD NOW USING
* You can use your debit or credit card with PayPal.

13.0 Download LEVEL 2 Source Code

FEATURESLEVEL 2
All features of LEVEL 1YES
Create categoryYES
Read categoryYES
Read one categoryYES
Update categoryYES
Delete categoryYES
Navigation barYES
FREE email support for 6 monthsYES
LEVEL 2: BUY AND DOWNLOAD NOW USING
* You can use your debit or credit card with PayPal.

14.0 Download ALL LEVELS

DOWNLOAD ALL PACKAGES ABOVEALL
Level 1 source code packageYES
Level 2 source code packageYES
ALL LEVELS: BUY AND DOWNLOAD NOW USING
* You can use your debit or credit card with PayPal.

Do you need more reasons to get it?

MORE REASONS TO DOWNLOAD THE CODEALL
Use new skills for your multiple projectsYES
Save huge amount of time learning Angular / Angular 2YES
Code examples are direct to the pointYES
Well explained and commented source codeYES
Fast and friendly email supportYES
Free source code updatesYES

15.0 What’s Next?

We are currently building more high quality tutorials for PHP, Laravel, CakePHP, and Social Media APIs. We love publishing high-quality tutorials for you guys!

Please subscribe via email so that we can let you know immediately once these new and high-quality tutorials are published.

CLICK HERE TO SUBSCRIBE!

16.0 Notes

#1 Found An Issue?

If you found a problem with this code, we can solve it faster via Email or FB message, please send me a message via email mike@codeofaninja.com, or via our official Facebook page!

Please be more detailed about your issue. Best if you can provide an error message and your test or page URL. Thanks!

Please feel free to comment if you have any questions, suggestions, found something wrong or want to contribute to this code.

#2 Become a true Ninja!

We constantly add new tutorials and improve our existing tutorials and source codes. Be one of the first to know an update by subscribing to our FREE newsletter. Get a FREE EBOOK as well. CLICK HERE TO SUBSCRIBE FOR FREE!

#3 Thank You!

If you have a friend or know someone who needs this Angular CRUD Tutorial, please share this page to them! I know you will help them a lot by doing it. Thanks!

React CRUD Tutorial – JavaScript Programming – Step By Step Guide!


Previously, we learned our jQuery AJAX CRUD tutorial. Today we will learn how to create or insert, read, update, and delete database records with React.

Welcome to the fifth part of our CRUD tutorial series! I’m really happy that you’re here. I appreciate your time and support on our website!

I highly recommend studying the previous tutorials first before proceeding here. But if you think you can take this one, then go on.

Our tutorial for today will cover the following contents:

1.0 Program Output
2.0 React: What, Why, Who and Think
3.0 Technologies Used In This Tutorial
4.0 File Structure

5.0 Prepare The Database
5.1 Create the Database
5.2 Create the Database Table
5.3 Dump Sample Data On The Table
5.4 Create Database Connection PHP File

6.0 Basic File Requirements
6.1 Create core.php
6.2 Download Bootstrap
6.3 Download jQuery
6.4 Create layout_head.php
6.5 Create layout_foot.php
6.6 Create index.php
6.7 Output

7.0 Create Record with React
7.1 Initialize Virtual DOM
7.2 Create MainApp Component
7.3 Create Product Component
7.4 Prepare Component State
7.5 Request List Of Categories
7.6 Create read_all_categories.php
7.7 Object Method To Read All Categories
7.8 Stop Category List Request
7.9 Handle State Changes
7.10 Handle Form Submit
7.11 Create create_product.php
7.12 Create Product Class
7.13 HTML Form To Create Product
7.14 Output

8.0 Read Records with React
8.1 MainApp Initial State
8.2 Put Custom changeAppMode Method
8.3 Add App Mode
8.4 Add ReadProductsComponent
8.5 Create read_all_products.php
8.6 Object Code To Real All Products
8.7 Add TopActionsComponent
8.8 Add ProductsTable Component
8.9 Add ProductRow Component
8.10 Output

9.0 Read One Record with React
9.1 Change MainApp Render Method
9.2 Add ReadOneProductComponent
9.3 PHP Script To Read One Product
9.4 Object Method To Read One Product
9.5 Output

10.0 Update Record with React
10.1 Change MainApp Render Method
10.2 Add UpdateProductComponent
10.3 Prepare Component State
10.4 Prepare componentDidMount Method
10.5 Prepare componentWillUnmount Method
10.6 Handle State Changes
10.7 Handle Form Submit
10.8 PHP Script To Update Product
10.9 Object Method To Update Product
10.10 Update Product Form
10.11 Output

11.0 Delete Record with React
11.1 Change MainApp Render Method
11.2 Add DeleteProductComponent
11.3 Create delete_product.php
11.4 Object Method To Delete A Product
11.5 Output

12.0 Download Source Codes
13.0 What’s Next?
14.0 Related Source Codes
15.0 Online Resources
16.0 Some Notes

1.0 Program Output – React CRUD Tutorial

We usually have three LEVELS of source code output. But WHY? Because I believe in "Learning Progression" to ensure efficient learning. Learn more

1.1 LEVEL 1 Source Code Output

1.2 LEVEL 2 Source Code Output

2.0 React: What, Why, Who and Think

2.1 What is React?

React is a JavaScript library for building user interfaces. It is maintained by Facebook, Instagram and a community of individual developers and corporations.

2.2 Why use React?

Using React allows you to simply express how your app should look at any given point in time. React will automatically manage all UI updates when your underlying data changes. When the data changes, React conceptually hits the “refresh” button, and knows to only update the changed parts.

2.3 Who uses React?

Aside from Facebook and Instagram, many well-known companies uses React including: Dropbox, PayPal, Nextflix, Khan Academy, Airbnb, Alipay, Asana and many more.

2.4 Thinking in React

It is very important that we understand the thought process of building web apps with React. Here’s a start: We start with a mock. Then, we break the UI into a component or parts of the UI. In the following image, you can see the mock and components in colored boxes.

thinking-in-react-components

I will not discuss this topic longer here. To learn more about how to think in React, click here.

Another point: Before, we code HTML and JavaScript separately on a web page. In React, we code HTML inside JavaScript. This is made possible through JSX. To learn more about JSX, click here.

React concepts can be hard to grasp at first, but you will get there. If you do actual coding with React, you will understand it better and faster. This is why our step-by-step React CRUD tutorial below will be very useful.

3.0 Technologies Used In This Tutorial

Here we will learn how to create, read update and delete database records – CRUD operations with PHP, MySQL, React, Babel, jQuery and Bootstrap.

Why do I use these technologies for this React CRUD tutorial? Here are my reasons:

  1. They work.
  2. Most audience of our blog are very familiar with these technologies.
  3. The goal of this tutorial is for you to learn how to use React.
  4. React is only the V in MVC.
  5. React don’t make assumptions about the rest of your technology stack.

Now, let’s take a look at the roles of each technology that we will use:

  • PHP – will handle server side script.
  • MySQL – will store our data.
  • React – will make our UI fast and interactive.
  • Babel – will compile our JavaScript so we don’t have to wait for browser support.
  • jQuery – will do AJAX requests. React official docs shows jQuery examples.
  • Bootstrap – will make our UI look better.

For those who ask, React can work with AngularJS. In short, it can work with the MEAN (mongoDB, Express, AngularJS and NodeJS) framework. We will have another tutorial for that soon! Please subscribe so I can notify you when it is done.

4.0 File Structure

It is useful to see the final file structure of this React CRUD tutorial. This way, we can easily visualize where we are going. You do not need to memorize all the files or folders. You just have to understand them.

Folders and files are in bold characters. I put a small description of each files and folders. Once we complete the tutorial, we will have the file structure that looks like the following.

├─api/ – contains PHP files that works with the server.
├───create_product.php – will insert data to MySQL database.
├───delete_products.php – will delete data from MySQL database.
├───read_all_categories.php – will select categories from MySQL database.
├───read_all_products.php – will select products from MySQL database.
├───read_one_product.php – will select one product from MySQL database.
├───update_product.php – will update a product from MySQL database.
├─config/ – contains PHP files for settings.
├───core.php – here we can set error reporting and timezone.
├───database.php – will connect us to the MySQL database.
├─dev/ – contains SQL file and notes for developers.
├───db.sql – import this file to your MySQL database.
├───readme.txt – important notes for developer.
├─libs/ – contains useful libraries for our app.
├───css/ – contains libraries related to styling.
├─────bootstrap/ – makes our app look good. [Learn more]
├───js/ – contains libraries related to JavaScript.
├─────react/ – contains our React script.
├───────main.js – our React script can be edited in this file.
├─────jquery.js – library that will help us with AJAX requests.
├─objects/ – contains PHP libraries or objects
├─────product.php – object related to products database table
├─────category.php – object related to categories database table
├─index.php – is where our single page app (SPA) will load.
├─info.php – for debugging purposes.
├─layout_foot.php – contains HTML and JS on the lower part of index.php
├─layout_head.php – contains HTML and CSS on the upper part of index.php

5.0 Prepare The Database

I assume you are using PhpMyAdmin to manage the database. If not, contact me.

5.1 Create the Database

First, create a database with a name “php_react_crud”.

5.2 Create the Database Tables

Run the following SQL to create the products and categories table.

-- Table structure for table `products`
CREATE TABLE IF NOT EXISTS `products` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(128) NOT NULL,
  `description` text NOT NULL,
  `price` double NOT NULL,
  `category_id` int(11) NOT NULL,
  `created` datetime NOT NULL,
  `modified` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB  DEFAULT CHARSET=latin1 AUTO_INCREMENT=35 ;

-- Table structure for table `categories`
CREATE TABLE IF NOT EXISTS `categories` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(128) NOT NULL,
  `description` text NOT NULL,
  `created` datetime NOT NULL,
  `modified` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB  DEFAULT CHARSET=latin1 AUTO_INCREMENT=3 ;

5.3 Dump Sample Data On The Table

Use the following SQL query to insert sample data.

-- Dumping data for table `products`
INSERT INTO `products` (`id`, `name`, `description`, `price`, `category_id`, `created`, `modified`) VALUES
(1, 'Basketball', 'A ball used in the NBA.', 49.99, 1, '2015-08-02 12:04:03', '2015-08-02 15:57:13'),
(3, 'Gatorade', 'This is a very good drink for athletes.', 1.99, 1, '2015-08-02 12:14:29', '2015-08-02 15:57:13'),
(4, 'Eye Glasses', 'It will make you read better.', 6, 1, '2015-08-02 12:15:04', '2015-08-02 15:57:13'),
(5, 'Trash Can', 'It will help you maintain cleanliness.', 3.95, 1, '2015-08-02 12:16:08', '2015-08-02 15:57:13'),
(6, 'Mouse', 'Very useful if you love your computer.', 11.35, 2, '2015-08-02 12:17:58', '2015-08-02 15:57:38'),
(7, 'Earphone', 'You need this one if you love music.', 9, 2, '2015-08-02 12:18:21', '2016-07-20 14:56:22'),
(8, 'Pillow', 'Sleeping well is important.', 8.99, 2, '2015-08-02 12:18:56', '2015-08-02 15:57:38'),
(13, 'Cellphone Stand', 'Very useful if you are a developer.', 5.55, 2, '2015-08-03 08:00:16', '2016-07-20 14:56:17');

-- Dumping data for table `categories`
INSERT INTO `categories` (`id`, `name`, `description`, `created`, `modified`) VALUES
(1, 'Sports', 'Products you use for sports.', '2015-08-02 23:56:46', '2015-08-03 05:59:36'),
(2, 'Personal', 'Products for you personal needs.', '2015-08-02 23:56:46', '2015-08-03 05:59:36');

5.4 Create Database Connection PHP File

Create the “config” folder and “database.php” file inside that folder. The database.php file will have the following code:

<?php
class Database{

	// specify your own database credentials
	private $host = "localhost";
	private $db_name = "php_react_crud";
	private $username = "root";
	private $password = "";
	public $conn;

	// get the database connection
	public function getConnection(){

		$this->conn = null;

		try{
			$this->conn = new PDO("mysql:host=" . $this->host . ";dbname=" . $this->db_name, $this->username, $this->password);
		}catch(PDOException $exception){
			echo "Connection error: " . $exception->getMessage();
		}

		return $this->conn;
	}
}
?>

Good job preparing the database for this React CRUD tutorial! Let’s achieve more output by proceeding to the next sections below.

6.0 Basic File Requirements

6.1 Create core.php

Create core.php file inside the config folder. This file contains other app settings such as error reporting or timezone settings.

This is not required but this is a good thing to practice. Put the following code inside it.

<?php
// show error reporting
error_reporting(E_ALL);

// set your default time-zone
date_default_timezone_set('Asia/Manila');
?>

6.2 Download Bootstrap

We use Bootstrap to make our user interface look good. If you’re not yet familiar with it, please learn from our Bootstrap tutorial for beginners

Put the bootstrap files inside “libs/css/” directory.

6.3 Download jQuery

We use jQuery for AJAX requests. If you’re not yet familiar with it, please learn from our jQuery tutorial for beginners

Put the jQuery library file inside “libs/js/” directory.

6.4 Create layout_head.php

Create the layout_head.php file. This file will hold our page’s header HTML and CSS. Put the following code inside it.

<!DOCTYPE html>
<html lang="en">
<head>

    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <title>PHP CRUD with ReactJS</title>

    <!-- Bootstrap CSS -->
	<link href="libs/css/bootstrap/dist/css/bootstrap.css" rel="stylesheet" media="screen">

    <!-- HTML5 Shiv and Respond.js IE8 support of HTML5 elements and media queries -->
    <!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
    <!--[if lt IE 9]>
    <script src="https://oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script>
    <script src="https://oss.maxcdn.com/libs/respond.js/1.4.2/respond.min.js"></script>
    <![endif]-->

	<style>
	.text-align-center{
		text-align:center;
	}

	.margin-zero{
		margin:0;
	}

	.overflow-hidden{
		overflow:hidden;
	}

	.margin-bottom-1em{
		margin-bottom:1em;
	}

	.m-r-1em{
		margin-right:1em;
	}
	</style>

</head>
<body>

    <!-- container -->
    <div class="container">

        <div class="page-header">
            <h1>Loading...</h1>
        </div>

6.5 Create layout_foot.php

This time, create the layout_foot.php file. This file will hold our page footer’s HTML and JavaScript. Put the following code inside it.

	</div>
	<!-- /container -->

<!-- react js -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-core/5.8.34/browser.min.js"></script>

<!-- main react components -->
<script type="text/babel" src="libs/js/react/main.js"></script>

<!-- jQuery library -->
<script src="libs/js/jquery.js"></script>

<!-- bootstrap JavaScript -->
<script src="libs/css/bootstrap/dist/js/bootstrap.min.js"></script>
<script src="libs/css/bootstrap/docs-assets/js/holder.js"></script>

</body>
</html>

As you may have noticed, we used the CloudFlare CDN to load React and Babel in our project. This will save us some extra steps.

6.6 Create index.php

Create index.php file with the following code. The index.php is where our app will load.

<?php
// include configuration file
include 'config/core.php';

// include the head template
include_once "layout_head.php";

// placeholder for rendering React components
echo "<div id='content'></div>";

// page footer
include_once "layout_foot.php";
?>

6.7 Output

Here’s the output of all we did in section 6 of this React CRUD tutorial.
level-1-index-page

It is almost like a blank page. But don’t worry! We will add awesome features, let’s continue to code by proceeding to the next sections.

7.0 Create Record with React

7.1 Initialize Virtual DOM

Create main.js file and put it inside “libs/js/react/” directory. This file will hold all our React scripts. Put the following code inside main.js, this will initialize our React DOM, also called Virtual DOM.

ReactDOM.render(
    <MainApp />,
    document.getElementById('content')
);

7.2 Create MainApp Component

As you can see in the previous section, the MainApp tag was called. Now, we will create a React component class for that. Put the following code above the previous section’s code.

var MainApp = React.createClass({
    render: function() {
        return <CreateProductComponent />;
    }
});

7.3 Create Product Component

The CreateProductComponent was returned in the previous section’s code. This component will contain the logic to create a new record. Put the following code above the previous section’s code.

var CreateProductComponent = React.createClass({

});

7.4 Prepare Component State

State is reserved only for interactivity or data that changes overtime. State is invoked once before the component is mounted.

On our case, we’ll have the product categories, fields and display data. Put the following code inside the createClass function of the previous section.

getInitialState: function() {
    return {
        categories: [],
        selectedCategoryId: -1,
        name: '',
        description: '',
        price: '',
        successCreation: null
    };
},

7.5 Request List Of Categories

The componentDidMount method is invoked once immediately after the initial rendering occurs. This is where we usually perform AJAX requests. In our case, we will get the list of categories from the database using a jQuery AJAX request.

We change the page title to ‘Create Product’ as well. Put the following code right after the getInitialState method of the previous section.

componentDidMount: function() {
    this.serverRequest = $.get("api/read_all_categories.php", function (categories) {
        this.setState({
            categories: JSON.parse(categories)
        });
    }.bind(this));

    $('.page-header h1').text('Create product');
},

7.6 Create read_all_categories.php

The previous section won’t work without the read_all_categories.php file. Create read_all_categories.php file inside the “api” folder and put the following code inside it.

<?php
// include core configuration
include_once '../config/core.php';

// include database connection
include_once '../config/database.php';

// product object
include_once '../objects/category.php';

// class instance
$database = new Database();
$db = $database->getConnection();
$category = new Category($db);

// read all products
$results=$category->readAll();

// output in json format
echo $results;
?>

7.7 Object Method To Read All Categories

The previous section will not work without the readAll() object method. Create “category.php” inside the “objects” folder and put the following code.

<?php
class Category{

	// database connection and table name
	private $conn;
	private $table_name = "categories";

	// object properties
	public $id;
	public $name;

	public function __construct($db){
		$this->conn = $db;
	}

	public function readAll(){

		//select all data
		$query = "SELECT id, name FROM categories ORDER BY name";

		$stmt = $this->conn->prepare($query);
		$stmt->execute();

		$categories=$stmt->fetchAll(PDO::FETCH_ASSOC);

		return json_encode($categories);
	}

}
?>

7.8 Stop Category List Request

The componentWillUnmount method is invoked immediately before a component is unmounted from the DOM. This is where we perform any necessary cleanup, such as invalidating timers or cleaning up any DOM elements that were created in componentDidMount.

In our case, we use this method to stop getting categories data – in case the request is still loading. This is a very useful feature to make our app run smoothly. Put the following code under the code in section 7.5 above.

componentWillUnmount: function() {
    this.serverRequest.abort();
},

7.9 Handle State Changes

This is how we handle changes in our state. When state changes, it means there is a change in our form input as well. We will create custom methods bound to each fields in our form. They are like ‘on change’ listeners. We will see how this is integrated with our form later. For now, put the following codes after the code on the previous section.

// handle category change
onCategoryChange: function(e) {
    this.setState({selectedCategoryId: e.target.value});
},

// handle name change
onNameChange: function(e) {
    this.setState({name: e.target.value});
},

// handle description change
onDescriptionChange: function(e) {
    this.setState({description: e.target.value});
},

// handle price change
onPriceChange: function(e) {
    this.setState({price: e.target.value});
},

7.10 Handle Form Submit

Here’s our custom method that is executed once the ‘create product’ form was submitted. We use jQuery AJAX to post the data to create_product.php file. We empty the fields once the response returned is ‘true’. Put the following codes after the previous section’s code.

onSave: function(e){
    $.post("api/create_product.php", {
            name: this.state.name,
            description: this.state.description,
            price: this.state.price,
            category_id: this.state.selectedCategoryId
        },
        function(res){
            this.setState({successCreation: res});
            this.setState({name: ""});
            this.setState({description: ""});
            this.setState({price: ""});
            this.setState({selectedCategoryId: -1});
        }.bind(this)
    );
    e.preventDefault();
},

7.11 Create create_product.php

Create create_product.php inside the “api” folder. This PHP script will accept the posted data from the previous section. Put the following code inside create_product.php

<?php
// if the form was submitted
if($_POST){

	// include core configuration
	include_once '../config/core.php';

	// include database connection
	include_once '../config/database.php';

	// product object
	include_once '../objects/product.php';

	// class instance
	$database = new Database();
	$db = $database->getConnection();
	$product = new Product($db);

	// set product property values
	$product->name = $_POST['name'];
	$product->price = $_POST['price'];
	$product->description = $_POST['description'];
	$product->category_id = $_POST['category_id'];

	// create the product
	echo $product->create() ? "true" : "false";
}
?>

7.12 Create Product Class

The previous section will not work without the product.php file. This file holds our product object class where we can call useful methods to perform tasks us create, read, update and delete products.

Create the “objects” folder and create the “product.php” file inside it. Open product.php and put the following code.

<?php
class Product{

	// database connection and table name
	private $conn;
	private $table_name = "products";

	// object properties
	public $id;
	public $name;
	public $price;
	public $description;
	public $category_id;
	public $timestamp;

	public function __construct($db){
		$this->conn = $db;
	}

    public function create(){
        try{

            // insert query
            $query = "INSERT INTO products
                SET name=:name, description=:description, price=:price, category_id=:category_id, created=:created";

            // prepare query for execution
            $stmt = $this->conn->prepare($query);

            // sanitize
            $name=htmlspecialchars(strip_tags($this->name));
            $description=htmlspecialchars(strip_tags($this->description));
            $price=htmlspecialchars(strip_tags($this->price));
            $category_id=htmlspecialchars(strip_tags($this->category_id));

            // bind the parameters
            $stmt->bindParam(':name', $name);
            $stmt->bindParam(':description', $description);
            $stmt->bindParam(':price', $price);
            $stmt->bindParam(':category_id', $category_id);

            // we need the created variable to know when the record was created
            // also, to comply with strict standards: only variables should be passed by reference
            $created=date('Y-m-d H:i:s');
            $stmt->bindParam(':created', $created);

            // Execute the query
            if($stmt->execute()){
                return true;
            }else{
                return false;
            }

        }

        // show error if any
        catch(PDOException $exception){
            die('ERROR: ' . $exception->getMessage());
        }
    }
}

7.13 HTML Form To Create Product

The following render method code does the following.

  • Arrange ‘options’ for our categories drop down. (related to section 7.5)
  • Tell the user if a product was created.
  • Tell the user if unable to create product.
  • Button to go back to products list.
  • Form to create a product.
  • Binds each field to custom onChange methods. (related to section 7.6)

Put the following code under section 7.10’s code.

render: function() {

    // make categories as option for the select tag.
    var categoriesOptions = this.state.categories.map(function(category){
        return (
            <option key={category.id} value={category.id}>{category.name}</option>
        );
    });

    /*
    - tell the user if a product was created
    - tell the user if unable to create product
    - button to go back to products list
    - form to create a product
    */
    return (
    <div>
        {

            this.state.successCreation == "true" ?
                <div className='alert alert-success'>
                    Product was saved.
                </div>
            : null
        }

        {

            this.state.successCreation == "false" ?
                <div className='alert alert-danger'>
                    Unable to save product. Please try again.
                </div>
            : null
        }

        <a href='#'
            onClick={() => this.props.changeAppMode('read')}
            className='btn btn-primary margin-bottom-1em'> Read Products
        </a>


        <form onSubmit={this.onSave}>
            <table className='table table-bordered table-hover'>
            <tbody>
                <tr>
                    <td>Name</td>
                    <td>
                        <input
                        type='text'
                        className='form-control'
                        value={this.state.name}
                        required
                        onChange={this.onNameChange} />
                    </td>
                </tr>

                <tr>
                    <td>Description</td>
                    <td>
                        <textarea
                        type='text'
                        className='form-control'
                        required
                        value={this.state.description}
                        onChange={this.onDescriptionChange}>
                        </textarea>
                    </td>
                </tr>

                <tr>
                    <td>Price ($)</td>
                    <td>
                        <input
                        type='number'
                        step="0.01"
                        className='form-control'
                        value={this.state.price}
                        required
                        onChange={this.onPriceChange}/>
                    </td>
                </tr>

                <tr>
                    <td>Category</td>
                    <td>
                        <select
                        onChange={this.onCategoryChange}
                        className='form-control'
                        value={this.state.selectedCategoryId}>
                        <option value="-1">Select category...</option>
                        {categoriesOptions}
                        </select>
                    </td>
                </tr>

                <tr>
                    <td></td>
                    <td>
                        <button
                        className='btn btn-primary'
                        onClick={this.onSave}>Save</button>
                    </td>
                </tr>
                </tbody>
            </table>
        </form>
    </div>
    );
}

7.14 Output

You should have the following output so far in this React CRUD tutorial.

level-1-create-record

8.0 Read Records with React

We will now have two screens, we call it ‘mode’ in this tutorial. One for the “create product” and one for the “read product”.

8.1 MainApp Initial State

Now we will put values to MainApp component’s initial state. Put the following code inside the MainApp component class (and before the render method code) in section 7.2 above.

getInitialState: function() {
    return {
        currentMode: 'read',
        productId: null
    };
},

8.2 Put Custom changeAppMode Method

The custom changeAppMode method will help set the current mode of our app, in which we currently have two modes: “create” and “read” products. Put the following code after the previous section’s code.

changeAppMode: function(newMode, productId){
    this.setState({currentMode: newMode});

    if(productId !== undefined){
        this.setState({productId: productId});
    }
},

8.3 Add App Mode

We will change the code on section 7.2 above. We will have ReadProductsComponent as the default component. We put the switch statement there to identify the current mode of our app.

We will add the “readOne”, “update” and “delete” mode on the next few parts of our tutorial. For now, the render method (section 7.2 code) will look like the following:

render: function() {

    var modeComponent =
        <ReadProductsComponent
        changeAppMode={this.changeAppMode} />;

    switch(this.state.currentMode){
        case 'read':
            break;
        case 'create':
            modeComponent = <CreateProductComponent changeAppMode={this.changeAppMode}/>;
            break;
        default:
            break;
    }

    return modeComponent;
}

8.4 Add ReadProductsComponent Class

By now, you should know what methods our react component class usually contains: getInitialState, componentDidMount, componentWillUnmount and render method.

Our ReadProductsComponent will look like the following. You can read the code comments to learn what this component does, it should be easy to understand. If not, ask via the comments section below or email me mike@codeofaninja.com, I will answer your question.

Put the following code above the MainApp (section 7.2) component.

// component that contains all the logic and other smaller components
// that form the Read Products view
var ReadProductsComponent = React.createClass({
    getInitialState: function() {
        return {
            products: []
        };
    },

    // on mount, fetch all products and stored them as this component's state
    componentDidMount: function() {
        this.serverRequest = $.get("api/read_all_products.php", function (products) {
            this.setState({
                products: JSON.parse(products)
            });
        }.bind(this));
    },

    // on unmount, kill product fetching in case the request is still pending
    componentWillUnmount: function() {
        this.serverRequest.abort();
    },

    render: function() {
        // list of products
        var filteredProducts = this.state.products;
        $('.page-header h1').text('Read Products');

        return (
            <div className='overflow-hidden'>
                <TopActionsComponent changeAppMode={this.props.changeAppMode} />

                <ProductsTable
                    products={filteredProducts}
                    changeAppMode={this.props.changeAppMode} />
            </div>
        );
    }
});

8.5 Create read_all_products.php

The previous section won’t work without read_all_products.php that gets data from the database in JSON format. Inside “api” folder, create read_all_products.php with the following code.

<?php
// include core configuration
include_once '../config/core.php';

// include database connection
include_once '../config/database.php';

// product object
include_once '../objects/product.php';

// class instance
$database = new Database();
$db = $database->getConnection();
$product = new Product($db);

// read all products
$results=$product->readAll();

// output in json format
echo $results;
?>

8.6 Object Code To Real All Products

The previous section will not work without the readAll() object method. Add the following code below the create() method of our “product.php” inside the “objects” folder.

public function readAll(){

    //select all data
    $query = "SELECT p.id, p.name, p.description, p.price, c.name as category_name
    			FROM " . $this->table_name . " p
    				LEFT JOIN categories c
    					ON p.category_id=c.id
    			ORDER BY id DESC";

    $stmt = $this->conn->prepare($query);
    $stmt->execute();

    $results=$stmt->fetchAll(PDO::FETCH_ASSOC);

    return json_encode($results);
}

8.7 Add TopActionsComponent

As you can see in the section 8.4, ReadProductsComponent renders two other components: TopActionsComponent and ProductsTable. We will add the TopActionsComponent by putting the following code above ReadProductsComponent.

// component that contains the functionalities that appear on top of
// the products table: create product
var TopActionsComponent = React.createClass({
    render: function() {
        return (
            <div>
                <a href='#'
                    onClick={() => this.props.changeAppMode('create')}
                    className='btn btn-primary margin-bottom-1em'> Create product
                </a>
            </div>
        );
    }
});

8.8 Add ProductsTable Component

ProductsTable component renders our table head and body where the list of data and action buttons (read, edit, delete) will be shown. Put the following code above the TopActionsComponent code of the previous section.

// component for the whole products table
var ProductsTable = React.createClass({
    render: function() {

    var rows = this.props.products
        .map(function(product, i) {
            return (
                <ProductRow
                    key={i}
                    product={product}
                    changeAppMode={this.props.changeAppMode} />
            );
        }.bind(this));

        return(
            !rows.length
                ? <div className='alert alert-danger'>No products found.</div>
                :
                <table className='table table-bordered table-hover'>
                    <thead>
                        <tr>
                            <th>Name</th>
                            <th>Description</th>
                            <th>Price</th>
                            <th>Category</th>
                            <th>Action</th>
                        </tr>
                    </thead>
                    <tbody>
                        {rows}
                    </tbody>
                </table>
        );
    }
});

8.9 Add ProductRow Component

As you can see in the previous section, ProductsTable component renders the ProductRow component. It renders the single row product record. Add the following code above the previous section’s code.

// component that renders a single product
var ProductRow = React.createClass({
    render: function() {
    return (
        <tr>
            <td>{this.props.product.name}</td>
            <td>{this.props.product.description}</td>
            <td>${parseFloat(this.props.product.price).toFixed(2)}</td>
            <td>{this.props.product.category_name}</td>
            <td>
                <a href='#'
                    onClick={() => this.props.changeAppMode('readOne', this.props.product.id)}
                    className='btn btn-info m-r-1em'> Read
                </a>
                <a href='#'
                    onClick={() => this.props.changeAppMode('update', this.props.product.id)}
                    className='btn btn-primary m-r-1em'> Edit
                </a>
                <a
                    onClick={() => this.props.changeAppMode('delete', this.props.product.id)}
                    className='btn btn-danger'> Delete
                </a>
            </td>
        </tr>
        );
    }
});

8.10 Output

Here’s the output of all we did in section 8 of this React CRUD tutorial. When you clicked the “Create Product” button, it will show you the working form to create a product.

level-1-read-records

9.0 Read One Record with React

9.1 Change MainApp Render Method

We need to add another mode or “case” on our switch statement. It will be the “readOne” case. It will return the ReadOneProductComponent class. Our MainApp render method will now look like the following.

render: function() {

    var modeComponent =
        <ReadProductsComponent
        changeAppMode={this.changeAppMode} />;

    switch(this.state.currentMode){
        case 'read':
            break;
        case 'readOne':
            modeComponent = <ReadOneProductComponent productId={this.state.productId} changeAppMode={this.changeAppMode}/>;
            break;
        case 'create':
            modeComponent = <CreateProductComponent changeAppMode={this.changeAppMode} />;
            break;
        default:
            break;
    }

    return modeComponent;
}

9.2 ReadOneProductComponent Class

Now, here’s the component that will render a single product data on an HTML table.

var ReadOneProductComponent = React.createClass({

    getInitialState: function() {
        // make sure that no other values are set
        return {
            id: 0,
            name: '',
            description: '',
            price: 0,
            category_name: ''
        };
    },

    // on mount, read one product based on given product ID
    componentDidMount: function() {

        var productId = this.props.productId;

        this.serverRequestProd = $.post("api/read_one_product.php",
            {prod_id: productId},
            function (product) {
                var p = JSON.parse(product)[0];
                this.setState({category_name: p.category_name});
                this.setState({id: p.id});
                this.setState({name: p.name});
                this.setState({description: p.description});
                this.setState({price: p.price});
            }.bind(this));

        $('.page-header h1').text('Read Product');
    },

    // on unmount, kill fetching the product data in case the request is still pending
    componentWillUnmount: function() {
        this.serverRequestProd.abort();
    },

    // show single product data on a table
    render: function() {

        return (
            <div>
                <a href='#'
                    onClick={() => this.props.changeAppMode('read')}
                    className='btn btn-primary margin-bottom-1em'>
                    Read Products
                </a>

                <form onSubmit={this.onSave}>
                    <table className='table table-bordered table-hover'>
                        <tbody>
                        <tr>
                            <td>Name</td>
                            <td>{this.state.name}</td>
                        </tr>

                        <tr>
                            <td>Description</td>
                            <td>{this.state.description}</td>
                        </tr>

                        <tr>
                            <td>Price ($)</td>
                            <td>${parseFloat(this.state.price).toFixed(2)}</td>
                        </tr>

                        <tr>
                            <td>Category</td>
                            <td>{this.state.category_name}</td>
                        </tr>

                        </tbody>
                    </table>
                </form>
            </div>
        );
    }
});

9.3 PHP Script To Read One Product

The componentDidMount method in the previous section will not work without read_one_product.php, create that file inside the “api” folder and put the following code inside it.

<?php
// include core configuration
include_once '../config/core.php';

// include database connection
include_once '../config/database.php';

// product object
include_once '../objects/product.php';

// class instance
$database = new Database();
$db = $database->getConnection();
$product = new Product($db);

// read all products
$product->id=$_POST['prod_id'];
$results=$product->readOne();

// output in json format
echo $results;
?>

9.4 Object Method To Read One Product

Add the following object method inside Product class in objects/product.php – this will make our code on previous section work.

public function readOne(){

    // select one record
    $query = "SELECT p.id, p.name, p.description, p.price, p.category_id, c.name as category_name
                FROM " . $this->table_name . " p LEFT JOIN categories c ON p.category_id=c.id
                WHERE p.id=:id";

    //prepare query for excecution
    $stmt = $this->conn->prepare($query);

    $id=htmlspecialchars(strip_tags($this->id));
    $stmt->bindParam(':id', $id);
    $stmt->execute();

    $results=$stmt->fetchAll(PDO::FETCH_ASSOC);

    return json_encode($results);
}

9.5 Output

Here’s the output when the user clicks the “Read” button in the read mode.

level-1-read-one-record

10.0 Update Record with React

10.1 Change MainApp Render Method

Add the ‘update’ case on the switch statement of our MainApp’s render method. The method should look like the following.

render: function() {

    var modeComponent =
        <ReadProductsComponent
        changeAppMode={this.changeAppMode} />;

    switch(this.state.currentMode){
        case 'read':
            break;
        case 'readOne':
            modeComponent = <ReadOneProductComponent productId={this.state.productId} changeAppMode={this.changeAppMode}/>;
            break;
        case 'create':
            modeComponent = <CreateProductComponent changeAppMode={this.changeAppMode}/>;
            break;
        case 'update':
            modeComponent = <UpdateProductComponent productId={this.state.productId} changeAppMode={this.changeAppMode}/>;
            break;
        default:
            break;
    }

    return modeComponent;
}

10.2 Add UpdateProductComponent

This component contains the logic to update a product. It includes server requests to read categories (for dropdown), read single product, update a product, and the update form itself. Add the following code above the MainApp component class.

var UpdateProductComponent = React.createClass({

});

10.3 Prepare Component State

Put the following code inside UpdateProductComponent class of the previous section.

getInitialState: function() {
    return {
        categories: [],
        selectedCategoryId: 0,
        id: 0,
        name: '',
        description: '',
        price: 0,
        successUpdate: null
    };
},

10.4 Prepare componentDidMount Method

On mount, fetch all categories and store them as this component’s state. Read a single record based on given product ID as well.

componentDidMount: function() {
    this.serverRequestCat = $.get("api/read_all_categories.php", function (categories) {
        this.setState({
            categories: JSON.parse(categories)
        });
    }.bind(this));

    var productId = this.props.productId;
    this.serverRequestProd = $.post("api/read_one_product.php",
        {prod_id: productId},
        function (product) {
            var p = JSON.parse(product)[0];
            this.setState({selectedCategoryId: p.category_id});
            this.setState({id: p.id});
            this.setState({name: p.name});
            this.setState({description: p.description});
            this.setState({price: p.price});
        }.bind(this));

    $('.page-header h1').text('Update product');
},

The scripts of read_all_categories.php and read_one_product.php is already done in the previous sections.

10.5 Prepare componentWillUnmount Method

On unmount, kill categories and product fetching in case the request is still pending. Put the following code after the previous section’s code.

componentWillUnmount: function() {
    this.serverRequestCat.abort();
    this.serverRequestProd.abort();
},

10.6 Handle State Changes

Put the following code after the previous sections’s code. This is how we handle form field changes as explained in section 7.9 above.

// handle category change
onCategoryChange: function(e) {
    this.setState({selectedCategoryId: e.target.value});
},

// handle name change
onNameChange: function(e) {
    this.setState({name: e.target.value});
},

// handle description change
onDescriptionChange: function(e) {
    this.setState({description: e.target.value});
},

// handle price change
onPriceChange: function(e) {
    this.setState({price: e.target.value});
},

10.7 Handle Form Submit

The following code is executed when the user clicked the “Save Changes” button. Put it after the previous section’s code.

onSave: function(e){
    $.post("api/update_product.php", {
            id: this.state.id,
            name: this.state.name,
            description: this.state.description,
            price: this.state.price,
            category_id: this.state.selectedCategoryId
        },
        function(res){
            this.setState({successUpdate: res});
        }.bind(this)
    );
    e.preventDefault();
},

10.8 PHP Script To Update Product

The previous section will not work without update_product.php file, let’s create that file inside the “api” folder. The following code must be inside it.

<?php
// if the form was submitted
if($_POST){

	// include core configuration
	include_once '../config/core.php';

	// include database connection
	include_once '../config/database.php';

	// product object
	include_once '../objects/product.php';

	// class instance
	$database = new Database();
	$db = $database->getConnection();
	$product = new Product($db);

	// new values
	$product->name=$_POST['name'];
	$product->description=$_POST['description'];
	$product->price=$_POST['price'];
	$product->category_id=$_POST['category_id'];
	$product->id=$_POST['id'];

	// update the product
	echo $product->update() ? "true" : "false";
}
?>

10.9 Object Method To Update Product

The previous section will not work without the following code on our product class in objects/product.php

public function update(){

    $query = "UPDATE products
                SET name=:name, description=:description, price=:price, category_id=:category_id
                WHERE id=:id";

    //prepare query for excecution
    $stmt = $this->conn->prepare($query);

    // sanitize
    $name=htmlspecialchars(strip_tags($this->name));
    $description=htmlspecialchars(strip_tags($this->description));
    $price=htmlspecialchars(strip_tags($this->price));
    $category_id=htmlspecialchars(strip_tags($this->category_id));
    $id=htmlspecialchars(strip_tags($this->id));

    // bind the parameters
    $stmt->bindParam(':name', $name);
    $stmt->bindParam(':description', $description);
    $stmt->bindParam(':price', $price);
    $stmt->bindParam(':category_id', $category_id);
    $stmt->bindParam(':id', $id);

    // execute the query
    if($stmt->execute()){
        return true;
    }else{
        return false;
    }
}

10.10 Update Product Form

The following render method code does the following.

  • Arrange “options” for our categories drop down. (related to section 10.4)
  • Tell the user if a product was updated.
  • Tell the user if unable to update product.
  • Button to go back to products list.
  • Form to update a product.
  • Binds each field to custom onChange methods. (related to section 10.6)

Put the following code after the code on section 10.7 above.

render: function() {
    var categoriesOptions = this.state.categories.map(function(category){
        return (
            <option key={category.id} value={category.id}>{category.name}</option>
        );
    });

    return (
        <div>
            {
                this.state.successUpdate == "true" ?
                    <div className='alert alert-success'>
                        Product was updated.
                    </div>
                : null
            }

            {
                this.state.successUpdate == "false" ?
                    <div className='alert alert-danger'>
                        Unable to update product. Please try again.
                    </div>
                : null
            }

            <a href='#'
                onClick={() => this.props.changeAppMode('read')}
                className='btn btn-primary margin-bottom-1em'>
                Read Products
            </a>

            <form onSubmit={this.onSave}>
                <table className='table table-bordered table-hover'>
                    <tbody>
                    <tr>
                        <td>Name</td>
                        <td>
                            <input
                                type='text'
                                className='form-control'
                                value={this.state.name}
                                required
                                onChange={this.onNameChange} />
                        </td>
                    </tr>

                    <tr>
                        <td>Description</td>
                        <td>
                            <textarea
                                type='text'
                                className='form-control'
                                required
                                value={this.state.description}
                                onChange={this.onDescriptionChange}></textarea>
                        </td>
                    </tr>

                    <tr>
                        <td>Price ($)</td>
                        <td>
                            <input
                                type='number'
                                step="0.01"
                                className='form-control'
                                value={this.state.price}
                                required
                                onChange={this.onPriceChange}/>
                        </td>
                    </tr>

                    <tr>
                        <td>Category</td>
                        <td>
                            <select
                                onChange={this.onCategoryChange}
                                className='form-control'
                                value={this.state.selectedCategoryId}>
                                <option value="-1">Select category...</option>
                                {categoriesOptions}
                                </select>
                        </td>
                    </tr>

                    <tr>
                        <td></td>
                        <td>
                            <button
                                className='btn btn-primary'
                                onClick={this.onSave}>Save Changes</button>
                        </td>
                    </tr>

                    </tbody>
                </table>
            </form>
        </div>
    );
}

10.11 Output

Here’s the output of all we did in section 10.
level-1-update-record

11.0 Delete Record with React

11.1 Change MainApp Render Method

On our MainApp component > render method > switch statement, add the ‘delete’ case. The render method should look like the following.

render: function() {

    var modeComponent =
        <ReadProductsComponent
        changeAppMode={this.changeAppMode} />;

    switch(this.state.currentMode){
        case 'read':
            break;
        case 'readOne':
            modeComponent = <ReadOneProductComponent productId={this.state.productId} changeAppMode={this.changeAppMode}/>;
            break;
        case 'create':
            modeComponent = <CreateProductComponent changeAppMode={this.changeAppMode}/>;
            break;
        case 'update':
            modeComponent = <UpdateProductComponent productId={this.state.productId} changeAppMode={this.changeAppMode}/>;
            break;
        case 'delete':
            modeComponent = <DeleteProductComponent productId={this.state.productId} changeAppMode={this.changeAppMode}/>;
            break;
        default:
            break;
    }

    return modeComponent;
}

11.2 Add DeleteProductComponent

This component contains the logic to delete a record. To explain each methods:

  • componentDidMount – on mount, change header text
  • onDelete – handle single row deletion, invoked when user clicked the “Yes” button
  • render – will show the question “Are you sure?” with “Yes” and “No” buttons.
var DeleteProductComponent = React.createClass({

    componentDidMount: function() {
        $('.page-header h1').text('Delete product');
    },

    onDelete: function(e){
        var productId = this.props.productId;

        $.post("api/delete_products.php",
            { del_ids: [productId] },
            function(res){
                this.props.changeAppMode('read');
            }.bind(this)
        );
    },

    render: function() {

        return (
            <div className='row'>
                <div className='col-md-3'></div>
                <div className='col-md-6'>
                    <div className='panel panel-default'>
                    	<div className='panel-body text-align-center'>Are you sure?</div>
                    	<div className='panel-footer clearfix'>
                    		<div className='text-align-center'>
                                <button onClick={this.onDelete}
                                    className='btn btn-danger m-r-1em'>Yes</button>
                                <button onClick={() => this.props.changeAppMode('read')}
                                    className='btn btn-primary'>No</button>
                    		</div>
                    	</div>
                    </div>
                </div>
                <div className='col-md-3'></div>
            </div>
        );
    }
});

11.3 Create delete_products.php

The previous section will not work without delete_product.php file. Create the file inside the “api” folder. The file should have the following code.

<?php
// if the form was submitted
if($_POST){

	// include core configuration
	include_once '../config/core.php';

	// include database connection
	include_once '../config/database.php';

	// product object
	include_once '../objects/product.php';

	// class instance
	$database = new Database();
	$db = $database->getConnection();
	$product = new Product($db);

	$ins="";
	foreach($_POST['del_ids'] as $id){
		$ins.="{$id},";
	}

	$ins=trim($ins, ",");

	// delete the product
	echo $product->delete($ins) ? "true" : "false";
}
?>

11.4 Object Method To Delete A Product

The previous section will not work without the following method on our Product object class.

// delete selected products
public function delete($ins){

    // query to delete multiple records
    $query = "DELETE FROM products WHERE id IN (:ins)";

    $stmt = $this->conn->prepare($query);

	// sanitize
	$ins=htmlspecialchars(strip_tags($ins));

	// bind the parameter
	$stmt->bindParam(':ins', $ins);

    if($stmt->execute()){
        return true;
    }else{
        return false;
    }
}

11.5 Output

level-1-delete-record

12.0 Download Source Codes

You can get the source code by following the whole, well detailed tutorial above. But isn’t it more convenient if you can just download the complete source code we used, and play around it?

There’s a small fee in getting the complete source code, it is small compared to the:

✔ Value or skill upgrade it can bring you, orYES
✔ Income you can get from your website project or business.YES
✔ Precious time you save.YES

For a limited time, I will give you the source code for a low price. DOWNLOAD THE SOURCE CODE LEVEL you desire by clicking its the BUY button below.

12.1 Download the LEVEL 1 Source Code

FEATURESLEVEL 1
PDO extension usedYES
Bootstrap UI enabledYES
Facebook ReactYES
Create productYES
Read productYES
Update productYES
Delete productYES
Category selection for create and update product.YES
Price display with dollar signYES
SQL file in “dev” folderYES
LEVEL 1: BUY AND DOWNLOAD NOW USING
* You can use your debit or credit card with PayPal.

12.2 Download the LEVEL 2 Source Code

FEATURESLEVEL 2
All features of LEVEL 1 aboveYES
Search recordsYES
Show records when search box is emptyYES
Routing works for search, page, create, edit and delete pagesYES
Export CSVYES
Multiple deleteYES
PaginationYES
Pagination in searchYES
Go to page numberYES
Limit go to page number based on number of pagesYES
Buttons with glyphiconsYES
LEVEL 1: BUY AND DOWNLOAD NOW USING
* You can use your debit or credit card with PayPal.

Do you need more reasons to get it?

MORE REASONS TO DOWNLOAD THE CODEALL
Use new skills for your multiple projectsYES
Save huge amount of time learning PHP, MySQL and ReactYES
Code examples are direct to the pointYES
Well explained and commented source codeYES
Fast and friendly email supportYES
Free source code updatesYES

12.4 Download ALL LEVELS in Separate Packages

DOWNLOAD ALL 2 PACKAGES ABOVEALL
LEVEL 1 source code packageYES
LEVEL 2 source code packageYES
LEVEL 1: BUY AND DOWNLOAD NOW USING
* You can use your debit or credit card with PayPal.

If you have any more questions, please feel free to contact me now. You can do it by sending a message on our official Facebook page, or via my email mike@codeofaninja.com

Thanks for supporting our website and projects here at codeofaninja.com!

13.0 What’s Next?

Next, we will learn how to create, read, update and delete records with AngularJS, also known as Angular 1.x. Click the following link: AngularJS CRUD Tutorial

14.0 Related Source Codes

Here are the related source codes used in this tutorial.

15.0 Online Resources

Here are useful resources related to React.

16.0 Some Notes

#1 Found An Issue?

If you found a problem with this code, we can solve it faster via Email or FB message, please send me a message via email mike@codeofaninja.com, or via our official Facebook page!

Please be more detailed about your issue. Best if you can provide an error message and your test or page URL. Thanks!

Please feel free to comment if you have any questions, suggestions, found something wrong or want to contribute to this code.

#2 Become a true Ninja!

We constantly add new tutorials and improve our existing tutorials and source codes. Be one of the first to know an update by subscribing to our FREE newsletter. Get a FREE EBOOK as well. CLICK HERE TO SUBSCRIBE FOR FREE!

#3 Thank You!

Thanks for reading our step by step tutorial about PHP, MySQL and React CRUD Tutorial!

AngularJS CRUD Example with Material Design – Step by Step Guide!


Previously, we learned how to create, read, update and delete database records with our React CRUD Tutorial. Today, we will learn how to do the same CRUD operations with AngularJS, Angular Material and REST API.

Contents of this tutorial include:

1.0 Tutorial Overview
2.0 Program Output – AngularJS CRUD Example
3.0 Set Up The REST API

4.0 Basic Files & Folders
4.1 File Structure
4.2 Create index.html file
4.3 Include Angular Material, Roboto Font and Custom CSS
4.4 AngularJS and Angular Material JavaScript
4.6 Custom JavaScript
4.7 Place AngularJS app container
4.8 Output

5.0 How To Read JSON Data Using AngularJS?
5.1 Create Template for products list
5.2 Create “products” controller
5.3 Create “products” factory
5.4 Output

6.0 How To Create or 6Insert Data Using AngularJS?
6.1 Add “create” product button
6.2 Show “create product” form in dialog box
6.3 Create dialog controller
6.4 Create “create_product” HTML form template
6.5 Add “createProduct” method in controller
6.6 Clear form contents
6.7 Show messages to user
6.8 Add “createProduct” method in factory
6.9 Output

7.0 How To Read One Data Using AngularJS?
7.1 Add “Read One” button in products list
7.2 Show product information in dialog box
7.3 Create “read_one_product.template.html” file
7.4 Add “readOneProduct” method in factory
7.5 Output

8.0 How To Update Data Using AngularJS?
8.1 Add “Edit” button in products list
8.2 Show editable product information in dialog box
8.3 Create “update_product.template.html” file
8.4 Add “updateProduct” method in controller
8.5 Add “updateProduct” method in factory
8.6 Output

9.0 How To Delete Data Using AngularJS?
9.1 Add “delete” button in products list
9.2 Ask user to confirm product deletion
9.3 Add “deleteProduct” function in controller
9.4 Add “deleteProduct” function in factory
9.5 Output

10.0 How To Search Data Using AngularJS?
10.1 Allow users to type search keywords
10.2 Add “searchProducts” function in controller
10.3 Add “searchProducts” function in factory
10.4 Output

11.0 How To Run The Source Code?
12.0 Download LEVEL 1 Source Code
13.0 Download LEVEL 2 Source Code
14.0 Download ALL LEVELS

15.0 What’s Next?
16.0 Related Source Codes
17.0 Notes

1.0 Tutorial Overview

What is AngularJS? It is one of the leading framework for building large JavaScript applications that are easy to maintain.

It makes the development faster because we can implement useful concepts like dependency injection, two-way data binding, reusable code and unit testing.

This tutorial will focus on creating, reading, updating, deleting and searching database records. We will do it using AngularJS and Angular Material as it’s user interface component.

JSON data will be handled by the REST API built using PHP.

2.0 Program Output – AngularJS CRUD Example

2.1 LEVEL 1 Source Code Output


2.2 LEVEL 2 Source Code Output


The LEVEL 2 source code output proves that you can add and customize more features. It’s easier and faster if you will learn by following our tutorial below.

Downloading our source codes is your huge advantage as well. For now, let’s proceed to the step by step tutorial of our LEVEL 1 source code. Enjoy!

3.0 Set Up The REST API

In this tutorial, we are going to use a REST API built with PHP.

We did not include REST API source code because we want you to focus on learning how to code with AngularJS, not PHP.

But the good news is, we made a separate tutorial about how to build a simple REST API with PHP. Click here to learn the step-by-step PHP REST API tutorial.

I highly recommend learning our REST API tutorial first. This is because we are going to use that API for the rest of this tutorial.

But if you already have your own REST API that will work with this tutorial, that’s okay as well.

In my case, one example where I can access the REST API is: http://localhost/api/product/read.php

That link will show me the list of products from the database, in JSON format. It looks like the following screenshot.

4.0 Basic Files & Folders

4.1 File Structure

At the end of this tutorial, we will have the following file structure.
├─ app/
├─── assets/
├────── css/
├───────── custom.css
├─── products/
├────── create_product.template.html
├────── products.controller.js
├────── products.factory.js
├────── read_one_product.template.html
├────── read_products.template.html
├────── update_product.template.html
├─── app.js
├─ index.html

4.2 Create index.html file

Create index.html file. Open that file and put the following basic HTML code.

<!DOCTYPE html>
<html>
<head>

    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <title>Read Products</title>

</head>
<body>

<!-- app container will be here -->

</body>
</html>

4.3 Include Angular Material, Roboto Font and Custom CSS

We are going to use CSS files from Google CDN. Place the following code inside “head” tag, under “title” tag of the previous section.

<!-- angular material style sheet -->
<link rel="stylesheet" href="http://ajax.googleapis.com/ajax/libs/angular_material/1.1.0/angular-material.min.css">

<!-- roboto font -->
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700,400italic">

<!-- include custom CSS -->
<link rel="stylesheet" href="app/assets/css/custom.css" />

Our “custom.css” file does not exist yet so we have to create it. We will put some CSS as well to style some parts of our application.

  1. Create “app” folder.
  2. Inside “app” folder, create “assets” folder.
  3. Inside “assets” folder, create “css” folder.
  4. Inside “css” folder, create “custom.css” file.
  5. Open “custom.css” file and place the following code.
md-dialog{ width:50%; }
.m-20px-0{ margin:20px 0; }
.m-b-20px{ margin-bottom:20px; }
.m-b-0px{ margin-bottom:0px; }
.md-info{ background-color:#2196F3 !important; color:#ffffff !important; }
.md-danger{ background-color:#D32F2F !important; color:#ffffff !important; }
.w-5-pct{ width:5%; }
.w-10-pct{ width:10%; }
.w-20-pct{ width:20%; }
.w-30-pct{ width:30%; }
.w-35-pct{ width:35%; }
.w-40-pct{ width:40%; }
.w-50-pct{ width:50%; }
.w-60-pct{ width:60%; }
.w-70-pct{ width:70%; }
.w-80-pct{ width:80%; }
.w-90-pct{ width:90%; }
.w-100-pct{ width:100%; }
.t-a-c{ text-align:center; }
.t-a-l{ text-align:left; }
.margin-bottom-1em{ margin-bottom:1em; }

4.4 AngularJS and Angular Material JavaScript

We are going to use AngularJS JavaScript files from Google CDN. Place the following code before the closing “body” tag.

<!-- include angular js -->
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.1/angular.min.js"></script>

<!-- needed for angular material -->
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.5.5/angular-animate.min.js"></script>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.5.5/angular-aria.min.js"></script>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.5.5/angular-messages.min.js"></script>

<!-- angular material Library -->
<script src="http://ajax.googleapis.com/ajax/libs/angular_material/1.1.0/angular-material.min.js"></script>

4.6 Custom JavaScript

Place the following code after the previous section’s code.

<!-- app -->
<script type="text/javascript" src="app/app.js"></script>

<!-- products controller -->
<script type="text/javascript" src="app/products/products.controller.js"></script>

<!-- products factory -->
<script type="text/javascript" src="app/products/products.factory.js"></script>

The JavaScript files above does not exist yet so we will create it.

  1. Open “app” folder.
  2. Inside “app” folder, create “app.js” file.
  3. Inside “app” folder again, create “products” folder.
  4. Inside “products” folder, create “products.controller.js” and “products.factory.js” files.

Open “app.js” file and place the following code.

var app = angular.module('myApp', ['ngMaterial']);

We added ‘ngMaterial’ parameter to enable our Angular Material user interface. We will explain and place AngularJS codes inside “products.controller.js” and “products.factor.js” files as we progress in this tutorial.

4.7 Place AngularJS app container

The “read_products.template.html” will hold the template for displaying list of products.

Find

<!-- app container will be here -->

in index.html and replace it with the following code.

<!-- container of our app -->
<div class="container" ng-app="myApp">

    <!-- read products template -->
    <ng-include src="'./app/products/read_products.template.html'"></ng-include>

</div> <!-- end container -->

The “read_products.template.html” file does not exist yet so we will create it.

  1. Open “app” folder.
  2. Inside “app” folder, open “products” folder.
  3. Inside “products” folder, create “read_products.template.html” file.

4.8 Output

After doing all the steps above. The output is a blank page. Yes, a blank page for now. But for sure, we will see something here after you complete the next section.

5.0 How To Read JSON Data Using AngularJS?

5.1 Template for products list

Open “read_products.template.html” file and place the following code.

<div layout="row" ng-controller="productsController" ng-cloak>
    <div flex="10"></div>

    <div flex="80" class='m-20px-0'>

        <!-- page title & 'create product' button -->
        <md-toolbar class="md-hue-2">
            <div class="md-toolbar-tools">
                <!-- page title -->
                <div flex md-truncate>
                    <h3 class="md-headline">Products</h3>
                </div>

                <!-- 'create' product button will be here -->
            </div>
        </md-toolbar>

        <!-- 'search' bar will be here -->

        <md-content flex>
            <!-- show product record list -->
            <md-list ng-init="readProducts()">
                <md-list-item class="md-3-line" ng-repeat="product in products">

                    <!-- product information -->
                    <div class="md-list-item-text">
                        <h3>{{product.name}}</h3>
                        <h4>{{product.description}}</h4>
                        <p>${{product.price}}</p>
                    </div>

                    <!-- 'read one', 'edit' and 'delete' buttons -->
                    <div class="md-secondary-container">
						<!-- 'read one' button will be here -->
						<!-- 'edit' button will be here -->
						<!-- 'delete' button will be here -->
                    </div>
                    <md-divider ng-if="!$last"></md-divider>
                </md-list-item>
            </md-list>
        </md-content>
    </div>

    <div flex="10"></div>
</div> <!-- end row -->

5.2 Create “products” controller

  1. Open “app” folder.
  2. Inside “app” folder, open “products” folder.
  3. Inside “products” folder, create “products.controller.js” file.
  4. Open “products.controller.js” file and place the following code.
app.controller('productsController', function($scope, $mdDialog, $mdToast, productsFactory){

	// read products
	$scope.readProducts = function(){

		// use products factory
		productsFactory.readProducts().then(function successCallback(response){
			$scope.products = response.data.records;
		}, function errorCallback(response){
			$scope.showToast("Unable to read record.");
		});

	}
	
	// showCreateProductForm will be here
	
	// DialogController will be here
});

5.3 Create “products” factory

Why do we use “factory” in AngularJS? We use it to make the code reusable.

Methods in factories can be used in different controllers or parts of your application. If there will be a need for change, you don’t have to go and change several parts of the application that uses the same code.

  1. Open “products.factory.js” file.
  2. Create “products.factory.js” file and place the following code.
app.factory("productsFactory", function($http){

    var factory = {};

    // read all products
    factory.readProducts = function(){
        return $http({
            method: 'GET',
            url: 'http://localhost/api/product/read.php'
        });
    };
	
	// createProduct will be here
	
	return factory;
});

5.4 Output

You should see the list of products. Each row should have product name, description and price.

6.0 How To Create or Insert Data Using AngularJS?

6.1 Add “create” product button

We need this button to show a form where user can enter new product information.

  1. Open “read_products.template.html”.
  2. Find
    <!-- 'create' product button will be here -->
  3. Replace it with the following code.
<!-- button for creating product -->
<md-button class="md-raised" ng-click="showCreateForm()">Create</md-button>

6.2 Show “create product” form in dialog box

  1. Open “products.controller.js” file.
  2. Find “// showCreateProductForm will be here” and replace it with the following code.
// show 'create product form' in dialog box
$scope.showCreateProductForm = function(event){

	$mdDialog.show({
		controller: DialogController,
		templateUrl: './app/products/create_product.template.html',
		parent: angular.element(document.body),
		targetEvent: event,
		clickOutsideToClose: true,
		scope: $scope,
		preserveScope: true,
		fullscreen: true // Only for -xs, -sm breakpoints.
	});
}

// createProduct will be here

6.3 Create dialog controller

We are using Angular Material’s dialog box above. Users may click the ‘Cancel’ button. We need a method for that.

  1. Open “products.controller.js”.
  2. Find “// DialogController will be here” and replace it with the following code.
// methods for dialog box
function DialogController($scope, $mdDialog) {
	$scope.cancel = function() {
		$mdDialog.cancel();
	};
}

6.4 Create “create_product” HTML form template

Our dialog box loads an HTML temlate via “templateUrl” property.

  1. Open “app” folder.
  2. Inside “app” folder, open “products” folder.
  3. Inside “products” folder, create “create_product.template.html” file.
  4. Open “create_product.template.html” file and place the following code.
<md-dialog>
    <form ng-cloak>
        
        <md-toolbar>
            <div class="md-toolbar-tools">
                <h2>Create Product</h2>
            </div>
        </md-toolbar>

        <md-dialog-content>
            <div class="md-dialog-content">

				<!-- we are using ng-model to bind this view and the 'product model -->
                <md-input-container class="md-block">
                    <label>Name</label>
                    <input ng-model="name">
                </md-input-container>

                <md-input-container class="md-block">
                    <label>Description</label>
                    <input ng-model="description">
                </md-input-container>

                <md-input-container class="md-block">
                    <label>Price</label>
                    <input ng-model="price">
                </md-input-container>

            </div>
        </md-dialog-content>

        <md-dialog-actions layout="row">
            <md-button ng-click="cancel()">Cancel</md-button>
            <md-button ng-click="createProduct()" class="md-primary">Create</md-button>
        </md-dialog-actions>
    </form>
</md-dialog>

6.5 Add “createProduct” method in controller

  1. Open “products.controller.js”.
  2. Find “// createProduct will be here” and replace it with the following code.
// create new product
$scope.createProduct = function(){

	productsFactory.createProduct($scope).then(function successCallback(response){

		// tell the user new product was created
		$scope.showToast(response.data.message);

		// refresh the list
		$scope.readProducts();

		// close dialog
		$scope.cancel();

		// remove form values
		$scope.clearProductForm();

	}, function errorCallback(response){
		$scope.showToast("Unable to create record.");
	});
}

// readOneProduct will be here

6.6 Clear form contents

We need clearProductForm to clear form values.

Place the following code after the previous section’s code.

// clear variable / form values
$scope.clearProductForm = function(){
	$scope.id = "";
	$scope.name = "";
	$scope.description = "";
	$scope.price = "";
}

6.7 Show messages to user

We are using “toasts” to show messages to users.

Place the following code after the previous section’s code.

// show toast message
$scope.showToast = function(message){
	$mdToast.show(
		$mdToast.simple()
			.textContent(message)
			.hideDelay(3000)
			.position("top right")
	);
}

6.8 Add “createProduct” method in factory

  1. Open “products.factory.js” file.
  2. Find “// createProduct will be here” and replace it with the following code.
// create product
factory.createProduct = function($scope){
	return $http({
		method: 'POST',
		data: {
			'name' : $scope.name,
			'description' : $scope.description,
			'price' : $scope.price,
			'category_id' : 1
		},
		url: 'http://localhost/api/product/create.php'
	});
};

// readOneProduct will be here

6.9 Output

You should see a “create” button on the upper right corner of the page. Once this button is clicked, a dialog box with a “create product” form should appear.

7.0 How To Read One Data Using AngularJS?

7.1 Put “Read One” button in products list

  1. Open “read_products.template.html” file.
  2. Find
    <!-- 'read one' button will be here -->

    and replace it with the following code.

<md-button class="md-raised md-info" ng-click="readOne(product.id)">Read One</md-button>

7.2 Show product information in dialog box

If the user click “read one” button, a dialog box will appear with the product information.

  1. Open “products.controller.js” file.
  2. Find “// readOneProduct will be here” and replace it with the following code.
// retrieve record to fill out the form
$scope.readOneProduct = function(id){

	// get product to be edited
	productsFactory.readOneProduct(id).then(function successCallback(response){

		// put the values in form
		$scope.id = response.data.id;
		$scope.name = response.data.name;
		$scope.description = response.data.description;
		$scope.price = response.data.price;

		$mdDialog.show({
			controller: DialogController,
			templateUrl: './app/products/read_one_product.template.html',
			parent: angular.element(document.body),
			targetEvent: event,
			clickOutsideToClose: true,
			scope: $scope,
			preserveScope: true,
			fullscreen: true
		}).then(
			function(){},

			// user clicked 'Cancel'
			function() {
				// clear modal content
				$scope.clearProductForm();
			}
		);

	}, function errorCallback(response){
		$scope.showToast("Unable to retrieve record.");
	});

}

// showUpdateProductForm will be here

7.3 Create “read_one_product.template.html” file

The dialog box need an HTML template with product information.

  1. Open “products” folder.
  2. Inside “products” folder, create “read_one_product.template.html” file and place the following code.
<md-dialog>
    <form ng-cloak>

        <!-- dialog box title -->
        <md-toolbar>
            <div class="md-toolbar-tools">
                <h2>Read One Product</h2>
            </div>
        </md-toolbar>

        <md-dialog-content>
            <div class="md-dialog-content">

                <!-- display product information -->
                <md-input-container class="md-block">
                    <label>Name</label>
                    <input ng-model="name" disabled>
                </md-input-container>

                <md-input-container class="md-block">
                    <label>Description</label>
                    <input ng-model="description" disabled>
                </md-input-container>

                <md-input-container class="md-block">
                    <label>Price</label>
                    <input ng-model="price" disabled>
                </md-input-container>

            </div>
        </md-dialog-content>

        <!-- button to close the dialog box -->
        <md-dialog-actions layout="row">
            <md-button ng-click="cancel()">Cancel</md-button>
        </md-dialog-actions>
    </form>
</md-dialog>

7.4 Add “readOneProduct” method in factory

We will get the product information from the API.

  1. Open “products.factory.js” file.
  2. Find “// readOneProduct will be here” and replace it with the following code.
// read one product
factory.readOneProduct = function(id){
	return $http({
		method: 'GET',
		url: 'http://localhost/api/product/read_one.php?id=' + id
	});
};

// updateProduct will be here

7.5 Output

You should see a “read one” button in the product list. Once user clicks this, a dialog box will appear with a product information.

8.0 How To Update Data Using AngularJS?

8.1 Put “Edit” button in products list

  1. Open “read_products.template.html” file.
  2. Find
    <!-- 'edit' button will be here -->

    and replace it with the following code.

<md-button class="md-raised md-primary" ng-click="showUpdateProductForm(product.id)">Edit</md-button>

8.2 Show editable product information in dialog box

  1. Open “products.controller.js” file.
  2. Find “// showUpdateProductForm will be here” and replace it with the following code.
// retrieve record to fill out the form
$scope.showUpdateProductForm = function(id){

	// get product to be edited
	productsFactory.readOneProduct(id).then(function successCallback(response){

		// put the values in form
		$scope.id = response.data.id;
		$scope.name = response.data.name;
		$scope.description = response.data.description;
		$scope.price = response.data.price;

		$mdDialog.show({
			controller: DialogController,
			templateUrl: './app/products/update_product.template.html',
			parent: angular.element(document.body),
			targetEvent: event,
			clickOutsideToClose: true,
			scope: $scope,
			preserveScope: true,
			fullscreen: true
		}).then(
			function(){},

			// user clicked 'Cancel'
			function() {
				// clear modal content
				$scope.clearProductForm();
			}
		);

	}, function errorCallback(response){
		$scope.showToast("Unable to retrieve record.");
	});

}

// updateProduct will be here

8.3 Create “update_product.template.html” file

The dialog box need an HTML template with product information.

  1. Open “products” folder.
  2. Inside “products” folder, create “update_product.template.html” file and place the following code.
<md-dialog>
    <form ng-cloak>

        <!-- dialog box title -->
        <md-toolbar>
            <div class="md-toolbar-tools">
                <h2>Update Product</h2>
            </div>
        </md-toolbar>

        <md-dialog-content>
            <div class="md-dialog-content">

                <!-- dialog box input fields with editable product information -->
                <md-input-container class="md-block">
                    <label>Name</label>
                    <input ng-model="name">
                </md-input-container>

                <md-input-container class="md-block">
                    <label>Description</label>
                    <input ng-model="description">
                </md-input-container>

                <md-input-container class="md-block">
                    <label>Price</label>
                    <input ng-model="price">
                </md-input-container>

            </div>
        </md-dialog-content>

        <!-- dialog box buttons -->
        <md-dialog-actions layout="row">
            <md-button ng-click="cancel()">Cancel</md-button>
            <md-button ng-click="updateProduct()" class="md-primary">Update</md-button>
        </md-dialog-actions>
    </form>
</md-dialog>

8.4 Put “updateProduct” method in controller

If the user click the “update” button in the dialog box, we will send the udpated data to the API.

  1. Open “products.controller.js” file.
  2. Find “// updateProduct will be here” and replace it with the following code.
// update product record / save changes
$scope.updateProduct = function(){

	productsFactory.updateProduct($scope).then(function successCallback(response){

		// tell the user product record was updated
		$scope.showToast(response.data.message);

		// refresh the product list
		$scope.readProducts();

		// close dialog
		$scope.cancel();

		// clear modal content
		$scope.clearProductForm();

	},
	function errorCallback(response) {
		$scope.showToast("Unable to update record.");
	});

}

// confirmDeleteProduct will be here

8.5 Put “updateProduct” method in factory

The factory will help us send data to API.

  1. Open “products.factory.js” file.
  2. Find “// updateProduct will be here” and replace it with the following code.
// update product
factory.updateProduct = function($scope){

	return $http({
		method: 'POST',
		data: {
			'id' : $scope.id,
			'name' : $scope.name,
			'description' : $scope.description,
			'price' : $scope.price,
			'category_id' : 1
		},
		url: 'http://localhost/api/product/update.php'
	});
};

// deleteProduct will be here

8.6 Output

You should see the “Edit” button in the products list. When this button is clicked, a dialog box with “udpate product” form will appear.

The form contains the editable product information.

9.0 How To Delete Data Using AngularJS?

9.1 Put “delete” button in products list

  1. Open “read_products.template.html” file.
  2. Find
    <!-- 'delete' button will be here -->

    and replace it with the following code.

<md-button class="md-raised md-danger" ng-click="confirmDeleteProduct($event, product.id)">Delete</md-button>

9.2 Ask user to confirm product deletion

  1. Open “products.controller.js” file.
  2. Find “// confirmDeleteProduct will be here” and replace it with the following code.
// cofirm product deletion
$scope.confirmDeleteProduct = function(event, id){

	// set id of record to delete
	$scope.id = id;

	// dialog settings
	var confirm = $mdDialog.confirm()
		.title('Are you sure?')
		.textContent('Product will be deleted.')
		.targetEvent(event)
		.ok('Yes')
		.cancel('No');

	// show dialog
	$mdDialog.show(confirm).then(
		// 'Yes' button
		function() {
			// if user clicked 'Yes', delete product record
			$scope.deleteProduct();
		},

		// 'No' button
		function() {
			// hide dialog
		}
	);
}

9.3 Put “deleteProduct” function in controller

If the user click the ‘Yes’ button, our app will delete the record.

Place the following code after the previous section’s code.

// delete product
$scope.deleteProduct = function(){

	productsFactory.deleteProduct($scope.id).then(function successCallback(response){

		// tell the user product was deleted
		$scope.showToast(response.data.message);

		// refresh the list
		$scope.readProducts();

	}, function errorCallback(response){
		$scope.showToast("Unable to delete record.");
	});

}

// searchProducts will be here

9.4 Put “deleteProduct” function in factory

The factory will help us send data to API.

  1. Open “products.factory.js” file.
  2. Find “// deleteProduct will be here” and replace it with the following code.
// delete product
factory.deleteProduct = function(id){
	return $http({
		method: 'POST',
		data: { 'id' : id },
		url: 'http://localhost/api/product/delete.php'
	});
};

// searchProducts will be here

9.5 Output

You should see the “Delete” button in products list. When a user click this button, a dialog box will appear asking the confirmation to delete the record.

10.0 How To Search Data Using AngularJS?

This feature is part of LEVEL 2 source code. We did not use “filter” to search product records. We search the records with the help of API.

10.1 Allow users to type search keywords

  1. Open “read_products.template.html” file.
  2. Find
    <!-- 'search' bar will be here -->

    and replace it with the following code.

<!-- used for searching products in api -->
<md-input-container class="md-block m-b-0px">
	<form ng-submit="searchProducts()">
		<label>Search products...</label>
		<input ng-model="product_search_keywords">
	</form>
</md-input-container>

10.2 Add “searchProducts” function in controller

  1. Open “products.controller.js” file.
  2. Find “// searchProducts will be here” and replace it with the following code.
// search products
$scope.searchProducts = function(){

	// use products factory
	productsFactory.searchProducts($scope.product_search_keywords).then(function successCallback(response){
		$scope.products = response.data.records;
	}, function errorCallback(response){
		$scope.showToast("Unable to read record.");
	});
}

10.3 Add “searchProducts” function in factory

The factory will help us read records based on search keywords.

  1. Open “products.factory.js” file.
  2. Find “// searchProducts will be here” and replace it with the following code.
// search all products
factory.searchProducts = function(keywords){
	return $http({
		method: 'GET',
		url: 'http://localhost/api/product/search.php?s=' + keywords
	});
};

10.4 Output

You should see the “search” bar above products list. Once user type a keyword and press the “enter” key, search results must be seen.

11.0 How To Run The Source Code?

We highly recommend for you to follow and study our well-detailed, step-by-step tutorial above first. Nothing beats experience when it comes to learning.

But we believe you will learn faster if you’ll see the final source code as well. We consider it as your additional guide.

Imagine the value or skill upgrade it can bring you. The additional income you can get from your work, projects or business. The precious time you save. Isn’t that what you want?

By now, you need to download our source codes. To do it, use any download buttons in the next few sections below.

Once you downloaded the source codes, here’s how you can run it.

  1. Extract the files to your server directory.
  2. Set up the “api” by following the README.txt inside the “api” folder.
  3. Open your browser and run index.html
  4. If you can see the list of products, it means your set up is correct.

12.0 Download LEVEL 1 Source Code

FEATURESLEVEL 1
Create productYES
Read productYES
Read one productYES
Update productYES
Delete productYES
Angular Material UIYES
PHP REST API source codeYES
FREE email support for 3 monthsYES
Source code updates via emailYES
LEVEL 1: BUY TO DOWNLOAD NOW USING
* You can use your debit or credit card with PayPal.

13.0 Download LEVEL 2 Source Code

FEATURESLEVEL 2
All features of LEVEL 1YES
Search productsYES
Pagination of products & search productsYES
Create categoryYES
Read categoryYES
Read one categoryYES
Update categoryYES
Delete categoryYES
Search categoriesYES
Pagination of categories & search categoriesYES
Navigation barYES
Use of AngularJS DirectivesYES
Use of AngularJS $q.all()YES
Use of AngularJS MainControllerYES
Use of Single pagination template FileYES
Use of ng-class in navigation barYES
FREE email support for 6 monthsYES
LEVEL 2: BUY TO DOWNLOAD NOW USING
* You can use your debit or credit card with PayPal.

14.0 Download ALL LEVELS

This means you will download the LEVEL 1 and LEVEL 2 source codes in two different packages.

DOWNLOAD ALL PACKAGES ABOVEALL
Level 1 source code packageYES
Level 2 source code packageYES
ALL LEVELS: BUY TO DOWNLOAD NOW USING
* You can use your debit or credit card with PayPal.

Do you need more reasons to get it?

MORE REASONS TO DOWNLOAD THE CODEALL
Use new skills for your multiple projectsYES
Save huge amount of time learning AngularJSYES
Code examples are direct to the pointYES
Well explained and commented source codeYES
Fast and friendly email supportYES
Free source code updatesYES

16.0 What’s Next?

Learn more JavaScript programming with our Angular 2 CRUD Tutorial – Step By Step Guide!

17.0 Related Source Codes

18.0 Some Notes

#1 Found An Issue?

If you found a problem with this code, we can solve it faster via Email or FB message, please send me a message via email mike@codeofaninja.com, or via our official Facebook page!

Please be more detailed about your issue. Best if you can provide an error message and your test or page URL. Thanks!

Please feel free to comment if you have any questions, suggestions, found something wrong or want to contribute to this code.

#2 Become a true Ninja!

We constantly add new tutorials and improve our existing tutorials and source codes. Be one of the first to know an update by subscribing to our FREE newsletter. Get a FREE EBOOK as well. CLICK HERE TO SUBSCRIBE FOR FREE!

#3 Thank You!

Thanks for reading our AngularJS CRUD Example with Material Design – Step by Step Guide!

AJAX CRUD Tutorial Using jQuery, JSON and PHP – Step by Step Guide!


Previously, we learned how to create a simple REST API in PHP. Today, we are going to learn how to create or insert, read, update and delete records with our AJAX CRUD Tutorial. We will use jQuery, JSON and PHP.

Contents of this tutorial include: Read more