not working Delete Button in Vue js file on Laravel project - javascript

working with Vue Js 3 and Laravel. in the project I have tags.vue file as well following
https://codeshare.io/OdVL1P
using vuex this project and vuex configuration file is store.js like following
import { createStore } from 'vuex'
const store = createStore({
/* state, actions, mutations */
state : {
conuter : 1000,
deleteModalObj : {
showDeleteModal: false,
deleteUrl : '',
data : null,
deletingIndex: -1,
isDeleted : false,
},
},
getters: {
getCounter(state){
return state.conuter
},
getDeleteModalObj(state){
return state.deleteModalObj
}
},
mutations: {
changeTheCounter(state, data){
state.conuter += data
},
setDeleteModal(state, data){
const deleteModalObj = {
showDeleteModal: false,
deleteUrl : '',
data : null,
deletingIndex: -1,
isDeleted : data,
}
state.deleteModalObj = deleteModalObj
state.tags.splice()
},
setDeletingModalObj(state, data){
state.deleteModalObj = data
},
},
actions :{
changeCounterAction({commit}, data){
commit('changeTheCounter', data)
}
}
})
export default store;
and using deleteModal.vue component for delete button as following
<template>
<div>
<!--delete alert model-->
<Modal
:value="getDeleteModalObj.showDeleteModal"
:mask-closable="false"
:closable="false"
width="360">
<p slot="header" style="color:#f60;text-align:center">
<Icon type="ios-information-circle"></Icon>
<space>Delete confimation</space>
</p>
<div style="text-align:center">
<p>Are you sure you want to delete Tag?.</p>
</div>
<div slot="footer">
<Button type="error" size="large" long :loading="isDeleing" :disabled="isDeleing" #click="deleteTag">Delete</Button>
</div>
</Modal>
</div>
</template>
<script>
import {mapGetters} from 'vuex'
export default {
data(){
return {
isDeleing : false,
}
},
methods : {
async deleteTag(){
this.isDeleing = true
const res = await this.callApi('post', this.getDeleteModalObj.deleteUrl, this.getDeleteModalObj.data)
if(res.status===200){
this.s(this.getDeleteModalObj.successMsg)
this.$store.commit('setDeleteModal', true)
}else{
this.swr()
this.$store.commit('setDeleteModal', false)
}
this.isDeleing = false
this.showDeleteModal = false
},
closeModal(){
this.$store.commit('setDeleteModal', false)
}
},
computed : {
...mapGetters(['getDeleteModalObj'])
}
}
</script>
but when click delete button tags.vue it is not working no any error also. how could I fix this proble as well

Related

How do I watch the output of a function?

I have 2 buttons. One adds a movie to local storage, the other removes it from there. I made a function that basically switches the button. If the movie is added it shows "remove", if the movie's not been added it shows the button "add".
The function works but it doesn't know when the boolean changes so the button doesn't change. Someone explained that i should use watch property, but how am I supposed to watch an output of a function?
here is the code
<template>
<div>
<div class="card" v-for="movie in movies"
:key="movie.id">
{{movie.title}}
{{movie.release_date}}
<button v-show="!showButton(movie.id)" type="submit" #click="storeMovie(movie.id)" >
Aggiungi
</button>
<button v-show="showButton(movie.id)" type="submit" #click="removeMovie(movie.id)">
Rimuovi
</button>
</div>
<div class="card" v-for="favourite in watchlist"
:key="favourite.id">
{{favourite.title}}
</div>
</div>
</template>
<script>
import axios from 'axios'
export default {
name: 'HomeComp',
data () {
return {
movies: [],
watchlist: [],
movie: null,
}
},
mounted () {
axios
.get('https://api.themoviedb.org/3/movie/popular?api_key=###&language=it-IT&page=1&include_adult=false&region=IT')
.then(response => {
this.movies = response.data.results
// console.log(response.data.results)
})
.catch(error => {
console.log(error)
this.errored = true
})
.finally(() => this.loading = false)
},
watch: {
switchButton(oldValue, newValue) {
if (oldValue != newValue) {
this.showButton(id) = true;
} //made an attempt here
}
},
methods: {
storeMovie(id) {
const favouriteMovie = this.movies.find(movie => movie.id === id )
this.watchlist.push(favouriteMovie);
localStorage.setItem("watchlist", JSON.stringify(this.watchlist));
},
removeMovie(id) {
const removedMovie = this.watchlist.find(movie => movie.id === id )
const indexMovie = this.watchlist.indexOf(removedMovie);
if (indexMovie > -1) {
this.watchlist.splice(indexMovie, 1);
}
localStorage.setItem("watchlist", JSON.stringify(this.watchlist));
},
showButton(id) {
const favouriteMovie = this.watchlist.find(movie => movie.id === id )
if (favouriteMovie && favouriteMovie.length > 0) {
return true
} else{
return false
}
}
},
}
</script>
<style scoped lang="scss">
</style>
A better approach would be to store the state of a movie being stored or not in the watchlist directly on the movie object.
Then use a computed to get the watchlist from the movie list instead of using two different arrays.
<template>
<div>
<div class="card" v-for="movie in movies" :key="movie.id">
{{movie.title}}
{{movie.release_date}}
<button v-show="!movie.toWatch" type="submit" #click="storeMovie(movie.id)">
{{ movie.toWatch ? 'Rimuovi' : 'Aggiungi' }}
</button>
</div>
<div class="card" v-for="favourite in watchList" :key="favourite.id">
{{favourite.title}}
</div>
</div>
</template>
<script>
export default {
name: 'HomeComp',
data() {
return {
movies: [],
}
},
computed: {
// Get the watchList from the movies list
watchList() {
return this.movies.filter(movie => movie.toWatch)
}
},
watch: {
watchList(newWatchList) {
// Update the localStorage whenever the list changes
localStorage.setItem("watchlist", JSON.stringify(newWatchList));
}
},
mounted() {
// your axios call
},
methods: {
storeMovie(id) {
const favouriteMovie = this.movies.find(movie => movie.id === id)
if (favouriteMovie) {
// just reverse the boolean
favouriteMovie.toWatch = !favouriteMovie.toWatch
}
},
},
}
</script>

How to make correct pagination in Vue.js 2

