12/08/2018, 17:42

Tạo ứng dụng CRUD với Angular 5 và Laravel 5.6

Trong bài này tôi sẽ trình bày quá trình tạo ra ứng dụng sử dụng angular và laravel để tạo ra một CRUD app hoàn chỉnh. Trong đó angular sẽ đảm nhiệm frontend và laravel sẽ tạo ra rest api. Trong các ví dụ dưới đây tôi sẽ sử dụng MAMP làm môi trường server. Bắt đầu nào. Đây là 1 screen của ứng ...

Trong bài này tôi sẽ trình bày quá trình tạo ra ứng dụng sử dụng angular và laravel để tạo ra một CRUD app hoàn chỉnh. Trong đó angular sẽ đảm nhiệm frontend và laravel sẽ tạo ra rest api. Trong các ví dụ dưới đây tôi sẽ sử dụng MAMP làm môi trường server. Bắt đầu nào.

Đây là 1 screen của ứng dụng mà chúng ta cần làm ra.

Install homebrew and xcode

Đầu tiên, chúng ta sẽ sử dụng homebrew giúp quá trình cài đặt dễ dàng hơn nhiều so với xcode của apple, nó bổ sung thêm các công cụ bạn cần để biên dịch phần mềm miễn phí và có thể được cài đặt thông qua App Store. Mở terminal gõ lệnh sau để cài homebrew.

ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"

Upgrade để homebrew cập nhât và sửa lỗi:

brew upgrade

** Install node and npm** Install nodejs bằng brew.

brew install node.

Kiểm tra version node.

node -v
5.6.0

Và npm cũng đã được cài, check version npm:

npm -v
5.6.0

Khi ban muốn update tất cả chỉ cần dùng lệnh:

brew upgrade

Hoặc chỉ muốn update 1 thành phần:

brew upgrade node

Riêng npm nhiều khi sẽ không tự nâng cấp nên cần làm thông qua chính nó.

npm i -g npm

Install and setup the angular environment Cài đặt angular CI

npm install -g @angular/cli

Để update angular.

npm install -g upgrade-angular

Chọn địa chỉ thư mục. Tạo mới ứng dụng angular.

cd /Applications/MAMP/htdocs

ng new angular-web

Check angular version:

cd /Applications/MAMP/htdocs/angular-web

ng -v

