+14

Cùng nhau học VueX(Phần 2)

Xin chào các bạn, trước mình là viết một bài giới thiệu về VueX, hôm nay mình sẽ tiếp tục làm ví dụ đơn giản làm App Todo cho Vue + VueX với RESTful sử dụng Laravel.

Nội dung

1. Khởi tạo server với Laravel

Bài này mình sẽ dùng Laravel để làm server, bạn config đầy đủ cho Laravel chạy được, và kết nối đến mysql, chúng ta sẽ dùng mysql để lưu trữ dữ liệu cho ứng dụng. Trước tiên ta sẽ tạo bảng "todos". Bạn chạy lệnh sau để tạo Model và tạo luôn migration.

php artisan make:model Todo -m

Bạn vào sửa migration, ứng dụng này đơn giản ta chỉ cần cấu trúc bảng đơn giản như sau

Schema::create('todos', function (Blueprint $table) {
    $table->increments('id');
    $table->string('name');
    $table->tinyInteger('status')->default(0);
});

Và giờ bạn chạy php artisan migrate tạo bảng ta vừa tạo ở trên. Giờ ta sẽ tạo RESTful cho Model ở trên, với laravel thì ta có thể tạo đơn giản với câu lệnh

php artisan make:controller TodoController --resource -m Todo

Với câu lệnh trên ta đã tạo một resouce controller, và cấu hình cho controller này với Model Todo. Giờ bạn mở file routes/web.php và thêm vào một dòng sau:

Route::resource('todos', 'TodoController')->only(['index', 'store', 'update', 'destroy']);

Ở route trên ta đã gọi đến resource controller, mặc định sẽ tạo ra 7 phương thức, nhưng mà ta chỉ cần 4 phương thức nên ta thêm thuộc tính only và kèm theo 4 phương thức mà ta cần sử dụng. Giờ ta bắt đầu vào xử lý phần controller, trước tiên bạn sẽ cấu hình Model một chút, ta sẽ thêm fillable để laravel sẽ điền data cho những field này khi ta xử lý model. Bạn vào sửa file app/Todo.php, thêm vào dòng sau

protected $fillable = ['name', 'status'];
public $timestamps = false;

Và giờ bạn vào file 'app/Http/Controllers/TodoController.php` và viết phần xử lý cho ứng dụng.

<?php
namespace App\Http\Controllers;

use App\Todo;
use Illuminate\Http\Request;

class TodoController extends Controller
{
    /**
     * Display a listing of the resource.
     *
     * @return \Illuminate\Http\Response
     */
    public function index()
    {
        $todos = Todo::orderBy('id', 'desc')->get();

        return response()->json($todos);
    }

    /**
     * Store a newly created resource in storage.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Http\Response
     */
    public function store(Request $request)
    {
        $todo = Todo::create($request->all());

        return response()->json($todo);
    }

    /**
     * Update the specified resource in storage.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \App\Todo  $todo
     * @return \Illuminate\Http\Response
     */
    public function update(Request $request, Todo $todo)
    {
        $todo->fill($request->all());
        $todo->save();

        return response()->json($todo);
    }

    /**
     * Remove the specified resource from storage.
     *
     * @param  \App\Todo  $todo
     * @return \Illuminate\Http\Response
     */
    public function destroy(Todo $todo)
    {
        $todo->delete();

        return response()->json(['message' => 'Delete successfully!']);
    }
}

Như thế là ta đã xử lý xong xử lý dữ liệu trên server. Và giờ đến phần chính trong bài viết này, ta sẽ dùng Vue + VueX để làm ứng dụng

2. Client sử dụng Vue + Vuex

Bạn sủa file package.json phần devDependencies với các package như thế này

"devDependencies": {
   "axios": "^0.17",
    "bootstrap": "^4.0.0",
    "cross-env": "^5.1",
    "jquery": "^3.2",
    "laravel-mix": "^2.0",
    "lodash": "^4.17.4",
    "popper.js": "^1.12",
    "vue": "^2.5.7",
    "css-loader": "^0.28.10",
    "font-awesome": "^4.7.0",
    "vuex": "^3.0.1"
}

Và bạn chạy yarn install hoặc npm install để cài các package ở trên cho ứng dụng. Bạn mở file webpack.mix.js và xóa 2 dòng cuối file đi, ta thêm vào dòng này

mix.js('resources/assets/todos/index.js', 'public/js/todos.js')

Bạn vào routes/web.php và thêm vào dòng sau

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

Tương ứng ta sẽ tạo file blade cho route ở trên, bạn tạo file resources/views/todo.blade.php

<!doctype html>
<html lang="{{ app()->getLocale() }}">
    <head>
        <meta charset="utf-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <meta name="csrf-token" content="{{ csrf_token() }}">
        <title>Todos</title>
    </head>
    <body>
        <div id="root"></div>
        <script defer type="text/javascript" src="{{ mix('/js/todos.js') }}"></script>
    </body>
</html>

Bây giờ bạn truy cập vào ứng dụng với route /todo mà ra trang trắng là đã thành công rồi đó. Tiếp tục giờ ta sẽ config cho Vue và Vuex. Trước tiên là file gốc để chạy ứng dụng, các bạn thêm thêm mới file resources/assets/todos/index.js với nội dung như sau:

import Vue from 'vue'
import Vuex from 'vuex'
import Todos from './components/Todos.vue'

import storeTodo from './store/todos'

Vue.use(Vuex)

//config for axios
window.axios = require('axios')
window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest'
let token = document.head.querySelector('meta[name="csrf-token"]')
if (token) {
    window.axios.defaults.headers.common['X-CSRF-TOKEN'] = token.content
}

const store = new Vuex.Store({
    modules: {
        storeTodo,
    }
})

new Vue({
    store,
    template: '<Todos/>',
    components: { Todos }
}).$mount('#root')

Mình có dùng axios để call api trên server nên mình đã cấu hình cho axios. Bây giờ ta sẽ tạo file store cho ứng dụng, đây là nơi lưu trữ dữ liệu cho ứng dụng, bạn thêm file sau resources/assets/todos/store/todos.js với nội dung này:

import { apiFetchTodos, apiAddTodo, apiEditTodo, apiDeleteTodo } from '../api/todos'

const TODO_FETCH = 'todo_fetch'
const TODO_ADD = 'todo_add'
const TODO_TOGGLE_STATUS = 'todo_toggle_status'
const TODO_DELETE = 'todo_delete'

const state = {
    todos: []
}

const mutations = {
    [TODO_FETCH](state, todos) {
        return state.todos = todos
    },

    [TODO_ADD](state, todo) {
        return state.todos = [todo, ...state.todos]
    },

    [TODO_TOGGLE_STATUS](state, id) {
        return state.todos = state.todos.map((todo) => todo.id === id ? { ...todo, status: !todo.status } : todo)
    },

    [TODO_DELETE](state, id) {
        return state.todos = state.todos.filter((todo) => todo.id !== id)
    }
}

const actions = {
    async actionTodoFetch({ commit }) {
        let response = await apiFetchTodos()

        if (response.status == 200 ) {
            return commit(TODO_FETCH, response.data)
        }
    },

    async actionTodoAdd({ commit }, todo) {
        let response = await apiAddTodo(todo)

        if (response.status == 200) {
            return commit(TODO_ADD, response.data)
        }
    },

    async actionTodoChangeStatus({ commit }, { id, status }) {
        let response = await apiEditTodo(id, { status })

        if (response.status == 200) {
            return commit(TODO_TOGGLE_STATUS, id)
        }
    },

    async actionTodoDelete({ commit }, id) {
        let response = await apiDeleteTodo(id)

        if (response.status == 200) {
            return commit(TODO_DELETE, id)
        }
    }
}

export default {
    state,
    actions,
    mutations
}

Store bạn có thể tách riêng từng phần state, actions, mutations ra từng file riêng, tuy nhiên theo mình thì bạn nên gộp cả vào 1 file sẽ tiện theo dõi hơn. Bạn có thể tách ra và import vào nếu ứng dụng của bạn quá lớn. Tiếp theo mình sẽ tạo các api để gửi các request lên server, bạn tạo file resources/assets/todos/api/todos.js với nội dung:

export function apiFetchTodos() {
    return axios.get('/todos')
        .then(response => response)
        .catch(error => error)
}

export function apiAddTodo(params) {
    return axios.post('/todos', params)
        .then(response => response)
        .catch(error => error)
}

export function apiEditTodo(id, params) {
    return axios.put(`/todos/${id}`, params)
        .then(response => response)
        .catch(error => error)
}

export function apiDeleteTodo(id) {
    return axios.delete(`/todos/${id}`)
        .then(response => response)
        .catch(error => error)
}

Tất nhiên ta cần tạo component để hiển thị dữ liệu ra cho người dùng, bạn tạo file resources/assets/todos/components/Todos.vue với nội dung như theo đường dẫn này Component Todo và scss cho component này luôn Todo SCSS. Như thế là chúng ta đã viết xong ứng dụng Todo đơn giản sử dụng api.

Bước cuối cùng bạn chạy lệnh và mở ứng dụng với route /todo để xem thành quả.

yarn run prod #or npm run prod

Đến đây bài viết cũng khá dài, cảm ơn sự theo dõi của bạn. Nếu bạn có bất kì thắc mắc hay góp ý nào vui lòng comment ở dưới. Rất vui nếu bài này giúp ích được cho bạn.

Đây là source code nếu bạn cần Source code


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí