繁体   English   中英

使用 Vue 在 v-for 中进行条件渲染

[英]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>

但这也行不通。

我认为这不会那么困难,我在这里遗漏了一些东西。 任何帮助表示赞赏。

编辑 1

从我的代码中可以看出,整个列表都放在<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;
},

编辑 2

我的数据首先定义如下:

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM