应用程序/ 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>
应用程序/ JavaScript的/包/的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 } }) } });
应用程序/ JavaScript的/组件/ 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",beforeSend: function() { return true },success: (data) => { this.$store.commit('editCard',data) this.editing = false } }) },} } </script> <style scoped> </style>
应用程序/ JavaScript的/组件/ 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",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>
更新:我在编辑和编辑时根据要求更新了控制台和终端日志.保存一张卡片.
终端日志:
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],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)
浏览器控制台
{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
解决方法
此外,反应性规则有一些信息:https://vuex.vuejs.org/en/mutations.html#mutations-follow-vues-reactivity-rules
我认为,警惕页面实际上反映了你的情况.引:
Due to limitations in JavaScript,Vue cannot detect the following changes to an array:
When you directly set an item with the index,e.g.
vm.items[indexOfItem] = newValue
When you modify the length of the array,e.g.vm.items.length = newLength
To overcome caveat 1,both of the following will accomplish the same as
vm.items[indexOfItem] = newValue
,but will also trigger state updates in the reactivity system:
// Vue.set Vue.set(vm.items,indexOfItem,newValue)
// Array.prototype.splice vm.items.splice(indexOfItem,newValue)
基于此,我会尝试将您的代码更新为:
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) var updata = state.lists[list_index].cards[card_index] = data; state.lists.splice(list_index,updata);
将接头移动到列表的顶层现在应该触发更新.
请注意,如果您有反馈,我将很乐意适当更新此答案.