Xây dựng ứng dụng CRUD sử dụng Vue.js trong Laravel (ok)
https://allaravel.com/blog/xay-dung-ung-dung-crud-su-dung-vue-js-trong-laravel/
php artisan make:migration create_products_table --create=products
Created Migration: 2017_04_24_024728_create_products_table
C:\xampp\htdocs\songkhoe\routes\web.php
<?php
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\VueJSProductController;
/*
|--------------------------------------------------------------------------
| Web Routes
|--------------------------------------------------------------------------
|
| Here is where you can register web routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| contains the "web" middleware group. Now create something great!
|
*/
Route::get('/', function () {
return view('welcome');
});
Route::get('/vue-product', [VueJSProductController::class,'showVueProduct']);
Route::resource('vueproduct',VueJSProductController::class);
C:\xampp\htdocs\songkhoe\public\js\product.js
Vue.http.headers.common['X-CSRF-TOKEN'] = $("#token").attr("value");
new Vue({
el: '#manage-vue',
data: {
items: [],
pagination: {
total: 0,
per_page: 2,
from: 1,
to: 0,
current_page: 1
},
offset: 4,
formErrors:{},
formErrorsUpdate:{},
newItem : {'name':'','price':'','content':''},
fillItem : {'name':'','price':'','content':'','id':''}
},
computed: {
isActived: function () {
return this.pagination.current_page;
},
pagesNumber: function () {
if (!this.pagination.to) {
return [];
}
var from = this.pagination.current_page - this.offset;
if (from < 1) {
from = 1;
}
var to = from + (this.offset * 2);
if (to >= this.pagination.last_page) {
to = this.pagination.last_page;
}
var pagesArray = [];
while (from <= to) {
pagesArray.push(from);
from++;
}
return pagesArray;
}
},
ready : function(){
this.getvueproduct(this.pagination.current_page);
},
methods : {
getvueproduct: function(page){
this.$http.get('/vueproduct?page='+page).then((response) => {
if(response.status == 200 && response.statusText == 'OK') {
let dataParse = JSON.parse(response.data);
this.$set('items', dataParse.data.data);
this.$set('pagination', dataParse.pagination);
}
});
},
createItem: function(){
var input = this.newItem;
this.$http.post('/vueproduct',input).then((response) => {
this.changePage(this.pagination.current_page);
this.newItem = {'name':'','price':'','content':''};
$("#create-item").modal('hide');
toastr.success('Item Created Successfully.', 'Success Alert', {timeOut: 5000});
}, (response) => {
this.formErrors = response.data;
});
},
deleteItem: function(item){
this.$http.delete('/vueproduct/'+item.id).then((response) => {
this.changePage(this.pagination.current_page);
toastr.success('Item Deleted Successfully.', 'Success Alert', {timeOut: 5000});
});
},
editItem: function(item){
this.fillItem.name = item.name;
this.fillItem.id = item.id;
this.fillItem.price = item.price;
this.fillItem.content = item.content;
$("#edit-item").modal('show');
},
updateItem: function(id){
var input = this.fillItem;
this.$http.put('/vueproduct/'+id,input).then((response) => {
this.changePage(this.pagination.current_page);
this.fillItem = {'name':'','price':'','content':'','id':''};
$("#edit-item").modal('hide');
toastr.success('Item Updated Successfully.', 'Success Alert', {timeOut: 5000});
}, (response) => {
this.formErrorsUpdate = response.data;
});
},
changePage: function (page) {
this.pagination.current_page = page;
this.getvueproduct(page);
}
}
});
C:\xampp\htdocs\songkhoe\app\Http\Controllers\VueJSProductController.php
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Models\Product;
class VueJSProductController extends Controller
{
/**
* Display a listing of the resource.
*
* @return \Illuminate\Http\Response
*/
public function index()
{
$products = Product::latest()->paginate(5);
$response = [
'pagination' => [
'total' => $products->total(),
'per_page' => $products->perPage(),
'current_page' => $products->currentPage(),
'last_page' => $products->lastPage(),
'from' => $products->firstItem(),
'to' => $products->lastItem()
],
'data' => $products
];
return response()->json($response);
}
/**
* Show the form for creating a new resource.
*
* @return \Illuminate\Http\Response
*/
public function create()
{
//
}
/**
* Store a newly created resource in storage.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function store(Request $request)
{
$this->validate($request, [
'name' => 'required',
'price' => 'required',
'content' => 'required',
]);
$create = Product::create($request->all());
return response()->json($create);
}
/**
* Display the specified resource.
*
* @param int $id
* @return \Illuminate\Http\Response
*/
public function show($id)
{
//
}
/**
* Show the form for editing the specified resource.
*
* @param int $id
* @return \Illuminate\Http\Response
*/
public function edit($id)
{
//
}
/**
* Update the specified resource in storage.
*
* @param \Illuminate\Http\Request $request
* @param int $id
* @return \Illuminate\Http\Response
*/
public function update(Request $request, $id)
{
$this->validate($request, [
'name' => 'required',
'price' => 'required',
'content' => 'required',
]);
$edit = Product::find($id)->update($request->all());
return response()->json($edit);
}
/**
* Remove the specified resource from storage.
*
* @param int $id
* @return \Illuminate\Http\Response
*/
public function destroy($id)
{
Product::find($id)->delete();
return response()->json(['done']);
}
public function showVueProduct(){
return view('frontend.vue-product');
}
}
C:\xampp\htdocs\songkhoe\app\Models\Product.php
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Product extends Model
{
use HasFactory;
protected $fillable = [
"name",
"price",
"content"
];
}
C:\xampp\htdocs\songkhoe\resources\views\frontend\vue-product.blade.php
@extends('layouts.default')
@section('title', 'Referral system - Allaravel.com')
@section('link-header')
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css">
<link href="//cdnjs.cloudflare.com/ajax/libs/toastr.js/latest/css/toastr.min.css" rel="stylesheet">
<meta id="token" name="token" value="{{ csrf_token() }}">
@endsection
@section('content')
<div class="container" id="manage-vue">
<div class="row">
<div class="col-lg-12 margin-tb">
<div class="pull-left">
<h2>Laravel + VueJS Product CRUD</h2>
</div>
<div class="pull-right">
<button type="button" class="btn btn-success" data-toggle="modal" data-target="#create-item">
Tạo sản phẩm mới
</button>
</div>
</div>
</div>
<!-- Item Listing -->
<div class="row">
<div class="col-md-12">
<table class="table table-bordered">
<tr>
<th class="col-md-2">Name</th>
<th class="col-md-1">Price</th>
<th class="col-md-7">Content</th>
<th class="col-md-2">Action</th>
</tr>
<tr v-for="item in items">
<td>@{{ item.name }}</td>
<td>@{{ item.price }}</td>
<td>@{{ item.content }}</td>
<td>
<button class="btn btn-primary" @click.prevent="editItem(item)">Edit</button>
<button class="btn btn-danger" @click.prevent="deleteItem(item)">Delete</button>
</td>
</tr>
</table>
<!-- Pagination -->
<nav>
<ul class="pagination">
<li v-if="pagination.current_page > 1">
<a href="#" aria-label="Previous"
@click.prevent="changePage(pagination.current_page - 1)">
<span aria-hidden="true">«</span>
</a>
</li>
<li v-for="page in pagesNumber"
v-bind:class="[ page == isActived ? 'active' : '']">
<a href="#"
@click.prevent="changePage(page)">@{{ page }}</a>
</li>
<li v-if="pagination.current_page < pagination.last_page">
<a href="#" aria-label="Next"
@click.prevent="changePage(pagination.current_page + 1)">
<span aria-hidden="true">»</span>
</a>
</li>
</ul>
</nav>
</div>
</div>
<!-- Create Item Modal -->
<div class="modal fade" id="create-item" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
<h4 class="modal-title" id="myModalLabel">Tạo sản phẩm mới</h4>
</div>
<div class="modal-body">
<form method="POST" enctype="multipart/form-data" v-on:submit.prevent="createItem">
<div class="form-group">
<label for="name">Name:</label>
<input type="text" name="name" class="form-control" v-model="newItem.name" />
<span v-if="formErrors['name']" class="error text-danger">@{{ formErrors['name'] }}</span>
</div>
<div class="form-group">
<label for="price">Price:</label>
<input type="text" name="price" class="form-control" v-model="newItem.price" />
<span v-if="formErrors['price']" class="error text-danger">@{{ formErrors['price'] }}</span>
</div>
<div class="form-group">
<label for="content">Content:</label>
<textarea name="content" class="form-control" v-model="newItem.content"></textarea>
<span v-if="formErrors['content']" class="error text-danger">@{{ formErrors['content'] }}</span>
</div>
<div class="form-group">
<button type="submit" class="btn btn-success">Tạo sản phẩm</button>
</div>
</form>
</div>
</div>
</div>
</div>
<!-- Edit Item Modal -->
<div class="modal fade" id="edit-item" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
<h4 class="modal-title" id="myModalLabel">Chỉnh sửa sản phẩm</h4>
</div>
<div class="modal-body">
<form method="POST" enctype="multipart/form-data" v-on:submit.prevent="updateItem(fillItem.id)">
<div class="form-group">
<label for="name">Name:</label>
<input type="text" name="name" class="form-control" v-model="fillItem.name" />
<span v-if="formErrorsUpdate['name']" class="error text-danger">@{{ formErrorsUpdate['name'] }}</span>
</div>
<div class="form-group">
<label for="price">Price:</label>
<input type="text" name="price" class="form-control" v-model="fillItem.price" />
<span v-if="formErrorsUpdate['price']" class="error text-danger">@{{ formErrorsUpdate['price'] }}</span>
</div>
<div class="form-group">
<label for="content">Content:</label>
<textarea name="content" class="form-control" v-model="fillItem.content"></textarea>
<span v-if="formErrorsUpdate['content']" class="error text-danger">@{{ formErrorsUpdate['content'] }}</span>
</div>
<div class="form-group">
<button type="submit" class="btn btn-success">Cập nhật sản phẩm</button>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
@endsection
@section('js')
<script type="text/javascript" src="//code.jquery.com/jquery.js"></script>
<script type="text/javascript" src="//maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js"></script>
<script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/toastr.js/latest/js/toastr.min.js"></script>
<script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/vue/1.0.26/vue.min.js"></script>
<script type="text/javascript" src="//cdn.jsdelivr.net/vue.resource/0.9.3/vue-resource.min.js"></script>
<script type="text/javascript" src="{{ Asset('js/product.js') }}"></script>
@endsection
C:\xampp\htdocs\songkhoe\resources\views\layouts\default.blade.php
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
@yield('link-header')
</head>
<body>
@yield('content')
@yield('js')
</body>
</html>
PreviousA simple and easy to use library that creates fullscreen scrolling websites fullPage.js (ok)Next[Chart] Morris.js Line & Area Charts,Bar Charts, Donut Charts, graph (ok)
Last updated