vue.js:HTML 元素显示-如果不切换更改
[英] vue.js: HTML element show-if not toggling changes
本文介绍了vue.js:HTML 元素显示-如果不切换更改的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!
问题描述
我最初将 API 调用中的一些对象渲染到我的数据库,它们被序列化并且最初看起来像这样:
数据库消息
<p v-if="messages.length ===0">没有消息</p><div class="msg" v-for="(msg, index) in messages" :key="index"><p class="msg-index">[{{index}}]</p><p class="msg-subject" v-html="msg.subject" v-if="!msg.editing"></p><p><input type="text" v-model="msg.subject" v-if="msg.editing" ></p><p>{{msg.editing}}</p><p class="msg-body" v-html="msg.body" v-show="!messages[index].editing"></p><p><input type="text" v-model="msg.body" v-show="messages[index].editing" ></p><input type="submit" @click="deleteMsg(msg.pk)" value="Delete"/><input type="submit" @click="EditMsg(index)" value="Edit"/><input type="submit" @click="updateMsg(msg.pk)" value="更新"/>
</模板><脚本>导出默认{name: "消息",数据() {返回 {主题: "",msgBody: "",消息:[],};
},
每条消息看起来像这样:
注意 body
、pk
和 subject
是 Django 模型字段.数组中的每一项都代表一个数据库对象.
我想要使用 vue.js 做的是允许用户编辑每个项目.如果用户单击某个项目的 edit
按钮,我想将其元素从 p
转换为 input
,并将其提交到数据库.
为了允许编辑单个项目,我需要在数组中的每个项目中都有一个 editing
字段,所以我在我的 mounted()
属性:
安装(){this.fetchMessages();},方法: {fetchMessages() {this.$backend.$fetchMessages().then(responseData => {this.messages = responseData;this.messages.forEach(函数(值){值['编辑'] = 假;});控制台日志(this.messages);});},
现在,当我在控制台中加载数组时,我看到了:
所以我假设现在,当用户点击 Edit
按钮时,EditMsg
被调用,字段将根据 v-if
/v-show
指令:
EditMsg(msgIdx) {this.messages[msgIdx].editing = true;控制台日志(this.messages);},
但这并没有发生.实际发生的情况是:在控制台/vue-developer-tools 窗口中,项目的 editing
标志更改为 true
,但 HTML 中没有任何变化.
我错过了什么?
完整代码:
<div class="你好"><img src='@/assets/logo-django.png' style="width: 250px"/><p>以下数据是使用 Django 的 ORM 和 Restframork 从 Postgres 数据库中添加/删除的.</p><br/><p>主题</p><input type="text" placeholder="Hello" v-model="subject"><p>消息</p><input type="text" placeholder="来自另一边" v-model="msgBody"><br><br><input type="submit" value="Add" @click="postMessage" :disabled="!subject || !msgBody"><小时/><h3>数据库上的消息</h3><p v-if="messages.length ===0">没有消息</p><div class="msg" v-for="(msg, index) in messages" :key="index"><p class="msg-index">[{{index}}]</p><p class="msg-subject" v-html="msg.subject" v-if="!msg.editing"></p><p><input type="text" v-model="msg.subject" v-if="msg.editing" ></p><p>{{msg.editing}}</p><p class="msg-body" v-html="msg.body" v-show="!messages[index].editing"></p><p><input type="text" v-model="msg.body" v-show="messages[index].editing" ></p><input type="submit" @click="deleteMsg(msg.pk)" value="Delete"/><input type="submit" @click="EditMsg(index)" value="Edit"/><input type="submit" @click="updateMsg(msg.pk)" value="更新"/>
</模板><脚本>导出默认{name: "消息",数据() {返回 {主题: "",msgBody: "",消息:[],};},安装(){this.fetchMessages();},方法: {fetchMessages() {this.$backend.$fetchMessages().then(responseData => {this.messages = responseData;this.messages.forEach(函数(值){值['编辑'] = 假;});控制台日志(this.messages);});},postMessage() {const payload = { 主题:this.subject,正文:this.msgBody };this.$backend.$postMessage(payload).then(() => {this.msgBody = "";this.subject = "";this.fetchMessages();});},deleteMsg(msgId) {this.$backend.$deleteMessage(msgId).then(() => {this.messages = this.messages.filter(m => m.pk !== msgId);this.fetchMessages();});},EditMsg(msgIdx) {this.messages[msgIdx].editing = true;控制台日志(this.messages);},updateMsg(msgId) {console.log(this.subject, this.msgBody);const payload = { 主题:this.subject,body:this.msgBody };this.$backend.$putMessage(msgId, payload).then(() => {this.fetchMessages();})}}};<!-- 添加scoped"属性以将 CSS 限制为仅适用于该组件 --><样式范围>小时{最大宽度:65%;}.msg {边距:0 自动;最大宽度:30%;文本对齐:左;边框底部:1px 实心 #ccc;填充:1rem;}.msg-index {颜色:#ccc;字体大小:0.8rem;/* 底边距:0;*/}图像{宽度:250px;填充顶部:50px;填充底部:50px;}</风格>
解决方案
根据 Vue 内部结构:
<块引用>
Vue 通过使用 Object.defineProperty
转换属性来观察数据.但是,在 ECMAScript 5 中,无法检测何时向对象添加新属性,或何时从对象中删除属性.
因此,当您将响应数据绑定到 this.messages
时,对数组属性的任何更改都不再被 Vue 视为响应式.
相反,如果您在将 responseData
属性绑定到 Vue 数据属性之前丰富它,则所有数组都保持反应性.我的意思是这样的:
fetchMessages() {this.$backend.$fetchMessages().then(responseData => {让 editableMessages = responseData;editableMessages.forEach(函数(值){值['编辑'] = 假;});this.messages = editableMessages;});}
这里有一个基于您的域的小例子.
I'm initially rendering some objects from an API call to my database, they are serialized and look like this initially:
<h3>Messages on Database</h3>
<p v-if="messages.length ===0">No Messages</p>
<div class="msg" v-for="(msg, index) in messages" :key="index">
<p class="msg-index">[{{index}}]</p>
<p class="msg-subject" v-html="msg.subject" v-if="!msg.editing"></p>
<p><input type="text" v-model="msg.subject" v-if="msg.editing" ></p>
<p>{{msg.editing}}</p>
<p class="msg-body" v-html="msg.body" v-show="!messages[index].editing"></p>
<p><input type="text" v-model="msg.body" v-show="messages[index].editing" ></p>
<input type="submit" @click="deleteMsg(msg.pk)" value="Delete" />
<input type="submit" @click="EditMsg(index)" value="Edit" />
<input type="submit" @click="updateMsg(msg.pk)" value="Update" />
</div>
</div>
</template>
<script>
export default {
name: "Messages",
data() {
return {
subject: "",
msgBody: "",
messages: [],
};
},
each message looks like this:
notice that body
, pk
and subject
are the Django model fields. Each item in the array represents a database object.
What I want to do using vue.js, is allow users to edit each item. If the user clicks the edit
button for an item, I want to transform its element from p
to input
, and submit that to the database.
In order to allow editing of individual items, I need an editing
field in each item in the array, so I'm doing this in my mounted()
property:
mounted() {
this.fetchMessages();
},
methods: {
fetchMessages() {
this.$backend.$fetchMessages().then(responseData => {
this.messages = responseData;
this.messages.forEach(function (value) {
value['editing'] = false;
});
console.log(this.messages);
});
},
Now, when I load up the array in my console, I see this:
So I assumed that now, when the user clicks the Edit
button, EditMsg
is called, and the fields will transform according to the v-if
/v-show
directives:
EditMsg(msgIdx) {
this.messages[msgIdx].editing = true;
console.log(this.messages);
},
But that's not happening. What is actually happening is this: the editing
flag for the item is changed to true
in the console/vue-developer-tools window, but nothing changes in the HTML.
What am I missing?
Full code:
<template>
<div class="hello">
<img src='@/assets/logo-django.png' style="width: 250px" />
<p>The data below is added/removed from the Postgres Database using Django's ORM and Restframork.</p>
<br/>
<p>Subject</p>
<input type="text" placeholder="Hello" v-model="subject">
<p>Message</p>
<input type="text" placeholder="From the other side" v-model="msgBody">
<br><br>
<input type="submit" value="Add" @click="postMessage" :disabled="!subject || !msgBody">
<hr/>
<h3>Messages on Database</h3>
<p v-if="messages.length ===0">No Messages</p>
<div class="msg" v-for="(msg, index) in messages" :key="index">
<p class="msg-index">[{{index}}]</p>
<p class="msg-subject" v-html="msg.subject" v-if="!msg.editing"></p>
<p><input type="text" v-model="msg.subject" v-if="msg.editing" ></p>
<p>{{msg.editing}}</p>
<p class="msg-body" v-html="msg.body" v-show="!messages[index].editing"></p>
<p><input type="text" v-model="msg.body" v-show="messages[index].editing" ></p>
<input type="submit" @click="deleteMsg(msg.pk)" value="Delete" />
<input type="submit" @click="EditMsg(index)" value="Edit" />
<input type="submit" @click="updateMsg(msg.pk)" value="Update" />
</div>
</div>
</template>
<script>
export default {
name: "Messages",
data() {
return {
subject: "",
msgBody: "",
messages: [],
};
},
mounted() {
this.fetchMessages();
},
methods: {
fetchMessages() {
this.$backend.$fetchMessages().then(responseData => {
this.messages = responseData;
this.messages.forEach(function (value) {
value['editing'] = false;
});
console.log(this.messages);
});
},
postMessage() {
const payload = { subject: this.subject, body: this.msgBody };
this.$backend.$postMessage(payload).then(() => {
this.msgBody = "";
this.subject = "";
this.fetchMessages();
});
},
deleteMsg(msgId) {
this.$backend.$deleteMessage(msgId).then(() => {
this.messages = this.messages.filter(m => m.pk !== msgId);
this.fetchMessages();
});
},
EditMsg(msgIdx) {
this.messages[msgIdx].editing = true;
console.log(this.messages);
},
updateMsg(msgId) {
console.log(this.subject, this.msgBody);
const payload = { subject: this.subject, body: this.msgBody };
this.$backend.$putMessage(msgId, payload).then(() => {
this.fetchMessages();
}
)
}
}
};
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
hr {
max-width: 65%;
}
.msg {
margin: 0 auto;
max-width: 30%;
text-align: left;
border-bottom: 1px solid #ccc;
padding: 1rem;
}
.msg-index {
color: #ccc;
font-size: 0.8rem;
/* margin-bottom: 0; */
}
img {
width: 250px;
padding-top: 50px;
padding-bottom: 50px;
}
</style>
解决方案
According to Vue internals:
Vue observes data by converting properties with Object.defineProperty
. However, in ECMAScript 5 there is no way to detect when a new property is added to an Object, or when a property is deleted from an Object.
So, when you bind your response data to this.messages
, any mutation to array properties is not considered reactive anymore by Vue.
Instead, if you enrich responseData
properties before binding it to the Vue data properties, all the array stays reactive. I mean like this:
fetchMessages() {
this.$backend.$fetchMessages().then(responseData => {
let editableMessages = responseData;
editableMessages.forEach(function (value) {
value['editing'] = false;
});
this.messages = editableMessages;
});
}
Here there is a small example based on your domain.
这篇关于vue.js:HTML 元素显示-如果不切换更改的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!