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. 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 ...
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.
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 AppHttpControllers; use AppTodo; use IlluminateHttpRequest; class TodoController extends Controller { /** * Display a listing of the resource. * * @return IlluminateHttpResponse */ public function index() { $todos = Todo::orderBy('id', 'desc')->get(); return response()->json($todos); } /** * Store a newly created resource in storage. * * @param IlluminateHttpRequest $request * @return IlluminateHttpResponse */ public function store(Request $request) { $todo = Todo::create($request->all()); return response()->json($todo); } /** * Update the specified resource in storage. * * @param IlluminateHttpRequest $request * @param AppTodo $todo * @return IlluminateHttpResponse */ public function update(Request $request, Todo $todo) { $todo->fill($request->all()); $todo->save(); return response()->json($todo); } /** * Remove the specified resource from storage. * * @param AppTodo $todo * @return IlluminateHttpResponse */ 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="awidth=device-awidth, 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