Search code examples
vue.jsvuejs2axiosvue-componentvuex

Vue.js: How to change booleanfield in specific object from false to true @click in state with mutations and server with actions axios?


I want to change the boolean from false to true from one object(todo), when I do @click="updateTodoItem(todo.id, todo.title, todo.body)

thats how 1 object looks: {id: 1, title: "", body: "", done: false}

UPDATE (finished): I figured it out and updated code here. I can only pass done one argument as payload to the store actions.

Home.vue

<template>

<div class="container">

  <section class="hero is-link">
    <div class="hero-body">
      <div class="container">
        <h1 class="title">
          Hello Dudes
        </h1>
        <h2 class="subtitle">
          Hamster ToDo List
        </h2>
      </div>
    </div>
  </section>

<section class="section">

  <nav class="level">
    <div class="level-item has-text-centered">
      <div>
        <p class="heading">All Tasks</p>
        <p class="title">{{getTotalTodos}}</p>
      </div>
    </div>
    <div class="level-item has-text-centered">
      <div>
        <p class="heading">Tasks has to be done</p>
        <p class="title">{{getTodosFalse}}</p>
      </div>
    </div>
    <div class="level-item has-text-centered">
      <div>
        <p class="heading">Tasks finished</p>
        <p class="title">{{getTodosTrue}}</p>
      </div>
    </div>
    <!-- <div class="level-item has-text-centered">
      <div>
        <p class="heading">Likes</p>
        <p class="title">789</p>
      </div>
    </div> -->
  </nav>
</section>


<section class="section">
  <div class="columns">
    <div class="column is-half">
      <strong>Add ToDo</strong>
      <div class="control">
      <input class="input is-info" v-model="todoTitle" type="text" placeholder="Add Task Title">
      <input class="input is-info" v-model="todoBody" type="text" placeholder="Add Task Body">
      <button class="button is-link" @click="addTodoItem({title: todoTitle, body: todoBody, done: false}); empyInput()">Add</button>
    </div>
    </div>
  </div>

  <div class="tabs is-small">
    <ul>
      <li class="is-active"><a>All Tasks </a></li> <span class="tag is-link">{{getTotalTodos}}</span>
      <li><a>Tasks still not done</a></li><span class="tag is-link">{{getTodosFalse}}</span>
      <li><a>Finished Tasks</a></li><span class="tag is-link">{{getTodosTrue}}</span>
      <!-- <li><a>Documents</a></li> -->
    </ul>
  </div>

  <article class="media" v-for="todo in todoItems" :key="todo.id">
    <figure class="media-left">
      <!-- <p class="image is-64x64">
        <img src="https://bulma.io/images/placeholders/128x128.png">
      </p> -->
    </figure>
    <div class="media-content">
      <div class="content">
        <p>
          <strong>{{todo.title}}</strong>  
          <br>
            {{todo.body}}
        </p>
      </div>
      <nav class="level is-mobile">
        <div class="level-left">
          <a class="level-item">
            <span class="icon is-small"><i class="fas fa-edit"></i></span>
          </a>
          <a class="level-item" v-if="todo.done==false" @click="updateTodoToTrue(todo.id)">
            <span class="icon is-small"><i class="fas fa-check"></i></span>
          </a>
          <a class="level-item" v-if="todo.done==true" @click="updateTodoToFalse(todo.id)">
            <span class="icon is-small"><i class="fas fa-undo"></i></span>
          </a>

          <a class="level-item" @click="deleteTodoItem(todo.id)">
            <span class="icon is-small"><i class="fas fa-trash"></i></span>
          </a>
        </div>
      </nav>
    </div>
    <div class="media-right">
      <button class="delete" @click="deleteTodoItem(todo.id)"></button>
    </div>
  </article>


</section>

</div>


</template>


<script>
// @ is an alias to /src
import {mapGetters} from 'vuex';
import {mapActions} from 'vuex';

export default {
  name: 'Home',
  data() {
    return{
      todoTitle: '',
      todoBody: '',
    }
  },
  // updated() {
  //   this.$store.dispatch('getTodoItems');
  // },
  computed: {
   ...mapGetters(['todoItems', 'getTotalTodos', 'getTodosTrue', 'getTodosFalse']) 
  },
  methods: {
    ...mapActions(['addTodoItem', 'deleteTodoItem', 'updateTodoToTrue', 'updateTodoToFalse']),
    // addTodo() {
    //   return this.$store.actions.addTodoItem;
    empyInput() {
      this.todoTitle = '';
      this.todoBody = ''; 
    }
  }
}
</script>

store