I'm having trouble creating pagination with vue. My task is to make sure that when you click on the numbers of the buttons, new tasks from jsonplaceholder are loaded.
I have successfully loaded the first and second page. I assume this is related directly to my this.fetchTodos() action. I'm just learning vue and I need help figuring out how to update the data when moving to a new page without loading.
In this case, it is necessary that the url of the page changes (get request). My page state is changing, but posts are not loading when clicking on the third page.
Below is the code of four files that I think will help you understand the situation.
Maybe you will easier with GitHub, please check pagination branch
Thanks in advance for your help! If you have questions or need more information, write in the comments
TodoListView.vue - is starting page, where is todos fetching and rendered on page.
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<template>
<div class="todolist">
<ContainerBootstrap>
<div class="row">
<div class="col-12 text-center">
<TitlePage v-if="todos" text="Список задач"/>
<TitlePage v-else text="Список задач пуст, создайте новую!"/>
<button-bootstrap data-bs-toggle="modal" data-bs-target="#createTodo" css-class="btn-lg btn-primary mt-2 mb-4">Создать задачу</button-bootstrap>
<ModalBootstrap #create="createTodo" :todos="todos" css-id="createTodo"/>
<SearchBootstrap v-if="todos" #search="searchTodo"/>
<div v-if="todos" class="d-flex justify-content-end mt-2">
<button-bootstrap #click.native="setCompletedToAllTodo()" css-class="btn-lg btn-success">Отменить всё как "Выполненные"</button-bootstrap>
</div>
</div>
</div>
<TodoList v-if="todos" :todos="searchedTodos"/>
<PaginationBootstrap :page="page" :total-pages="totalPages" class="mt-4"/>
</ContainerBootstrap>
</div>
</template>
<script>
import ContainerBootstrap from "#/components/UI/ContainerBootstrap";
import TitlePage from "#/components/TitlePage";
import TodoList from "#/components/TodoList";
import {mapState, mapActions, mapMutations, mapGetters} from 'vuex'
import ButtonBootstrap from "#/components/UI/ButtonBootstrap";
import ModalBootstrap from "#/components/UI/ModalBootstrap";
import SearchBootstrap from "#/components/UI/SearchBootstrap";
import PaginationBootstrap from "#/components/UI/PaginationBootstrap";
export default {
name: "TodoListView",
components: {
PaginationBootstrap,
SearchBootstrap, ModalBootstrap, TodoList , ButtonBootstrap, TitlePage, ContainerBootstrap},
data: function() {
return {
isShow: false,
}
},
methods: {
...mapActions({
fetchTodos: "todos/fetchTodos"
}),
...mapMutations({
setSearchQuery: 'todos/setSearchQuery'
}),
createTodo(todo) {
this.$store.commit('todos/addTodo', todo);
},
setCompletedToAllTodo() {
console.log('hello')
this.$store.commit('todos/setCompletedToAllTodo')
},
searchTodo(query) {
this.$store.state.todos.searchQuery = query;
}
},
mounted() {
this.fetchTodos()
},
computed: {
...mapState({
todos: state => state.todos.todos,
isTodosLoading: state => state.todos.isTodosLoading,
page: state => state.todos.page,
limit: state => state.todos.limit,
totalPages: state => state.todos.totalPages,
searchQuery: state => state.todos.searchQuery
}),
...mapGetters({
searchedTodos: 'todos/searchedTodos'
})
}
}
</script>
TodoListPaginationView - is second file, where is loading second page and another when click on pagination.
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<template>
<div class="todolist">
<ContainerBootstrap>
<div class="row">
<div class="col-12 text-center">
<TitlePage :text="'Страница №'+ page"/>
<router-link to="/todolist">
<button-bootstrap css-class="btn-lg btn-primary mt-2 mb-4">Вернуться к началу</button-bootstrap>
</router-link>
</div>
<TodoList v-if="todos" :todos="searchedTodos"/>
<PaginationBootstrap :page="page" :total-pages="totalPages" class="mt-4"/>
</div>
</ContainerBootstrap>
</div>
</template>
<script>
import ContainerBootstrap from "#/components/UI/ContainerBootstrap";
import TitlePage from "#/components/TitlePage";
import ButtonBootstrap from "#/components/UI/ButtonBootstrap";
import TodoList from "#/components/TodoList";
import {mapActions, mapGetters, mapMutations, mapState} from "vuex";
import PaginationBootstrap from "#/components/UI/PaginationBootstrap";
export default {
name: "TodoListPaginationView",
components: {PaginationBootstrap, TodoList, ButtonBootstrap, TitlePage, ContainerBootstrap},
methods: {
...mapActions({
fetchTodos: "todos/fetchTodos",
}),
...mapMutations({
setSearchQuery: 'todos/setSearchQuery'
})
},
computed: {
...mapState({
todos: state => state.todos.todos,
isTodosLoading: state => state.todos.isTodosLoading,
page: state => state.todos.page,
limit: state => state.todos.limit,
totalPages: state => state.todos.totalPages,
searchQuery: state => state.todos.searchQuery
}),
...mapGetters({
searchedTodos: 'todos/searchedTodos'
})
},
mounted() {
this.fetchTodos()
},
}
</script>
PaginationBootstrap.vue - third file, where is logic for pagination. Ui bootstrap 5 file.
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<template>
<nav aria-label="Page navigation example">
<ul class="pagination">
<li class="page-item"><a class="page-link" href="#">Предыдущая</a></li>
<li v-for="pageNumber in totalPages" :key="pageNumber" :class="{'active' : page === pageNumber}" class="page-item">
<span #click="changePage(pageNumber)" class="page-link">{{pageNumber}}</span>
</li>
<li class="page-item"><a class="page-link" href="#">Далее</a></li>
</ul>
</nav>
</template>
<script>
export default {
name: "PaginationBootstrap",
props: {
page: Number,
totalPages: Number
},
methods: {
changePage(pageNumber) {
this.$store.commit('todos/setPage', pageNumber);
if (pageNumber === 1) {
this.$router.push('/todolist')
}
else {
this.$router.push({name: 'todolistPagination', params: {page: pageNumber}})
}
}
}
}
</script>
<style lang="scss" scoped>
.pagination {
.page-item {
.page-link {
cursor: pointer;
}
}
}
</style>
todosModule.js - last file, where is vuex logic for todos.
import axios from "axios";
export const todosModule = {
state: () => ({
todos: [],
page: 1,
limit: 10,
totalPages: 0,
isTodosLoading: false,
searchQuery: '',
}),
mutations: {
setTodos(state, todos) {
state.todos = todos
},
setPage(state, page) {
state.page = page
},
setTotalPages(state, totalPages) {
state.totalPages = totalPages
},
setLoadingTodos(state, bool) {
state.isTodosLoading = bool
},
setCompleted(state, completed) {
const index = state.todos.findIndex(todo => todo.id === completed.id);
state.todos[index].completed = completed.completed
},
setCompletedToAllTodo(state) {
state.todos.map(obj => {
obj.completed = true
})
},
removeTodo(state, id) {
const index = state.todos.findIndex(todo => todo.id === id)
state.todos.splice(index, 1)
},
addTodo(state, todo) {
state.todos.unshift(todo);
},
setTitle(state, tusk) {
const index = state.todos.findIndex(todo => todo.id === tusk.id);
state.todos[index].title = tusk.title
},
setSearchQuery(state, searchQuery) {
state.searchQuery = searchQuery;
}
},
actions: {
async fetchTodos({state, commit}) {
try {
commit('setLoadingTodos' , true)
const response = await axios.get('https://jsonplaceholder.typicode.com/todos', {
params: {
_page: state.page,
_limit: state.limit
}
})
commit('setTotalPages', Math.ceil(response.headers['x-total-count'] / state.limit))
commit('setTodos', response.data)
}
catch (e) {
console.log(e)
}
finally {
commit('setLoadingTodos', false)
}
},
async getCurrentPage({commit}, currentPage) {
try {
commit('setPage', currentPage)
}
catch (e) {
console.log(e);
}
}
},
getters: {
searchedTodos(state) {
return [...state.todos].filter(todo => todo.title.toLowerCase().includes(state.searchQuery.toLowerCase()))
},
},
namespaced: true
}
Okey, I found solution for myself.
Most importent thing is watcher. I added to TodoListPaginationView.vue next code:
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<template>
<div class="todolist">
<ContainerBootstrap>
<div class="row">
<div class="col-12 text-center">
<TitlePage :text="'Страница №'+ page"/>
<router-link to="/todolist">
<button-bootstrap css-class="btn-lg btn-primary mt-2 mb-4">Вернуться к началу</button-bootstrap>
</router-link>
</div>
<TodoList v-if="todos" :todos="searchedTodos"/>
<PaginationBootstrap :page="page" :total-pages="totalPages" class="mt-4"/>
</div>
</ContainerBootstrap>
</div>
</template>
<script>
import ContainerBootstrap from "#/components/UI/ContainerBootstrap";
import TitlePage from "#/components/TitlePage";
import ButtonBootstrap from "#/components/UI/ButtonBootstrap";
import TodoList from "#/components/TodoList";
import {mapActions, mapGetters, mapMutations, mapState} from "vuex";
import PaginationBootstrap from "#/components/UI/PaginationBootstrap";
export default {
name: "TodoListPaginationView",
components: {PaginationBootstrap, TodoList, ButtonBootstrap, TitlePage, ContainerBootstrap},
methods: {
...mapActions({
fetchTodos: "todos/fetchTodos",
}),
...mapMutations({
setSearchQuery: 'todos/setSearchQuery'
})
},
computed: {
...mapState({
todos: state => state.todos.todos,
isTodosLoading: state => state.todos.isTodosLoading,
page: state => state.todos.page,
limit: state => state.todos.limit,
totalPages: state => state.todos.totalPages,
searchQuery: state => state.todos.searchQuery
}),
...mapGetters({
searchedTodos: 'todos/searchedTodos'
})
},
watch: {
page: function (val) {
if (val) {
this.fetchTodos()
}
},
},
mounted() {
this.fetchTodos();
},
}
</script>

Nuxtjs: Axios Request does not work when switching to another route

i try to build a little clothing web shop with nuxtjs. You can choose the color on the details page. The details page represents a pice of clothing. The ColorMenu is a component. If you choose something a color, it will emit it back to the details page and will send a new details request to my backend.
However, changing the color only works if you don't choose another piece of clothing. If you choose another piece of clothing (so the route parameters will change) and choose another color in the menu, there is a always an error that it cannot load anything. it seems that it sends repeated requests until the request is blocked.
The details routes are built according to this scheme: localhost/details/{sellableId}/{ideaId}/{appearanceId}
Details Page:
<template>
<section class="section">
<div v-if="details">
<div class="columns">
<div class="column">
<ImageCaroussel :images="details.images"></ImageCaroussel>
</div>
<div class="column">
<h3>Farben</h3>
<ColorMenu
:appearances="productType.appearances"
:appearanceIds="details.appearanceIds"
></ColorMenu>
</div>
</div>
</div>
</section>
</template>
<script>
import { mapState } from 'vuex'
import Dropdown from '~/components/details/Dropdown.vue'
import ColorMenu from '~/components/details/ColorMenu.vue'
import ImageCaroussel from '~/components/details/ImageCaroussel.vue'
export default {
created() {
this.$nuxt.$on('selected', ($event) => (this.selected = $event))
this.$nuxt.$on('selectedColor', ($event) => this.setSelectedColor($event))
},
data() {
return {
modal: false,
selected: '',
selectedColor: '',
}
},
async asyncData({ store, params }) {
console.log('asyncfirst')
if (params.sellableId && params.appearanceId && params.ideaId) {
await store.dispatch('details/get_details', {
sellableId: params.sellableId,
appearanceId: params.appearanceId,
ideaId: params.ideaId,
})
let sellableId = params.sellableId
let appearanceId = params.appearanceId
let ideaId = params.ideaId
console.log('asyncsecond!')
return { sellableId, appearanceId, ideaId }
}
},
mounted() {
this.sellableId = this.$route.params.sellableId
this.appearanceId = this.$route.params.appearanceId
this.ideaId = this.$route.params.ideaId
console.log('Mounted!')
},
components: {
Dropdown,
ColorMenu,
ImageCaroussel,
},
computed: {
...mapState({
details: (state) => {
return state.details.details
},
currency: (state) => {
return state.sellable.currency
},
productType: (state) => {
return state.details.productType
},
}),
},
methods: {
checkout: async function (sellableId, size, appearanceId) {
let link = await this.$backendrepositories.basket.checkout(
sellableId,
size,
appearanceId
)
if (link.status === 200 && link.data) {
this.modal = true
setTimeout(() => {
window.location.href = link.data.link
}, 3000)
}
},
setSelectedColor: async function (event) {
this.selectedColor = event
await this.$store.dispatch('details/get_details', {
sellableId: this.sellableId,
appearanceId: this.selectedColor,
ideaId: this.ideaId,
})
},
},
}
</script>
ColorMenu Component:
<template>
<div>
<div
v-for="(cell, index) in appearances"
:key="index"
style="display: inline-block"
>
<label v-if="appearanceIds.includes(cell.id)" class="self-container">
<input type="radio" checked="checked" name="color" />
<span
class="checkmark"
:style="`background-color: ${cell.colors[0].value}`"
#click="select(cell.id)"
></span>
</label>
</div>
</div>
</template>
<script>
export default {
data: function () {
return {
selected: '',
}
},
props: ['appearances', 'appearanceIds'],
methods: {
select(select) {
this.selected = select
this.$nuxt.$emit('selectedColor', this.selected)
},
},
}
</script>
There is a live demo at https://akano-frontend.vercel.app/