Đây là những gì bạn thấy, app được chạy trên Angular 5.2.10.

   _                      _                 ____ _     ___
   /    _ __   __ _ _   _| | __ _ _ __     / ___| |   |_ _|
  / △  | '_  / _` | | | | |/ _` | '__|   | |   | |    | |
 / ___ | | | | (_| | |_| | | (_| | |      | |___| |___ | |
/_/   \_\_| |_|\__, |\__,_|_|\__,_|_|       \____|_____|___|
               |___/

Angular CLI: 1.7.4
Node: 10.0.0
OS: darwin x64
Angular: 5.2.10
... animations, common, compiler, compiler-cli, core, forms
... http, language-service, platform-browser
... platform-browser-dynamic, router

@angular/cli: 1.7.4
@angular-devkit/build-optimizer: 0.3.2
@angular-devkit/core: 0.3.2
@angular-devkit/schematics: 0.3.2
@ngtools/json-schema: 1.2.0
@ngtools/webpack: 1.10.2
@schematics/angular: 0.3.2
@schematics/package-update: 0.3.2
typescript: 2.5.3
webpack: 3.11.0
  1. Install laravel Bây giờ là lúc cài đặt Laravel nhưng chỉ trong trường hợp bạn không có thiết lập phím tắt 'composer' và được cài đặt tốt nhất để đọc bài viết này trước tiên. Vì vậy, hãy tạo api còn lại bằng cách sử dụng 'laravel-api' làm thư mục.
cd /Applications/MAMP/htdocs

composer create-project laravel/laravel laravel-api --prefer-dist

Setup both app and api test domains Bây giờ theo mặc định, bạn có thể thiết lập và chạy ứng dụng góc bằng cách sử dụng các thao tác sau ...

ng serve --open

nhưng theo mặc định, nó sử dụng http: // localhost: 4200 làm url xem trước, nhưng chúng tôi muốn tự đặt tên miền để phát triển. Vì vậy, thay vào đó hãy nhập thông tin sau và nó sẽ tạo thư mục '/ dist /'.

cd /Applications/MAMP/htdocs/angular-web

// Keep a note of this since we will be using it everytime we want to generate a preview.
ng build --prod

Next setup the two localhost domains by editing the 'httpd.conf' file.

cd /Applications/MAMP/conf/apache/

open -e httpd.conf

Đối với điều này, tôi sẽ sử dụng 2 server 'web.testing' và 'laravel-api.testing' tương ứng với angular và laravel.

NameVirtualHost *:80

<VirtualHost *:80>
    ServerName angular-web.testing
    DocumentRoot /Applications/MAMP/htdocs/angular-web/dist
    <Directory /Applications/MAMP/htdocs/angular-web/dist/>
        Options Indexes FollowSymLinks MultiViews
        AllowOverride All
        Order allow,deny
        allow from all
    </Directory>
</VirtualHost>

<VirtualHost *:80>
    ServerName laravel-api.testing
    DocumentRoot /Applications/MAMP/htdocs/laravel-api/public
    <Directory /Applications/MAMP/htdocs/laravel-api/public/>
        Options Indexes FollowSymLinks MultiViews
        AllowOverride All
        Order allow,deny
        allow from all
    </Directory>
</VirtualHost>

Sửa file host để tạo domain ảo.

cd /private/etc/

open -e hosts

Thêm những dòng sau:

127.0.0.1 angular-web.testing
127.0.0.1 laravel-api.testing

Bây giờ khởi động MAMP và kiểm tra 2 domain bạn sẽ thấy kết quả như sau.

Create CRUD app layout Bây giờ chúng ta đang tìm đến những thứ thú vị và tạo ra một ứng dụng crud ví dụ. Vì vậy, đầu tiên mở tệp sau trong trình soạn thảo mã của bạn '/Applications/MAMP/htdocs/angular-web/src/index.html' cho điều này tôi đang sử dụng Bootstrap 4 cho bố cục.

<!doctype html>
<html lang="en">
    <head>
         
        <meta charset="utf-8">
        <title>Angular and Laravel CRUD web app</title>
        <meta name="viewport" content="awidth=device-awidth, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">

         
        <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
    </head>
    <body>
        <app-root></app-root>
    </body>
</html>

Tiếp theo, hãy chỉnh sửa tệp sau đây '/Applications/MAMP/htdocs/angular-web/src/app/app.component.html' và thêm phần sau có chứa một bảng để xuất ra Lari api json và một biểu mẫu để chỉnh sửa chi tiết.

 
<div class="container">
    <div class="row">
        <div class="col-xs-12">

            <h1>{{title}}</h1>

             
            <table class="table table-striped">
                <thead>
                    <tr>
                        <th>ID</th>
                        <th>SKU</th>
                        <th>PRODUCT</th>
                        <th>PRICE</th>
                        <th>QUANTITY</th>
                        <th>OPTIONS</th>
                    </tr>
                </thead>
                <tbody>
                    <tr *ngFor="let product of products">
                        <td>{{ product.id }}</td>
                        <td>{{ product.sku }}</td>
                        <td>{{ product.name }}</td>
                        <td>{{ product.price }}</td>
                        <td>{{ product.quantity }}</td>
                        <td>
                            <button (click)="deleteProduct(product.id)" class="btn btn-danger">Delete</button>
                            <button (click)="showProduct(product.id)" class="btn btn-primary">Edit</button>
                        </td>
                    </tr>
                </tbody>
            </table>

            <hr />

             
            <form [formGroup]="productForm" (ngSubmit)="storeProduct(productForm)">
                <div class="form-group">
                    <label for="sku">SKU</label>
                    <input type="text" class="form-control" name="sku" formControlName="sku">
                </div>
                <div class="form-group">
                    <label for="name">Product Name</label>
                    <input type="text" class="form-control" name="name" formControlName="name">
                </div>
                <div class="form-group">
                    <label for="price">Price</label>
                    <input type="number" class="form-control" name="price" min="0" max="99999" step=".01" formControlName="price">
                </div>
                <div class="form-group">
                    <label for="quantity">Quantity</label>
                    <input type="number" class="form-control" name="quantity" min="1" max="10" formControlName="quantity">
                </div>

                 
                <pre>{{productForm.value | json}}</pre>

                <button type="submit" class="btn btn-primary">Add</button>
                <button type="button" (click)="putProduct(product.id)" class="btn btn-primary">Update</button>

            </form>

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

Mở file '/Applications/MAMP/htdocs/angular-web/src/app/app.component.ts'.

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

@Component({
    selector: 'app-root',
    templateUrl: './app.component.html',
    styleUrls: ['./app.component.css']
})
export class AppComponent {
    title = 'Angular Laravel CRUD App';
}

Build angular thành production.

ng build --prod

And preview in the browser 'http://angular-web.testing'.

Handle the api json data Trước khi migrate database có thể sẽ gặp phải lỗi "Specified key was too long error'". Hãy chỉnh sửa file này để an toàn .

cd /Applications/MAMP/htdocs/laravel-api/app/Providers/

open -e AppServiceProvider.php

Thêm dòng này vào.' Schema::defaultStringLength(191); '.

<?php

namespace AppProviders;

use IlluminateSupportServiceProvider;
use IlluminateSupportFacadesSchema;

class AppServiceProvider extends ServiceProvider
{
    /**
     * Bootstrap any application services.
     *
     * @return void
     */
    public function boot()
    {
        // This is the important bit
        Schema::defaultStringLength(191);
    }

    /**
     * Register any application services.
     *
     * @return void
     */
    public function register()
    {
        //
    }
}

Chính sửa file enviroment cho databsse.

cd /Applications/MAMP/htdocs/laravel-api/

open -e .env

Tạo các biens môi trường. Bạn có thể dùng mặc định của MAMP.

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=angular_laravel_api
DB_USERNAME=root
DB_PASSWORD=root

Tạo model và sample data.

php artisan make:model Product -m

Vào thư mục databse mở file '/Applications/MAMP/htdocs/laravel-api/database/migrations ..._create_products_table' vừa mới được tạo.

<?php

use IlluminateSupportFacadesSchema;
use IlluminateDatabaseSchemaBlueprint;
use IlluminateDatabaseMigrationsMigration;

class CreateProductsTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('products', function (Blueprint $table) {
            $table->increments('id');
            $table->string('sku');
            $table->string('name');
            $table->float('price');
            $table->integer('quantity');
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('products');
    }
}

Thực hiện migrate database.

php artisan migrate

Edit file model '/Applications/MAMP/htdocs/laravel-api/app/Product.php'

<?php

namespace App;

use IlluminateDatabaseEloquentModel;

class Product extends Model
{
    protected $fillable = ['sku', 'name', 'price', 'quantity'];
}

Tạo controller bằng lệnh sau.

// -- resource adds the default methods we will be using in the controller
php artisan make:controller ProductController --resource

Mở file controler vừa được tạo '/Applications/MAMP/htdocs/laravel-api/app/Http/Controllers/ProductController.php'.

<?php

namespace AppHttpControllers;

use AppProduct;
use IlluminateHttpRequest;

class ProductController extends Controller
{

    public function index()
    {
        // List all the products
        return Product::get();
    }

    public function store(Request $request)
    {
        // Create new product
        $product = new Product();
        $product->sku = $request->sku;
        $product->name = $request->name;
        $product->price = $request->price;
        $product->quantity = $request->quantity;
        $product->save();
    }

    public function show($id)
    {
        // Show single product
        return Product::find($id);
    }

    public function update(Request $request, $id)
    {
        // Update the Product
        if ($id != null) {
            Product::where('id', $id)->update($request->all());  
        }
    }

    public function destroy($id)
    {
        // Delete the Product
        if ($id != null) {
            $product = Product::find($id);
            $product->delete();    
        }
    }
}

Và bây giờ chúng ta cần thêm các routes cho api trong file '/Applications/MAMP/htdocs/laravel-api/routes/api.php'.

<?php

use IlluminateHttpRequest;

// Get list of Products
Route::get('products','ProductController@index');

// Get specific Product
Route::get('product/{id}','ProductController@show');

// Delete a Product
Route::delete('product/{id}','ProductController@destroy');

// Update existing Product
Route::put('product/{id}','ProductController@update');

// Create new Product
Route::post('product','ProductController@store');

** Solved 'no access control allow origin header' when testing api calls locally** There was an issue when I started testing locally when all my rest api calls were being blocked with the dreaded 'no access control allow origin header is present on the requested resource' so here is the solution I found. If you are testing via staging then you can skip this bit.

Nếu bạn gặp lỗi 'no access control allow origin header'. Có nghĩa là request tới rest api bị chặn . Để sửa lỗi này có thể làm như sau.

Tạo file sau đây '/Applications/MAMP/htdocs/laravel-api/app/Http/Middleware/Cors.php'.

<?php

namespace AppHttpMiddleware;

use Closure;

class Cors {

    /**
     * Handle an incoming request.
     *
     * @param  IlluminateHttpRequest  $request
     * @param  Closure  $next
     * @return mixed
     */
    public function handle($request, Closure $next) {

        return $next($request)
          ->header('Access-Control-Allow-Origin', '*')
          ->header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS')
          ->header('Access-Control-Allow-Headers',' Origin, Content-Type, Accept, Authorization, X-Request-With')
          ->header('Access-Control-Allow-Credentials',' true');
    }
}

Mở file ' /Applications/MAMP/htdocs/laravel-api/app/Http/Kernel.php ' add the following 'AppHttpMiddlewareCors::class,'.

protected $middleware = [
        IlluminateFoundationHttpMiddlewareCheckForMaintenanceMode::class,
        IlluminateFoundationHttpMiddlewareValidatePostSize::class,
        AppHttpMiddlewareTrimStrings::class,
        IlluminateFoundationHttpMiddlewareConvertEmptyStringsToNull::class,
        AppHttpMiddlewareTrustProxies::class,
        AppHttpMiddlewareCors::class,//cors added here 
    ];

** Call REST api from Angular** Mở file ' /Applications/MAMP/htdocs/angular-web/src/app/app.module.ts ' và thêm code sau;

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { ReactiveFormsModule } from "@angular/forms";
import { HttpClientModule } from '@angular/common/http';
import { Observable } from "rxjs/Observable";
import { AppComponent } from './app.component';

@NgModule({
   declarations: [
       AppComponent
   ],
   imports: [ 
       BrowserModule, 
       ReactiveFormsModule,
       HttpClientModule
   ],
   providers: [AppComponent],
   bootstrap: [AppComponent]
})

export class AppModule { 
}

Chỉnh sửa file '/Applications/MAMP/htdocs/angular-web/src/app/app.component.ts'.

import { Component, OnInit, Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { FormGroup, FormControl, FormArray, NgForm } from '@angular/forms';

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

@Injectable()
export class AppComponent implements OnInit {
    title = 'Angular Laravel CRUD App';

    private productForm: FormGroup;
    product: any;
    products: any;

    constructor(private http: HttpClient) {
        this.getProducts();
    }

    ngOnInit() {
        this.productForm = new FormGroup({
            'sku': new FormControl(),
            'name': new FormControl(),
            'price': new FormControl(),
            'quantity': new FormControl()
        });
    }

    // Add a New Product
    storeProduct(productForm: NgForm) {
        // console.log('Form successful submit.');
        // console.log(productForm.value);

        this.http.post('http://laravel-api.testing/api/product', productForm.value).subscribe(res => {
            this.getProducts();
            productForm.reset();
        }, err => {
            console.log('Error occured');
        });
    }

    getProducts() {
        // console.log('Get Products and Update Table');
        return this.http.get('http://laravel-api.testing/api/products').subscribe(products => {
            this.products = products;
        });
    }

    showProduct(id) {
        console.log('Get Product ' + id);
        return this.http.get('http://laravel-api.testing/api/product/' + id).subscribe(product => {
            this.product = product;
            this.productForm.patchValue({ 
                id: this.product.id,
                sku: this.product.sku,
                name: this.product.name,
                price: this.product.price,
                quantity: this.product.quantity
            });
        });
    }

    deleteProduct(id) {
        console.log('Delete Product id ' + id);

        this.http.delete('http://laravel-api.testing/api/product/' + id).subscribe(res => {
            console.log('Product Deleted and refresh Table');
            this.getProducts();
        }, err => {
            console.log('Error occured');
        });
    }

    putProduct(id) {
        console.log('Update Product id ' + id);

        this.http.put('http://laravel-api.testing/api/product/' + id, this.productForm.value).subscribe(res => {
            console.log('Product Updated and refresh table');
            this.getProducts();
        }, err => {
            console.log('Error occured');
        });

    }
}

Và bây giờ refresh địa chỉ sau trên trình duyệt và cảm nhận http://angular-web.testing:

ng build --prod

Tôi mong là bài viết này sẽ giúp bạn được một phần nào đó. Có góp ý nào xin hãy comment bên dưới.Thank.

0