Skip to content

Instantly share code, notes, and snippets.

@boynoiz
Last active April 29, 2018 08:37
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save boynoiz/fb9556f74a32127a5f8f263bac9bab6b to your computer and use it in GitHub Desktop.
Save boynoiz/fb9556f74a32127a5f8f263bac9bab6b to your computer and use it in GitHub Desktop.
My User DataTable Snippet (BoostrapVue + VueJS + Vee-validate + Ziggy + Axios on Top Laravel API)
<template>
<!-- Main table element -->
<div class="main-content">
<div class="card">
<header class="card-header">
<h4 class="card-title">
<strong>User</strong> List
</h4>
<div class="card-header-actions">
<div class="btn-group btn-group-sm">
<button class="btn" title="Refresh" @click.stop="refreshTable">
<i class="fa fa-refresh"></i>
</button>
<button class="btn" title="Add new"
@click.stop="openModalForm('create', $event.target)"><i
class="fa fa-plus"></i></button>
</div>
<div class="btn-group btn-group-sm ml-2 d-none d-sm-flex">
<button class="btn dropdown-toggle">Export</button>
<div class="dropdown-menu dropdown-menu-right">
<a class="dropdown-item" href="#">CSV</a>
<a class="dropdown-item" href="#">SQL</a>
<a class="dropdown-item" href="#">PDF</a>
<a class="dropdown-item" href="#">Text</a>
</div>
</div>
</div>
</header>
<div class="card-body">
<div class="flexbox mb-20">
<div class="lookup lookup-sm d-none d-lg-block">
<b-form-input id="field_search"
v-model="filter"
type="text"
placeholder="Search">
</b-form-input>
</div>
<div class="btn-toolbar">
<div>
<b-form-select id="field_perpage_select" class="form-control-sm" :options="pageOptions" v-model="meta.per_page"/>
</div>
</div>
</div>
<b-table id="userTable" striped hover
ref="userTable"
:busy.sync="isBusy"
:items="!filtered ? listAllUsers : searchUser"
:fields="fields"
:current-page="meta.current_page"
:per-page="meta.per_page"
:filter="filtered"
>
<template slot="profile" slot-scope="row">
{{ row.value ? row.value.company : '' }}
</template>
<template slot="is_activated" slot-scope="row">
<span :class="row.value ? 'badge badge-lg badge-pill badge-success bg-success' : 'badge badge-lg badge-pill badge-danger bg-danger'">
<i :class="row.value ? 'fa fa-lg fa-check-circle' : 'fa fa-lg fa-times-circle'"></i>
<template> {{ row.value ? 'Yes' : 'No' }}</template>
</span>
</template>
<template slot="actions" slot-scope="row">
<!-- We use click.stop here to prevent a 'row-clicked' event from also happening -->
<button class="btn btn-w-md btn-round btn-primary"
@click.stop="openModalForm('show', $event.target, row.item)"><i
class="fa fa-search-plus"></i> Details
</button>
</template>
</b-table>
<b-pagination align="center"
:total-rows="meta.total"
:per-page="meta.per_page"
v-model="meta.current_page"/>
<!-- Open modal -->
<!-- Start create modal -->
<b-modal id="createUserForm" size="lg" :ok-disabled="errors.any()" @ok="createUser()"
@hidden="hideModalForm('createUserForm')">
<div class="card">
<header class="card-header no-border">
<h4 class="card-title"><strong>Create </strong>new user</h4>
</header>
<div class="card-body">
<b-form-group horizontal id="field_c_username" label="Username"
:label-cols="2"
:feedback="errors.has('c_username') ? errors.first('c_username') : null"
:state="errors.has('c_username') ? 'invalid' : 'valid'">
<b-input-group>
<b-input name="c_username" id="c_username"
v-model="userDetails.username"
type="text"
:state="errors.has('c_username') ? 'invalid' : 'valid'"
v-validate.initial="{
rules: {
UsernameUnique: true,
regex: /^[a-zA-Z0-9.\-_]{3,30}$/,
required: true
}
}">
</b-input>
</b-input-group>
</b-form-group>
<b-form-group horizontal id="field_c_password" label="Password"
:label-cols="2"
:feedback="errors.first('c_password')">
<b-form-input name="c_password" id="c_password"
v-model="userDetails.password"
type="password"
:state="!errors.has('c_password')"
v-validate.initial="{
rules: {
regex: /^(?=.*[a-zA-Z$*_^-])(?=.*\d).+$/,
required: true,
min: 6
}
}">
</b-form-input>
</b-form-group>
<b-form-group horizontal id="field_c_password_confirmation" label="Password Confirm"
:label-cols="2"
:feedback="errors.first('c_password_confirmation')">
<b-form-input name="c_password_confirmation" id="c_password_confirmation"
v-model="userDetails.password_confirmation" type="password"
:state="!errors.has('c_password_confirmation')"
v-validate.initial="'required|confirmed:c_password'">
</b-form-input>
</b-form-group>
<b-form-group horizontal id="field_c_email" label="Email"
:label-cols="2"
:feedback="errors.first('c_email')">
<b-form-input name="c_email" id="c_email"
v-model="userDetails.email" type="text"
:state="!errors.has('c_email')"
v-validate.initial="'required|email|EmailUnique'">
</b-form-input>
</b-form-group>
<b-form-group horizontal id="field_c_company" label="Company"
:label-cols="2"
:feedback="errors.first('c_company')">
<b-form-input name="c_company" id="c_company"
v-model="userDetails.company"
type="text"
:state="!errors.has('c_company')"
v-validate.initial="'required'">
</b-form-input>
</b-form-group>
<b-form-group horizontal id="field_c_location" label="Location"
:label-cols="2"
:feedback="errors.first('c_location')">
<b-form-input name="c_location" id="c_location"
v-model="userDetails.location"
type="text"
:state="!errors.has('c_location')"
v-validate.initial="{
rules: {
regex:/^[a-zA-Z\d\-_\s]+$/
}
}">
</b-form-input>
</b-form-group>
</div>
</div>
</b-modal>
<!-- End create modal -->
<b-modal id="showUserForm" size="lg"
:title="'User detail: ' + userDetails.username"
@ok="editFormToggle ? updateUser():null"
@hidden="hideModalForm('showUserForm')">
<label class="switch switch-lg">
<input type="checkbox" v-model="editFormToggle">
<span class="switch-indicator"></span>
<span class="switch-description">Edit</span>
</label>
<b-form-group id="field_s_username" horizontal label="Username">
<b-form-input name="s_username" id="s_username" v-model.lazy="userDetails.username"
type="text" plaintext>
</b-form-input>
</b-form-group>
<b-form-group id="field_s_password" horizontal label="Password"
:state="errors.has('s_password')"
:feedback="errors.first('s_password')">
<b-form-input name="s_password" id="s_password" v-model.lazy="userDetails.password"
type="password"
:plaintext="!editFormToggle"
v-validate.initial="{
rules: {
regex: /^(?=.*[a-zA-Z$*_^-])(?=.*\d).+$/,
required: true,
min: 6
}
}"
:class="{'input': true, 'is-invalid': errors.has('s_password') }">
</b-form-input>
</b-form-group>
<b-form-group id="field_s_password_confirmation" horizontal label="Password Confirm"
:state="errors.has('s_password_confirmation')"
:feedback="errors.first('s_password_confirmation')">
<b-form-input name="s_password_confirmation" id="s_password_confirmation"
v-model.lazy="userDetails.password_confirmation" type="password"
:plaintext="!editFormToggle"
v-validate.initial="'confirmed:s_password'"
:class="{'input': true, 'is-invalid': errors.has('s_password_confirmation') }">
</b-form-input>
</b-form-group>
<b-form-group id="field_s_email" horizontal label="Email"
:state="errors.has('s_email')"
:feedback="errors.first('s_email')">
<b-form-input name="s_email" id="s_email" v-model.lazy="userDetails.email" type="text"
:plaintext="!editFormToggle"
v-validate.initial="'required|email'"
:class="{'input': true, 'is-invalid': errors.has('s_email') }">
</b-form-input>
</b-form-group>
<b-form-group id="field_s_company" horizontal label="Company"
:state="errors.has('s_company')"
:feedback="errors.first('s_company')">
<b-form-input name="s_company" id="s_company" v-model.lazy="userDetails.company" type="text"
:plaintext="!editFormToggle"
v-validate.initial="'required'"
:class="{'input': true, 'is-invalid': errors.has('s_company') }">
</b-form-input>
</b-form-group>
<b-form-group id="field_s_location" horizontal label="Location">
<b-form-input name="s_location" id="s_location" v-model.lazy="userDetails.location"
type="text"
:plaintext="!editFormToggle">
</b-form-input>
</b-form-group>
</b-modal>
<!-- End modal -->
</div>
<div v-show="isBusy" class="card-loading reveal">
<svg class="spinner-circle-material-svg" viewBox="0 0 50 50">
<circle class="circle" cx="25" cy="25" r="20"></circle>
</svg>
</div>
</div>
</div>
</template>
<script>
import axios from 'axios'
import Vue from 'vue'
import BootstrapVue from 'bootstrap-vue'
// import BootstrapVue from 'bootstrap-vue/dist/bootstrap-vue.esm'
import VeeValidate from 'vee-validate'
import {Validator} from 'vee-validate';
// Config for VeeValidate
const config = {
errorBagName: 'errors', // change if property conflicts.
fieldsBagName: 'fields ', // Default is fields
delay: 0,
locale: 'en',
dictionary: {
en: {
attributes: {
c_username: 'Username',
c_password: 'Password',
c_password_confirmation: 'Password Confirm',
c_email: 'Email Address',
c_company: 'Company Name',
c_location: 'Location',
s_username: 'Username',
s_password: 'Password',
s_password_confirmation: 'Password Confirm',
s_email: 'Email Address',
s_company: 'Company Name',
}
}
},
strict: true,
classes: false,
classNames: {
touched: 'touched', // the control has been blurred
untouched: 'untouched', // the control hasn't been blurred
valid: 'valid', // model is valid
invalid: 'invalid', // model is invalid
pristine: 'pristine', // control has not been interacted with
dirty: 'dirty' // control has been interacted with
},
events: 'input|blur',
inject: true,
validity: false,
aria: true
};
Vue.use(VeeValidate, config);
Vue.use(BootstrapVue);
export default {
name: 'user-table',
data: () => ({
items: {},
links: {},
meta: {
current_page: 1,
from: null,
last_page: null,
path: '',
per_page: 15,
to: null,
total: null
},
fields: [
{key: 'username', label: 'Username', sortable: true, 'class': 'text-center'},
{key: 'email', label: 'Email', sortable: true, 'class': 'text-center'},
{key: 'profile', label: 'Company Name', sortable: true, 'class': 'text-center'},
{key: 'is_activated', label: 'Activated?', 'class': 'text-center'},
{key: 'updated_at', label: 'Updated at', 'class': 'text-center'},
{key: 'actions', label: 'Actions', 'class': 'text-center'}
],
userDetails: {
id: null,
username: '',
password: '',
password_confirmation: '',
email: '',
company: '',
location: '',
is_activated: ''
},
methodForm: '',
editFormToggle: false,
isBusy: false,
pageOptions: [
{value: 15, text: 'Per page : 15 '},
{value: 30, text: ' --- 30 '},
{value: 60, text: ' --- 60 '},
{value: 90, text: ' --- 90 '},
{value: 120, text: ' --- 120 '}
],
sortBy: '',
sortDesc: false,
filter: null,
filtered: null,
searchTyping: false
}),
// Validate method via backend api
created() {
// Validate user exist
const isUsernameUnique = async (value) => {
try {
const promise = await axios.post(route('internal.api.user.validate'), {username: value});
const data = await promise.data;
return {
valid: data.valid,
data: {
message: data.message
}
};
} catch (e) {
console.log('Calling the API has been an error: ', e);
return []
}
};
Validator.extend('UsernameUnique', {
validate: isUsernameUnique,
getMessage: (field, params, data) => {
return data.message;
}
});
// Validate email exist
const isEmailUnique = async (value) => {
try {
const promise = await axios.post(route('internal.api.email.validate'), {email: value});
const data = await promise.data;
return {
valid: data.valid,
data: {
message: data.message
}
}
} catch (e) {
console.log('Calling api has been an error: ', e);
}
};
Validator.extend('EmailUnique', {
validate: isEmailUnique,
getMessage: (field, params, data) => {
return data.message;
}
});
},
computed: {},
watch: {
filter: _.debounce(function () {
this.filtered = this.filter;
console.log('Filtered!!')
}, 1000)
},
methods: {
// List all user
async listAllUsers() {
try {
this.isBusy = true;
const promise = await axios.get(route('internal.api.user.all') + "?page=" + this.meta.current_page + "&perPage=" + this.meta.per_page);
const data = await promise.data;
console.log(data);
this.items = data.data;
this.links = data.links;
this.meta = data.meta;
this.isBusy = false;
this.refreshTable();
return (data.data || []);
} catch (e) {
console.log('calling api error: ', e);
return []
}
},
// Search user
async searchUser() {
try {
this.isBusy = true;
const promise = await axios.get(route('internal.api.user.search') + "?q=" + this.filter);
const data = await promise.data;
console.log(data);
this.items = data.data;
this.links = data.links;
this.meta = data.meta;
this.refreshTable();
this.isBusy = false;
return (data.data || [])
} catch (e) {
console.log('calling api error: ', e);
return []
}
},
// Create a new user via api
async createUser() {
try {
this.isBusy = true;
const promise = await axios.post(route('internal.api.user.create'), this.userDetails);
const data = await promise.data;
console.log(data);
this.isBusy = false;
this.refreshTable();
return (data.data || [])
} catch (e) {
console.log('calling api error: ', e);
return []
}
},
// Update the user via api
async updateUser() {
try {
this.isBusy = true;
const promise = await axios.post(route('internal.api.user.update', {id: this.userDetails.id}), {
password: this.userDetails.password,
password_confirmation: this.userDetails.password_confirmation,
email: this.userDetails.email,
company: this.userDetails.company,
location: this.userDetails.location
});
const data = await promise.data;
console.log(this.userDetails);
console.log(data);
this.isBusy = false;
this.refreshTable();
return (data.data || [])
} catch (e) {
console.log('calling api error: ', e);
return []
}
},
openModalForm(method, button, item = null) {
if (method == 'create') {
this.methodForm = 'create';
this.$root.$emit('bv::show::modal', 'createUserForm', button)
}
if (method == 'show') {
this.methodForm = 'show';
this.userDetails = item;
this.$root.$emit('bv::show::modal', 'showUserForm', button)
}
},
hideModalForm(modal) {
this.userDetails = [];
this.editFormToggle = false;
this.refreshTable();
this.$root.$emit('bv::hide::modal', modal)
},
refreshTable() {
this.$refs.userTable.refresh()
}
}
}
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment