I'm following a simple tutorial and my for some reason, 2 of my view mutations (addCard, and addList) are working correctly...however, my 3rd mutation (editCard) does not seem to work in Vue. When i click on the card, a layover pops up where you can edit the name...and upon saving, it saves in rails correctly but does not update immediately in the browser. You must refresh the page before you can see the change. I initially thought this was a conflict with Vuex and Rails-ujs, but why would 2 mutations not be working while the 3rd does not? appreciate any help from Vue experts here...
app/javascript/app.vue
<template>
<div id="app" class="row">
<div class="col-12">
<!-- Button trigger modal -->
<button type="button" data-toggle="modal" data-target="#exampleModal">New List</button>
<!-- Bootstrap Modal -->
<div class="modal fade" id="exampleModal" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel" aria-hidden="true">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="exampleModalLabel">Modal title</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
<textarea ref="message" v-model="message" class="form-control mb-1">
</textarea>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
<button v-on:click="createList" class="btn btn-secondary">Add</button>
</div>
</div>
</div>
</div>
</div><br/><br/>
<hr /><hr />
<div class="col-2">
<div class="list">
<a v-if="!editing" v-on:click="startEditing">
<h1 style="padding: 20px 20px;">
<span style="font-style: italic;">+ Add a List</span>
</h1>
</a>
<textarea v-if="editing" ref="message" v-model="message" class="form-control mb-1">
</textarea>
<button v-if="editing" v-on:click="createList" class="btn btn-secondary">Add</button>
<a v-if="editing" v-on:click="editing=false">cancel</a>
</div>
</div>
<list v-for="(list, index) in lists" :list="list"></list>
</div>
</template>
<script>
import list from 'components/list'
export default {
components: { list },
data: function() {
return {
editing: false,
message: "",
}
},
computed: {
lists: {
get() {
return this.$store.state.lists;
},
set(value) {
this.$store.state.lists = value
},
},
},
methods: {
startEditing: function () {
this.editing = true
this.$nextTick(() => { this.$refs.message.focus() })
},
createList: function() {
var data = new FormData // -> {}
data.append("list[name]", this.message)// -> { "list[name]" => this.message }
Rails.ajax({
url: "/lists",
type: "POST",
data: data,
dataType: "json",
beforeSend: () => true,// 2xx, 3xx (SUCCESS), 4xx, 5xx (ERROR)
success: (data) => {
this.$store.commit('addList', data)
this.message = ""
this.editing = false
$('#exampleModal').modal('hide');
return false;
}
});
}
}
}
</script>
<style scoped>
.list {
background-color: #e2e4e6;
padding: 8px;
border-radius: 3px;
margin-bottom: 8px;
}
.card {
}
p {
font-size: 2em;
text-align: center;
}
</style>
app/javascript/packs/application.js
import Vue from 'vue/dist/vue.esm'
import Vuex from 'vuex'
// import BootstrapVue from 'bootstrap-vue' || These are for bootstrap vue removing for now
import App from'../app.vue'
import TurbolinksAdapter from 'vue-turbolinks'
// import 'bootstrap/dist/css/bootstrap.css'; || These are for bootstrap vue removing for now
// import 'bootstrap-vue/dist/bootstrap-vue.css'; || These are for bootstrap vue removing for now
// Vue.use(BootstrapVue); || These are for bootstrap vue removing for now
Vue.use(Vuex)
Vue.use(TurbolinksAdapter)
window.store = new Vuex.Store({
state: {
lists: []
},
mutations: {
addList(state, data) {
state.lists.unshift(data)
},
addCard(state, data) {
const index = state.lists.findIndex(item => item.id == data.list_id)
state.lists[index].cards.push(data)
},
editCard(state, data) {
const list_index = state.lists.findIndex((item) => item.id == data.list_id)
const card_index = state.lists[list_index].cards.findIndex((item) => item.id == data.id)
state.lists[list_index].cards.splice(card_index, 1, data)
},
}
})
document.addEventListener("turbolinks:load", function() {
var element = document.querySelector("#boards")
if (element != undefined) {
window.store.state.lists = JSON.parse(element.dataset.lists)
const app = new Vue({
el: element,
store: window.store,
template: "<App />",
components: { App }
})
}
});
app/javascript/components/card.vue
<template>
<div>
<div @click="editing=true" class="card card-body mb-3">
{{card.name}}
</div>
<div v-if='editing' class="modal-backdrop show"></div>
<div v-if='editing' @click="closeModal" class="modal show" style="display: block">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">{{ card.name }}</h5>
</div>
<div class="modal-body">
<input v-model="name" class="form-control"></input>
</div>
<div class="modal-footer">
<button @click="save" type="button" class="btn btn-primary">Save changes</button>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
props: ['card', 'list'],
data: function () {
return {
editing: false,
name: this.card.name,
}
},
methods: {
closeModal: function(event) {
if (event.target.classList.contains("modal")) {
this.editing = false
}
},
save: function() {
var data = new FormData
data.append("card[name]", this.name)
Rails.ajax({
url: `/cards/${this.card.id}`,
type: "PATCH",
data: data,
dataType: "json",
beforeSend: function() { return true },
success: (data) => {
this.$store.commit('editCard', data)
this.editing = false
}
})
},
}
}
</script>
<style scoped>
</style>
app/javascript/components/list.vue
<template>
<div class="col-2">
<div class="list">
<h6>{{ list.name }}</h6>
<card v-for="card in list.cards" :card="card" :list="list"></card>
<div class="card card-body">
<a v-if="!editing" v-on:click="startEditing">Add a Card</a>
<textarea v-if="editing" ref="message" v-model="message" class="form-control mb-1"></textarea>
<button v-if="editing" v-on:click="createCard" class="btn btn-secondary">Add</button>
<a v-if="editing" v-on:click="editing=false">cancel</a>
</div>
</div>
</div>
</template>
<script>
import card from 'components/card'
export default {
components: { card },
props: ["list"],
data: function () {
return {
editing: false,
message: ""
}
},
methods: {
startEditing: function () {
this.editing = true
this.$nextTick(() => { this.$refs.message.focus() })
},
createCard: function() {
var data = new FormData
data.append("card[list_id]", this.list.id)
data.append("card[name]", this.message)
Rails.ajax({
url: "/cards",
type: "POST",
data: data,
dataType: "json",
beforeSend: function() { return true },
success: (data) => {
this.$store.commit('addCard', data)
this.message = ""
this.$nextTick(() => { this.$refs.message.focus() })
}
});
}
}
}
</script>
<style scoped>
.list {
background-color: #e2e4e6;
padding: 8px;
border-radius: 3px;
margin-bottom: 8px;
}
.btn.btn-secondary {
width: 75px;
}
</style>
UPDATE: I've updated with the console and terminal log as requested when I edit & save a card.
Terminal Log:
Started GET "/lists/" for 127.0.0.1 at 2018-04-24 21:51:47 -0500
Processing by ListsController#index as HTML
Rendering lists/index.html.erb within layouts/application
List Load (11.3ms) SELECT "lists".* FROM "lists" ORDER BY "lists"."position" DESC
Card Load (0.1ms) SELECT "cards".* FROM "cards" WHERE "cards"."list_id" = ? ORDER BY "cards"."position" ASC [["list_id", 235]]
Card Load (0.1ms) SELECT "cards".* FROM "cards" WHERE "cards"."list_id" = ? ORDER BY "cards"."position" ASC [["list_id", 234]]
Card Load (0.1ms) SELECT "cards".* FROM "cards" WHERE "cards"."list_id" = ? ORDER BY "cards"."position" ASC [["list_id", 233]]
Card Load (0.1ms) SELECT "cards".* FROM "cards" WHERE "cards"."list_id" = ? ORDER BY "cards"."position" ASC [["list_id", 232]]
Card Load (0.1ms) SELECT "cards".* FROM "cards" WHERE "cards"."list_id" = ? ORDER BY "cards"."position" ASC [["list_id", 231]]
Rendered lists/index.html.erb within layouts/application (17.8ms)
Rendered shared/_head.html.erb (203.0ms)
Rendered shared/_navbar.html.erb (0.6ms)
Rendered shared/_notices.html.erb (0.3ms)
Completed 200 OK in 370ms (Views: 346.5ms | ActiveRecord: 11.9ms)
Started PATCH "/cards/106" for 127.0.0.1 at 2018-04-24 21:51:59 -0500
Processing by CardsController#update as JSON
Parameters: {"card"=>{"name"=>"Card C30006"}, "id"=>"106"}
Card Load (0.2ms) SELECT "cards".* FROM "cards" WHERE "cards"."id" = ? LIMIT ? [["id", 106], ["LIMIT", 1]]
(0.0ms) begin transaction
List Load (0.1ms) SELECT "lists".* FROM "lists" WHERE "lists"."id" = ? LIMIT ? [["id", 231], ["LIMIT", 1]]
SQL (0.2ms) UPDATE "cards" SET "name" = ?, "updated_at" = ? WHERE "cards"."id" = ? [["name", "Card C30006"], ["updated_at", "2018-04-25 02:51:59.753283"], ["id", 106]]
(1.7ms) commit transaction
Rendering cards/show.json.jbuilder
Rendered cards/_card.json.jbuilder (0.6ms)
Rendered cards/show.json.jbuilder (2.5ms)
Completed 200 OK in 28ms (Views: 21.7ms | ActiveRecord: 2.2ms)
Browser Console:
{id: 106, list_id: 231, name: "Card C30006", position: 3, created_at: "2018-04-24T20:39:06.150Z", …}
created_at:(...)
id:(...)
list_id:(...)
name:(...)
position:(...)
updated_at:(...)
url:(...)
__ob__:Observer
dep:Dep {id: 86, subs: Array(0)}
value:{…}
vmCount:0
__proto__:Object
get created_at:ƒ reactiveGetter()
set created_at:ƒ reactiveSetter(newVal)
get id:ƒ reactiveGetter()
set id:ƒ reactiveSetter(newVal)
get list_id:ƒ reactiveGetter()
set list_id:ƒ reactiveSetter(newVal)
get name:ƒ reactiveGetter()
set name:ƒ reactiveSetter(newVal)
get position:ƒ reactiveGetter()
set position:ƒ reactiveSetter(newVal)
get updated_at:ƒ reactiveGetter()
set updated_at:ƒ reactiveSetter(newVal)
get url:ƒ reactiveGetter()
set url:ƒ reactiveSetter(newVal)
__proto__:Object
UPDATE 2: (adding in the Vuex panel output)