How to remove action buttons in data table in vuetify for non auth users? - vue.js

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>

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

Search in vuetify datatable (server side)

I'm using vuetify datatables with server side in my app.
Pagination and sorting works fine but I have problem with search input - when I write something in input, new requests dont't send to api.
I found solution with pagination.sync in options but when I try it I get an error in the console:
[BREAKING] 'pagination' has been removed, use 'options' instead. For
more information, see the upgrade guide
https://github.com/vuetifyjs/vuetify/releases/tag/v2.0.0#user-content-upgrade-guide
My code is here:
<template>
<v-container fluid>
<v-text-field
v-model="search"
append-icon="mdi-magnify"
></v-text-field>
<v-data-table
:items="usersList"
:headers="headers"
sort-by="name"
:sort-desc="false"
:options.sync="options"
:server-items-length="totalUsers"
:search="search"
:loading="loading"
loading-text="Ładowanie..."
class="elevation-0"
></v-data-table>
</v-container>
</template>
<script>
export default {
data: () => ({
totalUsers: 0,
usersList: [],
search: "",
loading: true,
options: {},
headers: [
{ text: 'Nazwa użytkownika', value: 'name' },
{ text: 'Adres e-mail', value: 'email' },
{ text: 'Opcje', value: 'actions', sortable: false },
]
}),
watch: {
options: {
handler () {
this.getUsers();
},
deep: true
},
},
mounted () {
this.getUsers();
},
methods: {
getUsers() {
this.loading = true;
userService.getAll(this.options).then(data => {
if (data.status == "success") {
this.usersList = data.items;
this.totalUsers = data.total;
} else {
console.log("Nie udało się pobrać użytkowników.");
}
this.loading = false;
});
},
}
};
</script>

Display Recipes in the Page

This is a project in order to display recipes and perform many operations on these recipes such as adding, deleting and modifying, but here I want to display the recipes and to view the recipes I wrote the following code, but the recipes were never displayed in the browser.
How can I solve this problem?
This file is for viewing recipes.
Recipes.vue:
<template>
<v-container>
<v-layout
row
wrap
v-for="recipe in Recipes"
:key="recipe.id"
class="mb-2"
>
<v-flex xs12 sm10 md8 offset-sm1 offset-md2>
<v-card class="grey lighten-4 pl-3 ">
<v-container fluid>
<v-layout row>
<v-flex xs5 sm4 md3>
<v-img height="180px" :src="recipe.imageUrl"></v-img>
</v-flex>
<v-flex xs7 sm8 md9>
<v-card-title primary-title>
<div>
<h5 class="color mb-0">{{ recipe.title }}</h5>
<div>
{{ recipe.description }}
</div>
</div>
</v-card-title>
<v-card-actions>
<v-flex>
<v-btn
text
left
:to="'/recipe/' + recipe.id"
class="green darken-1 btn-style"
>
View Recipe
</v-btn>
</v-flex>
<v-flex xs5 sm4 md2>
<v-btn class="deleteColorIcon">
<v-icon
left
class=" pl-4"
#click="$store.commit('delete_recipe', recipe.id)"
>
mdi-delete
</v-icon>
<!-- </v-btn> -->
</v-btn>
</v-flex>
</v-card-actions>
</v-flex>
</v-layout>
</v-container>
</v-card>
</v-flex>
</v-layout>
</v-container>
</template>
<script>
import { mapGetters } from 'vuex'
import { mapActions } from 'vuex'
export default {
actions: {
},
computed: {
...mapGetters([
//Here we put Getter
'loadedRecipes',
]),
Recipes() {
console.log('Hi I am i Recipe Component')
return this.$store.dispatch('loadedRecipes')
}
},
methods: {
...mapActions([
'createRecipe'
])
},
createRecipe() {
console.log('Hi I am in Create Recipe Component')
this.$store.dispatch('createRecipe');
},
};
</script>
<style scoped>
.color {
color: #43a047;
}
.btn-style {
color: #fafafa;
}
.deleteColorIcon {
color: #e53935;
}
</style>
And this file is for creating several functions that we want to use 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
seating..",
// loadedingredients
},
{
imageUrl: image1,
id: "1",
title: "Cake",
description:
"Small plates, salads & sandwiches - an intimate setting with 12 indoor seats plus patio
seating..",
// loadedingredients
},
{
imageUrl: image4,
id: "4",
title: "Salad",
description:
"Small plates, salads & sandwiches - an intimate setting with 12 indoor seats plus patio
seating..",
// loadedingredients
},
{
imageUrl: image2,
id: "2",
title: "Kabseh",
description:
"Small plates, salads & sandwiches - an intimate setting with 12 indoor seats plus patio
seating.",
// 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.ingredientData);
},
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;
});
},
featuredRecipes: (getters) => {
return getters.loadedRecipes.slice(0, 5);
},
loadedRecipe: (state) => {
return (recipeId) => {
return state.loadedRecipes.find((recipe) => {
return recipe.id === recipeId;
});
};
}
};
export default{
state,
mutations,
actions,
getters
}
First thing I can see is that it should be this.$store.state.loadedRecipes instead of this.$store.dispatch("loadedRecipes") when you load the elements from the store.
I also think you might be missing store inside the Vue instance and or the creation of the actual store.
The store should be imported like this in app.js, main.js or index.js like so:
import store from './store'
new Vue({
store,
...
})
If you don't have a index.js inside your store folder you probably haven't created the actual store:
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
const store = new Vuex.Store({
state,
mutations,
actions,
getters
})
Read more here about the store in Vuex.
Here is a working demo of your code.
Note that I have only changed the necessary to view the items in the store. There might be other bugs.

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.

Vuetify group of checkboxes returns all true

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>