Vuetify group of checkboxes returns all true - vue.js

I have an issue with all my checkboxes always being true.
I've tried using the "false-value" attribute, but to no help.
I also have a default input checkbox, which is functioning properly.
export default {
data() {
return {
straps: [],
checkedColors: [],
checkedSkins: [],
checkedTypes: [],
filterings: [{
title: "Farver",
filters: [{
title: "Grøn",
value: "grøn",
model: "checkedColors"
},
{
title: "Rød",
value: "rød",
model: "checkedColors"
},
{
title: "Gul",
value: "yellow",
model: "checkedColors"
},
{
title: "Lilla",
value: "lilla",
model: "checkedColors"
},
{
title: "Blå",
value: "blå",
model: "checkedColors"
},
{
title: "Grå",
value: "grå",
model: "checkedColors"
},
{
title: "Sort",
value: "sort",
model: "checkedColors"
},
{
title: "Hvid",
value: "hvid",
model: "checkedColors"
},
{
title: "Brun",
value: "brun",
model: "checkedColors"
}
]
},
{
title: "Materialer",
filters: [{
title: "Alligator",
value: "alligator",
model: "checkedSkins"
},
{
title: "Struds",
value: "ostridge",
model: "checkedSkins"
},
{
title: "Teju firben",
value: "teju",
model: "checkedSkins"
},
{
title: "Haj",
value: "shark",
model: "checkedSkins"
}
]
},
{
title: "Remme til",
filters: [{
title: "Universal",
value: "universal",
model: "checkedTypes"
},
{
title: "Audemars Piguet",
value: "ap",
model: "checkedTypes"
},
{
title: "Jaeger LeCoultre",
value: "jlc",
model: "checkedTypes"
},
{
title: "Rolex",
value: "rolex",
model: "checkedTypes"
}
]
}
]
};
},
computed: {
filteredStraps() {
var straps = this.straps;
if (this.search !== null) {
var straps = this.searchItems.filter(strap => {
if (!this.search) return this.searchItems;
return (
strap.title.toLowerCase().includes(this.search.toLowerCase()) ||
strap.skin.toLowerCase().includes(this.search.toLowerCase()) ||
strap.type.toLowerCase().includes(this.search.toLowerCase())
);
});
}
if (this.checkedSkins.length > 0) {
straps = straps.filter(strap => {
return this.checkedSkins.includes(strap.skin.toLowerCase());
});
}
if (this.checkedTypes.length > 0) {
straps = straps.filter(strap => {
return this.checkedTypes.includes(strap.type.toLowerCase());
});
}
if (this.sort == "newest") {
return straps.sort((a, b) => new Date(a.date) - new Date(b.date));
}
if (this.sort == "priceasc") {
return straps.sort((a, b) => a.price > b.price);
}
if (this.sort == "pricedesc") {
return straps.sort((a, b) => a.price < b.price);
} else {
return straps;
}
},
getStraps() {
db.collection("straps")
.get()
.then(querySnapshot => {
const straps = [];
querySnapshot.forEach(doc => {
const data = {
id: doc.id,
title:
doc
.data()
.type.charAt(0)
.toUpperCase() +
doc.data().type.slice(1) +
" RIOS1931 " +
doc
.data()
.title.charAt(0)
.toUpperCase() +
doc.data().title.slice(1) +
" Urrem i " +
doc
.data()
.skin.charAt(0)
.toUpperCase() +
doc.data().skin.slice(1),
price: doc.data().price,
skin: doc.data().skin,
type: doc.data().type,
imgs: doc.data().imgs[0].url,
colors: doc.data().colors,
date: doc
.data()
.date.toString()
.slice(0, 15)
};
straps.push(data);
});
this.straps = straps;
});
},
}
<v-layout>
<v-flex sm3 md2 class="hidden-xs-only text-xs-left">
<p class="pl-4"><strong>Sortering</strong></p>
<v-expansion-panel class="elevation-0">
<v-expansion-panel-content v-for="filtering in filterings" :key="filtering.title">
<div slot="header">{{filtering.title | capitalize}}</div>
<v-card>
<v-card-text>
<v-list>
<input type="checkbox" value="alligator" v-model="checkedSkins">
<label for="checker"></label>
<v-list-tile v-for="filter in filtering.filters" :key="filter.value">
<v-list-tile-content>
<v-checkbox :input-value="filter.value" :label="filter.title" v-model="filter.model" color="primary"></v-checkbox>
</v-list-tile-content>
</v-list-tile>
</v-list>
</v-card-text>
</v-card>
</v-expansion-panel-content>
<v-expansion-panel-content>
<div slot="header">Pris</div>
<v-card>
<v-card-text>
<v-layout>
<v-flex px-2>
<v-range-slider :min="slider[0]" :max="slider[1]" v-model="slider" thumb-label="always"></v-range-slider>
</v-flex>
</v-layout>
<v-layout>
<v-flex xs6 pr-2>
<v-text-field label="Fra pris" v-model="slider[0]" class="mt-0" hide-details single-line type="number"></v-text-field>
</v-flex>
<v-flex xs6 pl-2>
<v-text-field label="Til pris" v-model="slider[1]" class="mt-0" hide-details single-line type="number"></v-text-field>
</v-flex>
</v-layout>
</v-card-text>
</v-card>
</v-expansion-panel-content>
</v-expansion-panel>
</v-flex>
</v-layout>
As mentioned the default input works as intended, but the vuetify checkboxes are all returning true for some reason, and they won't work, even though they have the same attribute values in the front-end.

If you want to store checked objects as strings from filter.value property so you have 2 issues in your code(second one is related to your question):
You have incorrect value in your v-model directive. You bind filter.model variable to v-model not its stored array name, to fix this you should pass to v-model something like this $data[filter.model] to bind array from data as model dynamically.
You use input-value binding incorrectly. input-value is related to v-model value(see v-checkbox source code, it's overriding of default model), you don't need to change this value. So you need to pass filter.value to value attribute instead.
Result:
<v-checkbox :value="filter.value" :label="filter.title" v-model="$data[filter.model]" color="primary"></v-checkbox>

Related

Can't get ID to do delete method (VueX,VueJS)

I follow this guy to do my task:"Call an API to delete items"
https://www.youtube.com/watch?v=cjRst4qduzM&t=967s, start at 12:35.
According this guy said, If you wanna implement delete method there are 2 steps: Firstly, you delete items from state by get ID then use filter method to filter the id you selected.(but when you reload page item still available).Secondly, you call an api to server to delete item in server.
I get in stuck in step one, I can't get ID to delete though before I can select ID to choose company.
This is my component
<div>
<v-data-table
v-model="selected"
:search="search"
:headers="headers"
:items="items"
show-select
hide-default-footer
disable-pagination
class="elevation-1"
>
<template v-slot:item.actions="{ item }">
<v-menu offset-y>
<template v-slot:activator="{ on, attrs }">
<v-btn class="ma-2" v-bind="attrs" v-on="on" icon>
<v-icon>mdi-dots-vertical</v-icon>
</v-btn>
</template>
<v-list>
<v-list-item #click="editCompany(item)">
<span>Edit company</span></v-list-item
>
<v-list-item #click="deleteCompany()">
<span style="color: #e12d39"> Delete company</span>
</v-list-item>
</v-list>
</v-menu>
</template>
</v-data-table>
</div>
</template>
<script>
import { mapGetters } from "vuex";
export default {
props: {
items: {
type: Array,
},
search: { type: String },
},
data() {
return {
headers: [
{ text: "Name", align: "start", value: "name" },
{ text: "Phone Number", value: "phoneNumber" },
{ text: "Website", value: "website" },
{ text: "Address", value: "address" },
{ text: "Currency", value: "currency" },
{ text: "Image Name", value: "imageName" },
{ text: "Actions", value: "actions", sortable: false },
],
};
},
computed: {
...mapGetters({ deletedCompany: "companies/deletedCompany" }),
},
methods: {
deleteCompany(delID) {
console.log("dispatch:", delID);
this.$store.dispatch("companies/deleteCompany", delID);
},
},
};
</script>
and this is my store
import NProgress from "nprogress";
export const namespaced = true;
import Vue from "vue";
import Vuex from "vuex";
import { create } from "#/http/companies";
import VueToast from "vue-toast-notification";
import "vue-toast-notification/dist/theme-sugar.css";
import { getCompanies } from "#/http/companies";
// import { deleteCompanies } from "#/http/companies";
Vue.use(Vuex);
Vue.use(VueToast);
export const state = {
companies: [],
selectedCompanyId: "",
};
export const getters = {
allCompanies: (state) => state.companies,
selectedCompanyId: (state) => state.selectedCompanyId,
deletedCompanyId: (state) => state.deletedCompanyId,
selectedCompany: (state) => state.companies.find((c) => c.id === state.selectedCompanyId),
deletedCompany: (state) => state.companies.filter((c) => c.id != state.deletedCompanyId)
};
export const mutations = {
GET_COMPANIES(state, companies) {
state.companies = companies;
},
DELETE_COMPANIES(state, deletedCompanyId) {
console.log("mutations:", deletedCompanyId)
state.deletedCompanyId = deletedCompanyId;
},
SET_SELECTED_COMPANY_ID(state, selectedCompanyId) {
console.log(selectedCompanyId)
state.selectedCompanyId = selectedCompanyId
},
STORE_ID(state, payload) {
state.routeId = payload;
},
};
export const actions = {
storeID({ commit }, payload) {
commit("STORE_ID", payload);
},
getCompanies({ commit }) {
return getCompanies(NProgress.start()).then((response) => {
commit("GET_COMPANIES", response.data);
NProgress.done();
});
},
selectCompany({ commit }, companyId) {
commit("SET_SELECTED_COMPANY_ID", companyId, NProgress.start());
console.log("đây là id", companyId)
NProgress.done();
},
deleteCompany({ commit }, delId) {
console.log("actions:", delId)
return commit("DELETE_COMPANIES", delId);
},
registerCompany({ commit }, companyInfor) {
return create(companyInfor)
.then(() => {
Vue.$toast.open({
message: "Create company successfully!",
type: "success",
duration: 3000,
dismissible: true,
position: "top-right",
});
})
.catch((error) => {
commit("");
Vue.$toast.open({
message: error,
type: "error",
duration: 3000,
dismissible: true,
position: "top-right",
});
});
},
};
https://www.loom.com/share/1eb12a448aca41df8e4c77cdc8931002?focus_title=1&muted=1&from_recorder=1
pass the id via the click event handler <v-list-item #click="deleteCompany(item.id)">:
methods: {
deleteCompany(delID) {
console.log("dispatch:", delID);
this.$store.dispatch("companies/deleteCompany", delID);
// or this.$store.dispatch("companies/deleteCompany", delID);
},
},
you forgot to pass parameter as item_id to function
<v-list-item #click="deleteCompany(item)">
perhaps this will do the trick

undefined Object when i want to dispaly the Recipes

This is a project in order to display the recipes and perform many operations on these recipes such as deleting, adding and modifying the same as for presenting recipes
But when viewing the recipes I have many problems, including:
[Vue warn]: Error in render: "TypeError: Cannot read
property 'title' of undefined"
TypeError: Cannot read property 'title' of undefined
How can i solve the Problem?
Through this file, the recipes are presented in addition to calling the functions in the store.
Recipe:
<template>
<v-container>
<v-layout row wrap v-if="loading">
<v-flex xs12 class="text-xs-center">
<v-progress-circular
indeterminate
class="primary--text"
:width="7"
:size="70"
>
</v-progress-circular>
</v-flex>
</v-layout>
<v-layout row wrap v-else>
<v-flex x12>
<v-card>
<!-- <v-card-title> -->
<v-card-text>
<h4 class="btn-style mt-4 mb-4 font">
{{ recipe.title }}
</h4>
<v-spacer></v-spacer>
<app-edit-recipe-details :recipe="recipe"></app-edit-recipe-details>
<v-img height="530px" :src="recipe.imageUrl" class="mb-4"></v-img>
<div class="btn-style mb-6">
{{ recipe.description }}
</div>
<div v-for="ing in recipe.ingredients" :key="ing.id">
{{ ing.Name }} {{ ing.Quantity }}
<v-btn class="green darken-1 color mb-5 ml-4 mr-4 pl-50">
<v-icon class="green darken-1 btn-style">mdi-plus</v-icon>
</v-btn>
</div>
</v-card-text>
<v-card-actions>
<v-spacer></v-spacer>
</v-card-actions>
</v-card>
</v-flex>
</v-layout>
</v-container>
</template>
<script>
import { mapGetters } from "vuex";
export default {
props: ["id"],
computed: {
...mapGetters([
//Here we put Getter
'loadedRecipe',
'loadedingredient',
'loading'
]),
recipe() {
const loadedRecipe = this.$store.dispatch('loadedRecipe',this.id);
console.log("We loaded a recipe with value : ", loadedRecipe);
return loadedRecipe;
}
}
};
</script>
<style scoped>
.btn-style {
color: #43a047;
}
.color {
color: #fafafa;
}
.deleteColorIcon {
color: #e53935;
}
.font {
font-size: 30px;
}
</style>
And through this file, many of the necessary functions are performed in other components.
store.js:
import image1 from "../../assets/img/image1.jpg";
import image2 from "../../assets/img/image2.jpg";
import image3 from "../../assets/img/image3.jpg";
import image4 from "../../assets/img/image4.jpg";
const state = {
loadedingredients: [
{ id: "1", Name: "Sugar", Quantity: "5kg" },
{ id: "2", Name: "Sugar", Quantity: "5kg" },
{ id: "3", Name: "Sugar", Quantity: "5kg" },
],
loadedRecipes: [
{
imageUrl: image3,
id: "3",
title: "Homemade Burger",
description:
"Small plates, salads & sandwiches - an intimate setting with 12 indoor seats plus patio"
// loadedingredients
},
{
imageUrl: image1,
id: "1",
title: "Cake",
description:
"Small plates, salads & sandwiches - an intimate setting with 12 indoor seats plus patio"
// loadedingredients
},
{
imageUrl: image4,
id: "4",
title: "Salad",
description:
"Small plates, salads & sandwiches - an intimate setting with 12 indoor seats plus patio"
// loadedingredients
},
{
imageUrl: image2,
id: "2",
title: "Kabseh",
description:
"Small plates, salads & sandwiches - an intimate setting with 12 indoor seats plus patio"
// loadedingredients
},
],
user: [{ name: "Hiba", email: "Hiba69#gmail.com", password: "123442321325" }],
loading: false,
};
const mutations= {
createRecipe(state, payload) {
// Vue.set(state, 'loadedRecipes', [...state.loadedRecipes, payload])
// console.log('Recipe to adad recipe.js',payload)
state.loadedRecipes.push(payload.recipeData);
},
createIngredients(state, payload) {
// Vue.set(state, 'loadedRecipes', [...state.loadedRecipes, payload])
state.loadedingredients.push(payload);
},
delete_recipe(state, id) {
let index = state.loadedRecipes.findIndex((recipe) => recipe.id == id);
state.loadedRecipes.splice(index, 1);
console.log("Deleted Successfully");
},
delete_ingredient(state, id) {
let index = state.loadedingredients.findIndex(
(ingredient) => ingredient.id == id
);
state.loadedingredients.splice(index, 1);
console.log("Deleted Successfully");
},
updateRecipe(state, payload) {
const recipe = state.loadedRecipes.find((recipe) => {
return recipe.id == payload.id;
});
if (payload.title) {
recipe.title = payload.title;
}
if (payload.description) {
recipe.description = payload.description;
}
},
updateingredient(state,payload) {
const ingredient = state.loadedingredients.find((ingredient)=>{
return ingredient.id == payload.id;
});
if(payload.ingredientsQuantity){
ingredient.ingredientsQuantity=payload.ingredientsQuantity
}
},
setLoading(state, payload) {
state.loading = payload;
}
}
const actions = {
createRecipe:({commit},payload)=>{
commit('createRecipe',payload)
},
delete_recipe:({commit})=>{
commit('delete_recipe')
},
updateRecipeData({ commit }, payload) {
// commit('setLoading',true)
const updateObj = {};
if (payload.title) {
updateObj.title == payload.title;
}
if (payload.description) {
updateObj.description == payload.description;
}
commit("updateRecipe", payload);
localStorage.setItem("updateRecipe", this.loadedRecipes);
},
updateIngredientData({ commit }, payload) {
// commit('setLoading',true)
const updateObj = {};
if (payload.ingredientsQuantity) {
updateObj.ingredientsQuantity == payload.ingredientsQuantity;
}
commit("updateingredient", payload);
localStorage.setItem("updateingredient", this.loadedingredients);
}
};
const getters = {
loadedRecipes: (state) => {
return state.loadedRecipes
.sort((RecipeA, RecipeB) => {
return RecipeA.id > RecipeB.id;
})
.map((aRec) => {
aRec["ingredients"] = [...state.loadedingredients];
return aRec;
});
},
loadedingredients: (state) => {
return state.loadedingredients.sort((ingredientA, ingredientB) => {
return ingredientA.Quantity > ingredientB.Quantity;
});
},
featuredRecipes: (getters) => {
return getters.loadedRecipes.slice(0, 5);
},
loadedRecipe: (state) => {
return (recipeId) => {
return state.loadedRecipes.find((recipe) => {
return recipe.id === recipeId;
});
};
},
loadedingredient: (state) => {
return (ingredientId) => {
return state.loadedRecipes.find((ingredient) => {
return ingredient.id === ingredientId;
});
};
}
};
export default{
state,
mutations,
actions,
getters
}
The simplest solution with your current implementation would be to replace the v-else by v-else-if="recipe !== undefined" (or using a computed method v-else-if="recipeIsLoaded") inside your v-layout.

How to remove action buttons in data table in vuetify for non auth users?

I am trying to remove the edit and delete buttons in posts that are related to other users. so every user should only edit or delete his own post, I already made the authentication in the back-end but i dont want to render the buttons in the front-end. Below is my code:
This is the template:
<template>
<v-data-table
:headers="headers"
:items="posts"
:loading="loading"
:search="search"
:items-per-page="10"
:sort-desc.sync="sortDesc"
:sort-by.sync="sortBy"
item-key="items.key"
class="elevation-3"
:footer-props="{
showFirstLastPage: true,
firstIcon: 'mdi-arrow-collapse-left',
lastIcon: 'mdi-arrow-collapse-right',
prevIcon: 'mdi-minus',
nextIcon: 'mdi-plus',
}"
>
<template v-slot:item.created_at="{ item }">
<span>{{ new Date(item.created_at).toLocaleString() }}</span>
</template>
<template v-slot:top>
<v-toolbar text color="white">
<v-toolbar-title>Posts</v-toolbar-title>
<v-text-field
class="ml-6"
v-model="search"
append-icon="search"
label="Search"
single-line
hide-details
></v-text-field>
<v-divider class="mx-4" inset vertical></v-divider>
<v-spacer></v-spacer>
<v-dialog v-model="dialog" max-width="500px">
<template v-slot:activator="{ on, attrs }">
<v-btn color="green" dark v-bind="attrs" v-on="on">New Post</v-btn>
</template>
<v-card>
<v-card-title>
<span class="headline">{{ formTitle }}</span>
</v-card-title>
<v-card-text>
<v-container>
<v-row>
<v-col cols="12" sm="6" md="4">
<v-text-field
v-model="editedItem.title"
label="Title"
></v-text-field>
</v-col>
<v-col cols="12" sm="20" md="20">
<v-text-field
v-model="editedItem.body"
label="Content"
></v-text-field>
</v-col>
</v-row>
</v-container>
</v-card-text>
<v-card-actions>
<v-spacer></v-spacer>
<v-btn color="blue darken-1" text #click="close">Cancel</v-btn>
<v-btn color="blue darken-1" text #click="save">Save</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</v-toolbar>
</template>
<template v-slot:item.actions="{ item }">
<v-icon small class="mr-2" #click="editItem(item)">
mdi-pencil
</v-icon>
<v-icon small #click="deleteItem(item)">
mdi-delete
</v-icon>
</template>
</v-data-table>
</template>
and this is the javascript:
<script>
import axios from "axios";
export default {
data: () => ({
search: "",
dialog: false,
loading: true,
sortBy: "created_at",
sortDesc: true,
headers: [
{
text: "Author",
align: "start",
sortable: false,
value: "user.name",
},
{
text: "Title",
align: "start",
sortable: false,
value: "title",
},
{
text: "Content",
align: "start",
sortable: false,
value: "body",
},
{
text: "Published Date",
align: "start",
dataType: "Date",
value: "created_at",
},
{
text: "Actions",
align: "start",
sortable: false,
value: "actions",
},
],
posts: [],
editedIndex: -1,
editedItem: {
name: "",
title: "",
body: "",
created_at: "",
user_id: localStorage.getItem("id"),
},
defaultItem: {
name: "",
title: "",
body: "",
created_at: "",
user_id: localStorage.getItem("id"),
},
}),
mounted() {
this.getData();
},
computed: {
formTitle() {
return this.editedIndex === -1 ? "New Post" : "Edit Post";
},
},
watch: {
dialog(val) {
val || this.close();
},
},
methods: {
getData() {
let token = localStorage.getItem("token");
if (token) {
try {
axios
.get("http://127.0.0.1:8000/api/posts", {
headers: {
Authorization: `Bearer ` + token,
"Content-Type": "application/json",
},
})
.then((response) => {
this.loading = false;
this.posts = response.data;
});
} catch (err) {
console.log(err);
}
} else
alert(
"Unauthorized, please login to read, create, edit and delete posts"
);
},
editItem(item) {
if (this.$store.state.userId == item.user_id) {
this.editedIndex = this.posts.indexOf(item);
this.editedItem = Object.assign({}, item);
this.dialog = true;
} else {
alert("Unauthorized User");
return 0;
}
},
deleteItem(item) {
if (this.$store.state.userId == item.user_id) {
try {
let token = localStorage.getItem("token");
confirm("Are you sure you want to delete this post?") &&
axios
.delete(`http://127.0.0.1:8000/api/post/${item.id}`, {
headers: {
Authorization: `Bearer ` + token,
"Content-Type": "application/json",
},
})
.then((response) => {
if (response.data.message === "wrong user") {
alert("Unauthorized User");
return 0;
}
let arr = this.posts;
const result = arr.filter((post) => post.id !== item.id);
this.posts = result;
});
} catch (err) {
console.log(err);
}
} else {
alert("Unauthorized User");
return 0;
}
},
close() {
this.dialog = false;
this.$nextTick(() => {
this.editedItem = Object.assign({}, this.defaultItem);
this.editedIndex = -1;
});
},
save() {
let token = localStorage.getItem("token");
if (this.editedIndex > -1) {
if (this.$store.state.userId == this.editedItem.user_id) {
Object.assign(this.posts[this.editedIndex], this.editedItem);
axios
.put(
`http://127.0.0.1:8000/api/post/${this.editedItem.id}`,
{
title: this.editedItem.title,
body: this.editedItem.body,
user_id: this.editedItem.user_id,
},
{
headers: {
Authorization: `Bearer ` + token,
"Content-Type": "application/json",
},
}
)
.then((response) => {
if (response.data.message == "wrong user") {
alert("Unauthorized User");
}
});
} else {
this.close();
alert("Unauthorized User");
return 0;
}
} else {
axios
.post(
"http://127.0.0.1:8000/api/post",
{
title: this.editedItem.title,
body: this.editedItem.body,
user_id: this.editedItem.user_id,
},
{
headers: {
Authorization: `Bearer ` + token,
"Content-Type": "application/json",
},
}
)
.then((response) => {
this.posts.push(response.data[0]);
});
}
this.close();
},
},
};
</script>
Thank you for your help.
Add a conditional rendering in the template v-if="$store.state.userId == item.user_id" :
<template v-slot:item.actions="{ item }">
<template v-if="$store.state.userId == item.user_id">
<v-icon small class="mr-2" #click="editItem(item)">
mdi-pencil
</v-icon>
<v-icon small #click="deleteItem(item)">
mdi-delete
</v-icon>
</template>
</template>

How do I get replaceData function to work?

Unable to get replaceData, setData, or updateData to work. Using bootstrap vue and tabulator tables. API works fine. Tabulator works if I don't have the url for the API dependent on the modal select, but I need to have the modal select parameter to shorten the query results.
I've tried:
this.$refs.table.replaceData(equip)
this.$refs.tabulator.replaceData(equip)
this.$refs.tableData.replaceData(equip)
table.replaceData(equip)
tabulator.replaceData(equip)
tableData.replaceData(equip)
table.tableData.replaceData(equip)
tabulator.tableData.replaceData(equip)
The issue is under methods > bldgChange
<script>
var Tabulator = require('tabulator-tables')
export default {
name: 'Test',
data: function () {
return {
mfgVariants: [
{ mfg: 'mfgA', text: 'mfgA' },
{ mfg: 'mfgB', text: 'mfgB' },
{ mfg: 'mfgC', text: 'mfgC' }
],
modelVariants: [
{ model: 'A1', text: 'A1' },
{ model: 'B1', text: 'B1' },
{ model: 'C1', text: 'C1' }
],
buildingVariants: [
{ text: 'A' },
{ text: 'B' },
{ text: 'C' }
],
locationVariants: [
{ text: 'mechanical' },
{ text: 'electrical' }
],
classVariants: [
{ text: 'breaker' },
{ text: 'pump' },
{ text: 'generator' }
],
catVariants: [
{ text: 'high' },
{ text: 'low' },
{ text: 'gas' },
{ text: 'diesel' },
{ text: 'big' },
{ text: 'small' }
],
tabulator: null,
tableData: [
{}
]
}
},
Method
methods: {
mfgChange: function () {
console.log(this.selected)
},
bldgChange: function () {
console.log(this.selected)
var parameter = this.selected
var url = 'https://izk7c37zb3.execute-api.us-east-1.amazonaws.com/latest?Building=' + parameter
console.log(url)
fetch(url)
.then(function (response) {
return response.json()
})
.then(function (json) {
var equip = json.recordset
this.tabulator.replaceData(equip)
})
},
showTable: function () {
console.log('ok pressed')
}
},
Table
watch: {
// update table if data changes
tableData: {
handler: function (newData) {
this.tabulator.replaceData(newData)
}
},
deep: true
},
created: function () {
console.log('Page Loaded', this.$refs)
},
mounted () {
var bldgModalRef = this.$refs.bldgModalRef
bldgModalRef.show()
// instantiate tabulator
var locModalRef = this.$refs.locModalRef
var myModalRef = this.$refs.myModalRef
var catModalRef = this.$refs.catModalRef
this.tabulator = new Tabulator(this.$refs.table, {
data: this.tableData,
layout: 'fitColumns',
columns: [
{ title: 'Equipment', field: 'itemid', headerFilter: 'input' },
{ title: 'Manufacturer', field: 'mfg', headerFilter: 'input' },
{ title: 'Model', field: 'model', headerFilter: 'input' },
{ title: 'Class', field: 'class', headerFilter: 'input' },
{ title: 'Category', field: 'category', headerFilter: 'input' },
{ title: 'Description', field: 'description', headerFilter: 'input' },
{ title: 'Location', field: 'location', headerFilter: 'input' },
{ title: 'Building', field: 'building', headerFilter: 'input' }
],
cellClick: function (e, cell) {
var column = cell.getField()
var value = cell.getValue()
var name = cell
.getRow()
.getCell('itemid')
.getValue()
console.log(name, column, value)
if (column === 'mfg' || column === 'model') {
myModalRef.show()
} else if (column === 'itemid') {
alert(column + ' cannot be changed.')
} else if (column === 'description') {
alert(
column + ' is now automatically generated and cannot be changed.'
)
} else if (column === 'building' || column === 'location') {
locModalRef.show()
} else if (column === 'class' || column === 'category') {
catModalRef.show()
} else {
alert(column + ' clicked')
}
}
})
}
}
</script>
Template
<template>
<div>
<b-modal ref="bldgModalRef" v-model="modalShow" title="Buildings" #ok="showTable">
<b-container fluid>
<b-col>
Building(s)
<b-row>
<b-form-select ref="bldg" v-model="selected" v-on:change="bldgChange" :options="buildingVariants" />
</b-row>
</b-col>
</b-container>
</b-modal>
<b-modal ref="myModalRef" title="Manufacturer and Model">
<b-container fluid>
<b-col col="2">
Manufacturer
<b-row>
<b-form-select ref="mfg" v-model="selected" :change="mfgChange" :options="mfgVariants" />
</b-row>
</b-col>
<b-col col="2">
Model
<b-row>
<b-form-select ref="model" :options="modelVariants"/>
</b-row>
</b-col>
</b-container>
</b-modal>
<b-modal ref="locModalRef" title="Building and Location">
<b-container fluid>
<b-col>
Building
<b-row>
<b-form-select
ref="building"
v-model="selected"
v-on:change="buildingChange"
:options="buildingVariants"
/>
</b-row>
</b-col>
<b-col>
Location
<b-row>
<b-form-select ref="location" :options="locationVariants"/>
</b-row>
</b-col>
</b-container>
</b-modal>
<b-modal ref="catModalRef" title="Class and Category">
<b-container fluid>
<b-col>
Class
<b-row>
<b-form-select
ref="class"
v-model="selected"
v-on:change="buildChange"
:options="classVariants"
/>
</b-row>
</b-col>
<b-col>
Category
<b-row>
<b-form-select ref="category" :options="catVariants"/>
</b-row>
</b-col>
</b-container>
</b-modal>
<div ref="table"></div>
</div>
</template>
bldgChange: function () {
console.log(this.selected)
this.tabulator.parameter = this.selected
fetch('<API-URL>?Building=' + this.selected)
.then(response => response.json())
.then(json => this.tabulator.setData(json.recordset))
},

Vue - Update Data on Bootstrap Table Custom Component

I am attempting to make a custom component in Vue 2.0 that extends the existing functionality of the Bootstrap Vue library <b-table>. It mostly works how I would like it to except that the removeRow and resetData functions defined in the index.jsp don't work how I'd like them to.
removeRow does visually remove the row, and removes it from the data prop but the row reappears after the next interaction (sort, filter, etc.). So it's not actually updating the right thing. I'm trying to use a v-model as a shallow copy of items so that I can make deletions to it without affecting the original set of data but I'm just missing something.
resetData does set the data in the table back correctly, but doesn't re-render the table so you can't see the re-added rows, until you do a separate interaction (sort, filter, etc.), in which case they reappear.
So I know I'm somewhat close but would really appreciate any insight on how to get this working correctly and ways I could improve any part of this component.
OreillyTable.vue.js
const OreillyTable = {
inheritAttrs: false,
data: function () {
return {
filter: null,
sortDesc: false,
hideEmpty: false,
isBusy: false,
currentPage: 1,
data: null
}
},
mounted: function () {
let filtered = this.slots.filter(function(value, index, arr){
return value.customRender;
});
this.slots = filtered;
},
methods: {
oreillyTableSort (a, b, key) {
if (a[key] === null || b[key] === null) {
return a[key] === null && b[key] !== null ? -1 : (a[key] !== null && b[key] === null ? 1 : 0);
} else if (typeof a[key] === 'number' && typeof b[key] === 'number') {
// If both compared fields are native numbers
return a[key] < b[key] ? -1 : (a[key] > b[key] ? 1 : 0)
} else {
// Stringify the field data and use String.localeCompare
return this.toString(a[key]).localeCompare(this.toString(b[key]), undefined, {
numeric: true
});
}
},
toString (val) {
return typeof val !== "undefined" && val != null ? val.toString() : '';
},
oreillyFilter (filteredItems) {
this.totalRows = filteredItems.length;
this.currentPage = 1;
}
},
props: {
fields: {
type: Array
},
items: {
type: Array,
required: true
},
hideEmpty: {
type: Boolean
},
filterPlaceholder: {
type: String,
default: "Filter"
},
sortFunction: {
type: Function,
default: null
},
filterFunction: {
type: Function,
default: null
},
slots: {
type: Array
},
sortBy: {
type: String,
default: null
},
perPage: {
type: Number,
default: 10
},
value: {
}
},
template: `<div :class="{ 'no-events' : isBusy }">
<b-row>
<b-col md="12">
<b-form-group class="mb-2 col-md-3 float-right pr-0">
<b-input-group>
<b-form-input v-model="filter" :placeholder="filterPlaceholder" class="form-control" />
</b-input-group>
</b-form-group>
</b-col>
</b-row>
<div class="position-relative">
<div v-if="isBusy" class="loader"></div>
<b-table stacked="md" outlined responsive striped hover
v-bind="$attrs"
v-model="data"
:show-empty="!hideEmpty"
:items="items"
:fields="fields"
:no-provider-sorting="true"
:no-sort-reset="true"
:filter="filter"
:sort-by.sync="sortBy"
:sort-desc.sync="sortDesc"
:sort-compare="sortFunction === null ? this.oreillyTableSort : sortFunction"
:busy.sync="isBusy"
:current-page="currentPage"
:per-page="perPage"
#filtered="filterFunction === null ? this.oreillyFilter : filterFunction">
<template :slot="slot.key" slot-scope="row" v-for="slot in slots">
<slot :name="slot.key" :data="row"></slot>
</template>
</b-table>
<b-row v-if="items.length > perPage">
<b-col sm="12">
<b-pagination size="md" :total-rows="items.length" v-model="currentPage" :per-page="perPage"></b-pagination>
</b-col>
</b-row>
</div>
</div>`
};
index.jsp
<script>
Vue.use(window.vuelidate.default);
Vue.component('oreilly-table', OreillyTable);
const dashboardItems = [
{ id: 12, firstName: "John", lastName: "Adams", tmNumber: "588999", team: "Corporate", flapjackCount: 4, enrollDate: "2018-11-05" },
{ id: 13, firstName: "George", lastName: "Washington", tmNumber: "422111", team: "America", flapjackCount: 28, enrollDate: "2018-10-01" },
{ id: 14, firstName: "Abraham", lastName: "Lincoln", tmNumber: "358789", team: "America", flapjackCount: 16, enrollDate: "2017-09-02" },
{ id: 15, firstName: "Jimmy", lastName: "Carter", tmNumber: "225763", team: "Core", flapjackCount: 9, enrollDate: "2018-03-02" },
{ id: 16, firstName: "Thomas", lastName: "Jefferson", tmNumber: "169796", team: "Core", flapjackCount: 14, enrollDate: "2018-05-01" }
];
const Dashboard = {
template: `<jsp:include page="dashboard.jsp"/>`,
data: function(){
return {
notification: {
text: "The Great Flapjack Contest will be held on December 25, 2018.",
variant: "primary",
timer: true
},
fields: [
{ key: "name", label: "Name", sortable: true, customRender: true },
{ key: "team", label: "Team", sortable: true },
{ key: "enrollDate", label: "Enrollment Date", sortable: true, formatter: (value) => {return new Date(value).toLocaleDateString();} },
{ key: "flapjackCount", sortable: true },
{ key: "id", label: "", 'class': 'text-center', customRender: true }
]
}
},
methods: {
removeRow: function(id) {
this.$refs.table.isBusy = true;
setTimeout(() => { console.log("Ajax Request Here"); this.$refs.table.isBusy = false; }, 1000);
const index = this.$refs.table.data.findIndex(item => item.id === id)
if (~index)
this.$refs.table.data.splice(index, 1)
},
resetData: function() {
this.$refs.table.data = dashboardItems;
}
}
};
const router = new VueRouter({
mode: 'history',
base: "/ProjectTemplate/flapjack",
routes: [
{ path: '/enroll', component: Enroll },
{ path: '/', component: Dashboard },
{ path: '/404', component: NotFound },
{ path: '*', redirect: '/404' }
]
});
new Vue({router}).$mount('#app');
dashboard.jsp
<compress:html>
<div>
<oreilly-table ref="table" :items="dashboardItems" :slots="fields" :fields="fields">
<template slot="name" slot-scope="row">
{{ row.data.item.firstName }} {{ row.data.item.lastName }} ({{ row.data.item.tmNumber }})
</template>
<template slot="id" slot-scope="row">
Remove
</template>
</oreilly-table>
<footer class="footer position-sticky fixed-bottom bg-light">
<div class="container text-center">
<router-link tag="button" class="btn btn-outline-secondary" id="button" to="/enroll">Enroll</router-link>
 
<b-button #click.prevent="resetData" size="md" variant="outline-danger">Reset</b-button>
</div>
</footer>
</div>
I tried to reproduce your problem with a simple example (see: https://codesandbox.io/s/m30wqm0xk8?module=%2Fsrc%2Fcomponents%2FGridTest.vue) and I came across the same problem you have. Just like the others already said, I agree that the easiest way to reset original data is to make a copy. I wrote two methods to remove and reset data.
methods: {
removeRow(id) {
const index = this.records.findIndex(item => item.index === id);
this.records.splice(index, 1);
},
resetData() {
this.records = this.copyOfOrigin.slice(0);
}
}
On mount I execute a function that makes a copy of the data. This is done with slice because otherwise it makes only a reference to the original array (note that normally JS is pass-by-value, but as stated in the vue documentation with objects, and thus internally in vue it is pass by reference (see: Vue docs scroll to the red marked text)).
mounted: function() {
this.copyOfOrigin = this.records.slice(0);
}
Now you can simple remove a record but also reset all the data.
SEE FULL DEMO
I hope this fixes your issue and if you have any questions, feel free to ask.