[英]Conditional rendering inside v-for with Vue
我正在使用 Vue 构建一个项目,其中有一个项目列表,每个项目都有一个打开和删除按钮。 现在我需要使用单独的组件为每个项目添加标签。 我想为每个列表项单独切换此组件的呈现,以创建一个包含可折叠项的列表。
我的第一次尝试看起来像这样:
<b-list-group v-if="!isLoading">
<b-list-group-item v-for="project in projects" :key="project.path">
{{ project.name }}
<b-btn variant="danger" size="sm" class="list-button" @click="deleteProject(project.path)">Löschen</b-btn>
<b-btn variant="secondary" size="sm" class="list-button" @click="openProject(project.path)">Öffnen</b-btn>
<b-btn variant="secondary" size="sm" class="list-button" @click="project.showTags = !project.showTags">Tags</b-btn>
<search-tags v-if="project.showTags" style="margin-top: 1em;"></search-tags>
</b-list-group-item>
如果我 console.log 我的this.projects
,我可以看到每个项目的值都正确更改。 尽管如此,即使project.showTags == true
, v-if 也不会呈现。
然后我读到,你不应该在v-for
中使用v-if
,所以我尝试了 Bootstrap-Vue折叠组件:
<b-list-group v-if="!isLoading">
<b-list-group-item v-for="project in projects" :key="project.path">
{{ project.name }}
<b-btn variant="danger" size="sm" class="list-button" @click="deleteProject(project.path)">Löschen</b-btn>
<b-btn variant="secondary" size="sm" class="list-button" @click="openProject(project.path)">Öffnen</b-btn>
<b-btn variant="secondary" size="sm" class="list-button" @click="project.showTags = !project.showTags">Tags</b-btn>
<b-collapse v-model="project.showTags">
<search-tags style="margin-top: 1em;"></search-tags>
</b-collapse>
</b-list-group-item>
但这也行不通。
我认为这不会那么困难,我在这里遗漏了一些东西。 任何帮助表示赞赏。
从我的代码中可以看出,整个列表都放在<b-list-group>
中,根据isLoading
有条件地呈现。 如果我通过翻转isLoading
强制重新加载,然后翻转project.showTags
然后翻转isLoading
,它可以工作。 但我觉得这个解决方案非常阴暗和骇人听闻。
工作但丑陋的代码:
<b-list-group-item v-for="project in projects" :key="project.path">
{{ project.name }}
<b-btn variant="danger" size="sm" class="list-button" @click="deleteProject(project.path)">Löschen</b-btn>
<b-btn variant="secondary" size="sm" class="list-button" @click="openProject(project.path)">Öffnen</b-btn>
<b-btn variant="secondary" size="sm" class="list-button" @click="test(project)">Tags</b-btn>
<search-tags v-show="project.showTags" style="margin-top: 1em;"></search-tags>
</b-list-group-item>
</b-list-group>
和:
test(project) {
this.isLoading = !this.isLoading;
project.showTags = !project.showTags;
console.log(this.projects);
this.isLoading = !this.isLoading;
},
我的数据首先定义如下:
data() {
return {
projects: [],
tags: [],
newProject: false,
isLoading: true,
isError: false,
form: {
name: '',
},
};
},
然后在created()
上,我调用getAllProjects()
从后端获取我的项目:
created() {
this.getAllProjects();
},
我的项目没有showTags
道具,所以我在获取后直接分配它:
async getAllProjects() {
try {
this.isLoading = true;
const res = await fetch(store.state.urlEndpoints.getProjects, {
...AuthPost(), // request config object
});
this.projects = JSON.parse(await res.text());
this.projects.forEach((project) => { project.showTags = false; });
console.log(this.projects);
this.isLoading = false;
} catch (err) {
this.isError = true;
this.isLoading = false;
}
},
每次单击“标签”按钮时,我都会控制台this.projects
,如上面test()
函数中所示。 这些值就像我打算的那样翻转。
@Rouben,您需要对this.projects
的设置方式进行排序,因为您现在在created
时假设您将从getAllProjects
中获取固定的项目数组。
尝试做这样的事情:
async getAllProjects() {
try {
this.isLoading = true;
const res = await fetch(store.state.urlEndpoints.getProjects, {
...AuthPost(), // request config object
});
return await res.json().map(project => {
project.showTags = false;
return project;
});
您也可以在@click 中尝试这样做:
<b-list-group-item v-for="(project, index) in projects" :key="project.path">
{{ project.name }}
<b-btn variant="danger" size="sm" class="list-button" @click="deleteProject(project.path)">Löschen</b-btn>
<b-btn variant="secondary" size="sm" class="list-button" @click="openProject(project.path)">Öffnen</b-btn>
<b-btn variant="secondary" size="sm" class="list-button" @click="flipShowTags(index, project)">Tags</b-btn>
<search-tags v-show="project.showTags" style="margin-top: 1em;"></search-tags>
</b-list-group-item>
methods: {
flipShowTags(index, obj) {
this.$set(this.projects, index, {...obj, showTags: !obj.showTags})
}
}
如果是因为它没有反应
问题就在这里...
this.projects = JSON.parse(await res.text()); this.projects.forEach((project) => { project.showTags = false; });
像这样添加showTags
属性不是响应式的,这就是为什么 Vue 不会像您期望的那样对更改做出反应。
请参阅 更改检测注意事项
Vue 无法检测到属性添加或删除。 由于 Vue 在实例初始化期间执行 getter/setter 转换过程,因此数据对象中必须存在一个属性,以便 Vue 对其进行转换并使其具有反应性。
当您将值分配给projects
数据属性时,您需要存在所有反应性属性。 例如
export default {
data: () => ({
projects: [],
isLoading: true,
// etc
}),
methods: {
async loadAllProjects () {
try {
this.isLoading = true;
const res = await fetch(store.state.urlEndpoints.getProjects, {
...AuthPost(), // request config object
});
if (!res.ok) {
throw new Error(`${res.status}: ${await res.text()}`)
}
// note that the value assigned to `this.projects` now has the
// `showTags` property at the time of assignment
this.projects = (await res.json()).map(project => ({
...project,
showTags: false
}))
} catch (err) {
this.isError = true;
}
this.isLoading = false
}
},
created () {
this.loadAllProjects()
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.