import Vue from 'vue'
import Vuex from 'vuex'
import axios from 'axios';

Vue.use(Vuex)

export default new Vuex.Store({
  state: {
    todoItems: []
  },


  // Keep in mind that response is an HTTP response
  // returned by the Promise.
  // The mutations are in charge of updating the client state.
  mutations: {
    UPDATE_TODO_ITEMS (state, payload) {
      state.todoItems = payload;
    },
    ADD_TODO_ITEMS (state, payload) {
      state.todoItems.push(payload)
    },
    DELETE_TODO_ITEMS (state, payload) {
      state.todoItems.splice(payload, 1)
    },
    changeTodoStatus: (state, payload) => {
      let index = state.todoItems.findIndex(el => {
        return el.id == payload.id
      })
      state.todoItems[index].done = payload.done
    }

  },

  actions: {
    getTodoItems ({ commit }) {
      axios.get('http://127.0.0.1:8000/api/').then((response) => {
        commit('UPDATE_TODO_ITEMS', response.data)
      });
    },
    addTodoItem ({ commit }, todoItem) {
      axios.post('http://127.0.0.1:8000/api/', todoItem).then((response) => {
        commit('ADD_TODO_ITEMS', response.data)
      });
    },
    deleteTodoItem ({ commit }, todoItemId) {
      axios.delete('http://127.0.0.1:8000/api/' + todoItemId + '/').then((response) => {
        commit('DELETE_TODO_ITEMS', response.data)
      });
    }, 
    updateTodoToTrue ({ commit }, todoItemId) {
      var payload = {
        done: true
      };
      axios.patch('http://127.0.0.1:8000/api/' + todoItemId + '/', payload).then((response) => {
        commit('changeTodoStatus', response.data)
      });
    },   
    updateTodoToFalse ({ commit }, todoItemId) {
      var payload = {
        done: false
      };
      axios.patch('http://127.0.0.1:8000/api/' + todoItemId + '/', payload).then((response) => {
        commit('changeTodoStatus', response.data)
      });
    },   
  },

  getters: {
    todoItems: state => state.todoItems,
    getTotalTodos: state => state.todoItems.length,
    getTodosTrue: state => state.todoItems.filter(todo => todo.done).length,
    getTodosFalse: state => state.todoItems.filter(todo => !todo.done).length
  },

  modules: {
  }
})

Solution

  • My problem was that I tried to pass down 3 arguments to the actions payload in the store. The action accepts accepts only 1 argument which is the payload. I changed the axios method from PUT to PATCH.

    Home.vue

    <template>
    
    <div class="container">
    
      <section class="hero is-link">
        <div class="hero-body">
          <div class="container">
            <h1 class="title">
              Hello Dudes
            </h1>
            <h2 class="subtitle">
              Hamster ToDo List
            </h2>
          </div>
        </div>
      </section>
    
    <section class="section">
    
      <nav class="level">
        <div class="level-item has-text-centered">
          <div>
            <p class="heading">All Tasks</p>
            <p class="title">{{getTotalTodos}}</p>
          </div>
        </div>
        <div class="level-item has-text-centered">
          <div>
            <p class="heading">Tasks has to be done</p>
            <p class="title">{{getTodosFalse}}</p>
          </div>
        </div>
        <div class="level-item has-text-centered">
          <div>
            <p class="heading">Tasks finished</p>
            <p class="title">{{getTodosTrue}}</p>
          </div>
        </div>
        <!-- <div class="level-item has-text-centered">
          <div>
            <p class="heading">Likes</p>
            <p class="title">789</p>
          </div>
        </div> -->
      </nav>
    </section>
    
    
    <section class="section">
      <div class="columns">
        <div class="column is-half">
          <strong>Add ToDo</strong>
          <div class="control">
          <input class="input is-info" v-model="todoTitle" type="text" placeholder="Add Task Title">
          <input class="input is-info" v-model="todoBody" type="text" placeholder="Add Task Body">
          <button class="button is-link" @click="addTodoItem({title: todoTitle, body: todoBody, done: false}); empyInput()">Add</button>
        </div>
        </div>
      </div>
    
      <div class="tabs is-small">
        <ul>
          <li class="is-active"><a>All Tasks </a></li> <span class="tag is-link">{{getTotalTodos}}</span>
          <li><a>Tasks still not done</a></li><span class="tag is-link">{{getTodosFalse}}</span>
          <li><a>Finished Tasks</a></li><span class="tag is-link">{{getTodosTrue}}</span>
          <!-- <li><a>Documents</a></li> -->
        </ul>
      </div>
    
      <article class="media" v-for="todo in todoItems" :key="todo.id">
        <figure class="media-left">
          <!-- <p class="image is-64x64">
            <img src="https://bulma.io/images/placeholders/128x128.png">
          </p> -->
        </figure>
        <div class="media-content">
          <div class="content">
            <p>
              <strong>{{todo.title}}</strong>  
              <br>
                {{todo.body}}
            </p>
          </div>
          <nav class="level is-mobile">
            <div class="level-left">
              <a class="level-item">
                <span class="icon is-small"><i class="fas fa-edit"></i></span>
              </a>
              <a class="level-item" v-if="todo.done==false" @click="updateTodoToTrue(todo.id)">
                <span class="icon is-small"><i class="fas fa-check"></i></span>
              </a>
              <a class="level-item" v-if="todo.done==true" @click="updateTodoToFalse(todo.id)">
                <span class="icon is-small"><i class="fas fa-undo"></i></span>
              </a>
    
              <a class="level-item" @click="deleteTodoItem(todo.id)">
                <span class="icon is-small"><i class="fas fa-trash"></i></span>
              </a>
            </div>
          </nav>
        </div>
        <div class="media-right">
          <button class="delete" @click="deleteTodoItem(todo.id)"></button>
        </div>
      </article>
    
    
    </section>
    
    </div>
    
    
    </template>
    
    
    <script>
    // @ is an alias to /src
    import {mapGetters} from 'vuex';
    import {mapActions} from 'vuex';
    
    export default {
      name: 'Home',
      data() {
        return{
          todoTitle: '',
          todoBody: '',
        }
      },
      // updated() {
      //   this.$store.dispatch('getTodoItems');
      // },
      computed: {
       ...mapGetters(['todoItems', 'getTotalTodos', 'getTodosTrue', 'getTodosFalse']) 
      },
      methods: {
        ...mapActions(['addTodoItem', 'deleteTodoItem', 'updateTodoToTrue', 'updateTodoToFalse']),
        // addTodo() {
        //   return this.$store.actions.addTodoItem;
        empyInput() {
          this.todoTitle = '';
          this.todoBody = ''; 
        }
      }
    }
    </script>
    

    STORE

    import Vue from 'vue'
    import Vuex from 'vuex'
    import axios from 'axios';
    
    Vue.use(Vuex)
    
    export default new Vuex.Store({
      state: {
        todoItems: []
      },
    
    
      // Keep in mind that response is an HTTP response
      // returned by the Promise.
      // The mutations are in charge of updating the client state.
      mutations: {
        UPDATE_TODO_ITEMS (state, payload) {
          state.todoItems = payload;
        },
        ADD_TODO_ITEMS (state, payload) {
          state.todoItems.push(payload)
        },
        DELETE_TODO_ITEMS (state, payload) {
          state.todoItems.splice(payload, 1)
        },
        changeTodoStatus: (state, payload) => {
          let index = state.todoItems.findIndex(el => {
            return el.id == payload.id
          })
          state.todoItems[index].done = payload.done
        }
    
      },
    
      actions: {
        getTodoItems ({ commit }) {
          axios.get('http://127.0.0.1:8000/api/').then((response) => {
            commit('UPDATE_TODO_ITEMS', response.data)
          });
        },
        addTodoItem ({ commit }, todoItem) {
          axios.post('http://127.0.0.1:8000/api/', todoItem).then((response) => {
            commit('ADD_TODO_ITEMS', response.data)
          });
        },
        deleteTodoItem ({ commit }, todoItemId) {
          axios.delete('http://127.0.0.1:8000/api/' + todoItemId + '/').then((response) => {
            commit('DELETE_TODO_ITEMS', response.data)
          });
        }, 
        updateTodoToTrue ({ commit }, todoItemId) {
          var payload = {
            done: true
          };
          axios.patch('http://127.0.0.1:8000/api/' + todoItemId + '/', payload).then((response) => {
            commit('changeTodoStatus', response.data)
          });
        },   
        updateTodoToFalse ({ commit }, todoItemId) {
          var payload = {
            done: false
          };
          axios.patch('http://127.0.0.1:8000/api/' + todoItemId + '/', payload).then((response) => {
            commit('changeTodoStatus', response.data)
          });
        },   
      },
    
      getters: {
        todoItems: state => state.todoItems,
        getTotalTodos: state => state.todoItems.length,
        getTodosTrue: state => state.todoItems.filter(todo => todo.done).length,
        getTodosFalse: state => state.todoItems.filter(todo => !todo.done).length
      },
    
      modules: {
      }
    })