Vuex Action committing mutation

I have a vue app where a user can randomize a title and subtitle OR edit the fields using a custom input component.
When a user chooses to edit, I'd like to send the updated title and subtitle from the input component to the store to mutate the title and subtitle state when clicking the save button after filling out the values desired in the input component.
Currently able to pass values from parent to child and had an emit present for the parent to listen to, however, I'm not sure how to update the original values to the custom ones and get "undefined" as a result from the $emit.
I can't seem to find a solution to this problem, all the forums I have been on haven't helped so I really hope someone on here can help me with my problem; would really appreciate it.
Parent.vue
<template>
<main class="home-page page">
<div v-if="!editMode">
<div>
<span>Title: </span>{{title}}
</div>
<div>
<span>Subtitle: </span>{{subtitle}}
</div>
<div>
<button #click="randomizeTitleAndSubtitle">
Randomize
</button>
<button #click="onEdit">Edit</button>
</div>
</div>
<div v-else>
<DoubleInput
:value="{ title, subtitle }"
/>
<div>
<button #click="onCancel">Cancel</button>
<button #click="onSave">Save</button>
</div>
</div>
</main>
</template>
<script>
// # is an alias to /src
import DoubleInput from '#/components/DoubleInput.vue';
import { mapState, mapActions } from 'vuex';
export default {
name: 'Parent',
components: {
DoubleInput,
},
data() {
return {
editMode: false,
};
},
computed: {
...mapState(['title', 'subtitle']),
},
methods: {
...mapActions(['randomizeTitleAndSubtitle', 'updateTitleAndSubtitle']),
onEdit() {
this.editMode = true;
},
onCancel() {
this.editMode = false;
},
onSave() {
this.editMode = false;
const newTitle = this.title;
const newSubtitle = this.subtitle;
this.updateTitleAndSubtitle({ newTitle, newSubtitle });
},
},
mounted() {
this.randomizeTitleAndSubtitle();
},
};
</script>
Child.vue
<template>
<div>
<label>Edit Title: </label>
<input type="text" ref="title" :value="value.title" #input="updateValue()" />
<label>Edit Subtitle: </label>
<input type="text" ref="subtitle" :value="value.subtitle" #input="updateValue()" />
</div>
</template>
<script>
export default {
name: 'Child',
props: ['value'],
methods: {
updateValue() {
this.$emit('input', {
title: this.$refs.title.value,
subtitle: this.$refs.subtitle.value,
});
},
},
};
</script>
Store
import Vue from 'vue';
import Vuex from 'vuex';
import randomWords from 'random-words';
Vue.use(Vuex);
export default new Vuex.Store({
state: {
title: '',
subtitle: '',
},
mutations: {
UPDATE_TITLE(state, value) {
state.title = value;
},
UPDATE_SUBTITLE(state, value) {
state.subtitle = value;
},
},
actions: {
randomizeTitle({ commit }) {
const newTitle = randomWords();
commit('UPDATE_TITLE', newTitle);
},
randomizeSubtitle({ commit }) {
const newSubtitle = randomWords();
commit('UPDATE_SUBTITLE', newSubtitle);
},
randomizeTitleAndSubtitle({ dispatch }) {
dispatch('randomizeTitle');
dispatch('randomizeSubtitle');
},
updateTitleAndSubtitle({ commit }, value) {
const payload = {
title: value.title || null,
subtitle: value.subtitle || null,
};
commit('UPDATE_TITLE', payload);
commit('UPDATE_SUBTITLE', payload]);
},
},
modules: {
},
});
Where I was having the biggest issue was most in the Vuex store, not the parent to child lifecycle like I thought. The emit was working just fine and needed to add in some computed properties to the custom input component. How I was approaching the store was completely backwards and gutted the updateTitleAndSubtitle() action to what's shown below. And lastly, added an #input that would send the updated object of values to onEdit() to set the values to an empty object in the data. Then, use that object with the new values to dispatch/commit to the store! Vualá ~ the desired behavior, no errors, and ended up figuring it out with some time.
What I was missing was passing the new emitted data object to a store action to then mutate the state. The whole concept behind this code challenge was to take in data from the store, modify it through a component, send back the modified data to the store to then change the state. A bit overkill for this, BUT it's the practice and concept I needed to approach a much larger problem in an existing application at work.
Here's the code breakdown!
Custom Input:
<template>
<div>
<label for="title">Edit Title: </label>
<input
type="text"
id="title"
:setTitle="setTitle"
ref="title"
:value="value.title"
#input="updateValue()"
/>
<label for="title">Edit Subtitle: </label>
<input
type="text"
id="subtitle"
:setSubtitle="setSubtitle"
ref="subtitle"
:value="value.subtitle"
#input="updateValue()"
/>
</div>
</template>
<script>
export default {
name: 'DoubleInput',
props: {
value: {
type: Object,
required: true,
},
},
computed: {
setTitle() {
// console.log('set title: ', this.value.title);
return this.value.title;
},
setSubtitle() {
// console.log('set subtitle: ', this.value.subtitle);
return this.value.subtitle;
},
},
methods: {
updateValue() {
this.$emit('input', {
title: this.$refs.title.value,
subtitle: this.$refs.subtitle.value,
});
},
},
};
</script>
Parent:
<template>
<main class="home-page page">
<!-- <span class="bold">Title:</span> {{ title }} <br>
<span class="bold">Subtitle:</span> {{ subtitle }}
<hr> -->
<div v-if="!editMode" class="display-information">
<div class="title">
<span class="bold">Title: </span>{{title}}
</div>
<div class="subtitle">
<span class="bold">Subtitle: </span>{{subtitle}}
</div>
<div class="controls">
<button id="randomize-button" class="control-button" #click="randomizeTitleAndSubtitle">
Randomize
</button>
<button id="edit-button" class="control-button" #click="onEdit">Edit</button>
</div>
</div>
<div v-else class="edit-controls">
<CustomInput
:value="{ title, subtitle }"
#input="v => onEdit(v)"
/>
<div class="controls">
<button id="cancel-button" class="control-button" #click="onCancel">Cancel</button>
<button id="save-button" class="control-button" #click="onSave(v)">Save</button>
</div>
</div>
</main>
</template>
<script>
// # is an alias to /src
import CustomInput from '#/components/CustomInput.vue';
import { mapState, mapActions } from 'vuex';
export default {
name: 'Home',
components: {
CustomInput,
},
data() {
return {
editMode: false,
v: {},
};
},
computed: {
...mapState(['title', 'subtitle']),
},
methods: {
...mapActions(['randomizeTitleAndSubtitle', 'updateTitleAndSubtitle']),
onEdit(v) {
this.editMode = true;
this.v = v;
// console.log('returned value object: ', v);
},
onCancel() {
this.editMode = false;
},
onSave() {
this.editMode = false;
this.$store.dispatch('updateTitleAndSubtitle', this.v);
},
},
mounted() {
this.randomizeTitleAndSubtitle();
},
};
</script>
<style lang="stylus" scoped>
.bold
font-weight bold
.controls
width 100%
display flex
justify-content space-around
max-width 20rem
margin-top 2rem
margin-left auto
margin-right auto
.control-button
height 2.5rem
border-radius 1.25rem
background-color white
border 0.125rem solid black
padding-left 1.25rem
padding-right 1.25rem
&:hover
cursor pointer
background-color rgba(0, 0, 0, 0.1)
</style>
Store:
import Vue from 'vue';
import Vuex from 'vuex';
import randomWords from 'random-words';
Vue.use(Vuex);
export default new Vuex.Store({
state: {
title: '',
subtitle: '',
},
mutations: {
UPDATE_TITLE(state, value) {
state.title = value;
},
UPDATE_SUBTITLE(state, value) {
state.subtitle = value;
},
},
actions: {
randomizeTitle({ commit }) {
const newTitle = randomWords();
commit('UPDATE_TITLE', newTitle);
},
randomizeSubtitle({ commit }) {
const newSubtitle = randomWords();
commit('UPDATE_SUBTITLE', newSubtitle);
},
setTitle({ commit }, value) {
commit('UPDATE_TITLE', value);
},
setSubtitle({ commit }, value) {
commit('UPDATE_SUBTITLE', value);
},
randomizeTitleAndSubtitle({ dispatch }) {
dispatch('randomizeTitle');
dispatch('randomizeSubtitle');
},
updateTitleAndSubtitle({ dispatch }, value) {
dispatch('setTitle', value.title);
dispatch('setSubtitle', value.subtitle);
},
},
modules: {
},
});
Your call to updateTitleAndSubtitle in the onSave() method isn't passing the new title and subtitle to the action.
onSave() {
this.editMode = false;
this.updateTitleAndSubtitle({ title: this.title, subtitle: this.subtitle });
},
Also, I would hesitate using state.title and state.subtitle as your edit mode vars as conventionally you should only change those values via mutations. Instead, pass a local var as the prop to your child component and call the method using it instead.
<DoubleInput v-model="title_subtitle" />
<script>
// ...
data() {
return {
// ...
title_subtitle: {},
};
},
// ...
methods: {
onEdit() {
this.editMode = true;
this.title_subtitle = {
title: this.title,
subtitle: this.subtitle,
};
},
// ...
onSave() {
this.editMode = false;
this.updateTitleAndSubtitle(this.title_subtitle);
},
},
// ...
}
Given your comments it makes more sense why the code is unnecessarily complex. It's perfectly fine to pass your state vars as props as long as your #input handler doesn't try to write back to it. You don't need the local var. Try this:
<CustomInput :value="{ title: this.title, subtitle: this.subtitle }" #input="onSave" />
with
onSave(value) {
this.editMode = false;
this.updateTitleAndSubtitle(value);
}

Why the value from input is not passed to VUEX

I can't transfer the value from input to the store. When I click on the add item button, I need to create a block with its delete button and the text entered in the input. And then save it all in localstorage. But now I am creating only a new block without text. Please help me fix my code to make it work.
Here's how it should work
But how it works now
What I'm doing wrong? How do I transfer the value from Input to Vuex?
Here is my code
<template>
<f7-block-title>Some items</f7-block-title>
<f7-block v-for="(cat, n) in getCats" :key="n">
<span>{{ cat }}</span>
<f7-button fill color="red" #click="removeCat(n)">Delete Cat</f7-button>
</f7-block>
<f7-list form>
<f7-list-input :value="tempCat" type="text"></f7-list-input>
<f7-button fill color="blue" #click="addCat(tempCat)">Add some item</f7-button>
</f7-list>
</template>
<script>
import { mapGetters, mapActions } from 'vuex';
export default {
data () {
return {
tempCat: '',
};
},
computed:{
...mapGetters([
'getCats',
]),
},
methods: {
...mapActions([
'addCat',
'removeCat',
])
}
}
</script>
Code in VUEX
function loadLocalStorage() {
try {
return JSON.parse(localStorage.getItem('cats'));
} catch(e) {
localStorage.removeItem('cats');
return [];
}
}
export default new Vuex.Store({
state: {
cats: loadLocalStorage(),
},
getters:{
getCats: state => state.cats,
},
actions: {
addCat(context, data) {
context.commit('ADD_CAT', data);
context.commit('SAVE_CATS');
},
removeCat(context, data) {
context.commit('REMOVE_CAT', data);
context.commit('SAVE_CATS');
},
},
mutations: {
ADD_CAT(state, data) {
state.cats.push(data);
console.log(state.cats);
},
SAVE_CATS(state) {
localStorage.setItem('cats', JSON.stringify(state.cats));
console.log(state.cats);
},
REMOVE_CAT(state, index) {
state.cats.splice(index, 1);
},
},
});
GitHub link https://github.com/MrRJDio/ex1
First of all, your code doesn't respect the VueX state management standard. This article explains very well how to make proper use of VueX.
Some valid Vuex would like this:
Vue file:
<template>
<f7-block strong>
<f7-block-title>Some items</f7-block-title>
<f7-block v-for="(cat, n) in getCats" :key="n">
<span>{{ cat }}</span>
<f7-button fill color="red" #click="removeCat(n)">Delete Cat</f7-button>
</f7-block>
<f7-list form>
<f7-list-input :value="tempCat" type="text" placeholder="Заметка"></f7-list-input>
<f7-button fill color="blue" #click="addCat(tempCat)">Add some item</f7-button>
</f7-list>
</f7-block>
</template>
<script>
import { mapGetters, mapActions } from 'vuex';
export default {
data () {
return {
tempCat: '',
};
},
computed:{
...mapGetters([
'getCats',
]),
},
methods: {
...mapActions([
'addCat',
'removeCat',
])
}
}
</script>
Store:
function loadLocalStorage() {
try {
return JSON.parse(localStorage.getItem('cats'));
} catch(e) {
localStorage.removeItem('cats');
return [];
}
}
export default new Vuex.Store({
state: {
cats: loadLocalStorage(),
},
getters:{
getCats: state => state.cats,
},
actions: {
addCat(context, data) {
context.commit('ADD_CAT', data);
context.commit('SAVE_CATS');
},
removeCat(context, data) {
context.commit('REMOVE_CAT', data);
context.commit('SAVE_CATS');
},
},
mutations: {
ADD_CAT(state, data) {
state.cats.push(data);
},
SAVE_CATS(state) {
localStorage.setItem('cats', JSON.stringify(state.cats));
},
REMOVE_CAT(state, index) {
state.cats.splice(index, 1);
},
},
});

Categories