【问题标题】:update seperate component based on select value根据选择值更新单独的组件
【发布时间】:2021-11-17 19:59:40
【问题描述】:

我的博客页面包含两个不同的组件 filtercard

<template>
  <div>

    <div v-if='$apollo.loading'>Fetching data...</div>
    
    <div v-else>
      <FilterComponent :categories='categories' />

      <Card :blogs='blogs' />
    </div>

  </div>
</template>

<script>
import blogsGQL from '../graphql/blog/blog.graphql'
import categoriesGQL from '../graphql/category/category.graphql'
import Card from '~/components/Blog/Card'
import FilterComponent from '~/components/Blog/FilterComponent'

export default {
  name: 'Blogs',
  components: { FilterComponent, Card },
  layout: 'blog',

  apollo: {
    blogs: {
      query: blogsGQL,
      prefetch: true
    },
    categories: {
      query: categoriesGQL,
      prefetch: true
    }
  }
}
</script>

在我的 filterComponent 内部,我有一个由来自数据库的类别填充的选择下拉列表,当我选择这些类别时,会触发一个 graphql 查询,该查询会获取由 category_id 过滤的新博客文章

<template>
  <div class='w-full bg-gray-200 dark:bg-gray-900 py-10'>
    <div class='container mx-auto px-6 flex items-start justify-center'>

      <form id='filter' @submit.prevent>
        <div class='mt-16 flex flex-col  w-full'>
          <select id='category'
                  v-model.number='selected'
                  class='dark:border-gray-700 pl-3 py-3 shadow-sm rounded text-sm focus:outline-none focus:border-indigo-700 bg-transparent placeholder-gray-500 text-gray-500 dark:text-gray-400'
          >
            <option value='0' selected>Choose category</option>
            <option v-for='category in categories' :key='category.id' :value='category.id'>
              {{ category.category }}
            </option>
          </select>
        </div>
      </form>
    </div>
  </div>
</template>

<script>
import blogPostByCategoryId from '~/graphql/blog/blogByCategoryId.graphql'

export default {
  name: 'FilterComponent',

  props: {
    categories: {
      type: Array,
      required: true
    }
  },

  data() {
    return {
      selected: 0
    }
  },

  apollo: {
    blogPostByCategoryId: {
      query: blogPostByCategoryId,
      variables() {
        return {
          category_id: this.selected
        }
      },
      skip() {
        return !this.selected
      }
    }
  }
}
</script>

但是如何使用从过滤器组件收到的新数据更新卡片组件?是否可以将blogPostByCategoryId 结果以某种方式传回卡片组件?

编辑:

我设法让一些部分工作,我目前在我的 nuxt 的 store 目录中有一个 index.js 文件,该文件包含以下代码:

import blogsGQL from '~/graphql/blog/blog.graphql'

export const state = () => ({
  blogs: []
})


export const actions = {
  async nuxtServerInit({ commit }, context) {
    const client = context.app.apolloProvider.defaultClient

    const response = await client.query({ query: blogsGQL })

    commit('setBlogs', response.data.blogs)
  }
}

export const mutations = {
  setBlogs(state, blogs) {
    state.blogs.push(blogs)
  },

  filter(state, categoryId) {
    state.blogs.map(blog => blog.category_id === categoryId)
  }
}

我查询我的 graphql api 并将所有帖子放在我的状态中,然后在我的父组件中我将数据传递给我的卡片组件,如下所示:

      <Card :blogs='blogs' />


  computed: {
    blogs() {
      return this.$store.state.blogs
    }
  },

现在,我不确定我是不是脑子有问题,但是当我尝试像这样循环卡组件内部的数组时:

        <div v-for='blog in blogs'

我得到的只是空白?我在控制台记录了blogs 数组,数据就在其中。

编辑 2:

所以我解决了我之前的问题,必须使用state.blogs.push(...blogs) 才能让它工作。

在阅读了一些文档后,我还稍微更改了文件,现在我使用 getter 来获取过滤器博客:

export const getters = {
  filterdBlogs: state => (categoryId) => {
    return state.blogs.filter(blog => blog.category_id === categoryId)
  }
}

我在我的 filterComponent 内部的 apollo update 方法上调用此方法:

      update(data) {
        const id = data.blogByCategoryId[0].category_id

        this.$store.getters.filterdBlogs(id)
      }

这似乎工作,控制台记录 filterdblogs 方法返回正确的项目,但我的卡组件没有被更新。

编辑 3

所以我改变了卡片组件而不是通过道具传递它我现在这样做

//卡片组件

<template>
  <div class='w-full bg-gray-200 dark:bg-gray-900 py-10'>
    <div class='container mx-auto px-6 flex justify-center'>
      <div class='w-full'>
        <div v-for='blog in blogs'
             :key='blog.id'
             class='flex flex-row  mx-auto bg-white dark:bg-gray-800 justify-center shadow rounded'>
          <div class='w-full lg:w-1/3 px-12 flex flex-col items-center py-10'>
            <div class='w-24 h-24 mb-3 p-2 rounded-full bg-gray-200 dark:bg-gray-700 flex items-center justify-center'>
              <img class='w-full h-full overflow-hidden object-cover rounded-full'
                   :src='`https://tuk-cdn.s3.amazonaws.com/assets/components/grid_cards/gc_${blog.id}.png`'
                   alt='avatar' />
            </div>
            <h2 class='text-gray-800 dark:text-gray-100 text-xl tracking-normal font-medium mb-1'>{{ blog.title }}</h2>
            <p class='text-gray-600 dark:text-gray-100 text-sm tracking-normal font-normal mb-8 text-center w-10/12'>
              {{ blog.big_text }}</p>
            <p class='text-gray-600 dark:text-gray-100 text-sm tracking-normal font-normal mb-8 text-center w-10/12'>
              {{ blog.small_text }}</p>
            <div class='flex items-start'>
              <div class='mx-6 border-l border-r'>
                <NuxtLink :to='`blog/${blog.slug}`'>
                  <button type='submit'
                          class='group relative w-full flex justify-center py-2 px-4 border border-transparent text-sm font-medium rounded-md text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500'>
                    Read more
                  </button>
                </NuxtLink>
              </div>
              <div class='mx-4 border-l border-r'>
                <h2
                  class='group relative w-full flex justify-center py-2 px-4 border border-transparent text-sm font-medium rounded-md text-white text-black'>
                  Posted on {{ blog.created_at | formatDate }}
                </h2>
              </div>
              <div class='mx-4 border-l border-r'>
                <h2
                  class='group relative w-full flex justify-center py-2 px-4 border border-transparent text-sm font-medium rounded-md text-white text-black'>
                  Updated on {{ blog.updated_at | formatDate }}

                </h2>
              </div>
            </div>
          </div>
        </div>
        <!-- Card code block end -->
      </div>
    </div>
  </div>
</template>
<script>
export default {
  name: 'Card',

  filters: {
    formatDate: (dateStr) =>
      Intl.DateTimeFormat('us-EN').format(new Date(dateStr))
  },

  computed: {
    blogs() {
      return this.$store.state.blogs
    }
  }

}
</script>

现在,当我在下拉菜单中选择一个值时,状态不会更新;

//下拉组件

<template>
  <div class='w-full bg-gray-200 dark:bg-gray-900 py-10'>
    <div class='container mx-auto px-6 flex items-start justify-center'>

      <form id='filter' @submit.prevent>
        <div class='mt-16 flex flex-col  w-full'>
          <select id='category'
                  v-model.number='selected'
                  class='dark:border-gray-700 pl-3 py-3 shadow-sm rounded text-sm focus:outline-none focus:border-indigo-700 bg-transparent placeholder-gray-500 text-gray-500 dark:text-gray-400'
          >
            <option value='0' selected>Choose category</option>
            <option v-for='category in categories' :key='category.id' :value='category.id'>
              {{ category.category }}
            </option>
          </select>
        </div>
      </form>
    </div>
  </div>
</template>

<script>
import blogPostByCategoryId from '~/graphql/blog/blogByCategoryId.graphql'

export default {
  name: 'FilterComponent',

  props: {
    categories: {
      type: Array,
      required: true
    }
  },

  data() {
    return {
      selected: 0
    }
  },

  apollo: {
    blogPostByCategoryId: {
      query: blogPostByCategoryId,
      variables() {
        return {
          category_id: this.selected
        }
      },
      skip() {
        return !this.selected
      },
      update(data) {
        const id = data.blogByCategoryId[0].category_id

        this.$store.commit('filter', {
          categoryId: id
        })
      }
    }
  }
}
</script>

<style scoped>

</style>

我的过滤方法如下所示:

export const mutations = {
  filter(state, payload) {
    return state.blogs.filter(blog => blog.category_id === payload.categoryId)
  }
}

【问题讨论】:

  • 您可以使用 Vuex,从 filter component 发送新数据,在 card component 中它会响应式更新
  • 你是这样写的&lt;div v-for='(blog, key) in blogs' :key="key"&gt;{{blog}}&lt;/div&gt;还是真的&lt;div v-for='blog in blogs'
  • 设法解决了这个问题,必须使用state.blogs.push(...blogs) 才能让它工作,但现在我的组件在选择过滤器后没有刷新,将为您再次编辑
  • 分享你的卡片组件,其中数据未更新
  • 对于第一个 &lt;Card :blogs='blogs' /&gt; 为什么你通过 props 抛出 Vuex 状态?

标签: javascript vue.js graphql nuxt.js


【解决方案1】:

你在改变 Vuex 状态时犯了错误。正确的变异格式:

export const state = () => ({
     blogs: [],
     search: null //if you want multiselect you must use array [] with ids
})

export const getters = {
  filterBlog(state, payload) {
    return state.search ? state.blogs.filter(blog => +blog.category_id === +state.search) : state.blogs
  }
}

export const mutations = {
  setBlogs(state, blogs) {
    state.blogs.push(blogs)
  },
  search(state, id) {
     state.search = id
  }
}

卡片组件:

 <div v-for='blog in blogs'>

  computed: {
    blogs() {
      return this.$store.getters.filterBlog
    }
  }

下拉组件:

update(data) {
     const id = data.blogByCategoryId[0].category_id

     this.$store.commit('search', id)
 }

在 Vuex 中,您可以直接更改当前状态,而不是像在 Redux Reducer 中那样返回一个新状态。

【讨论】:

  • 嘿,好像元素正在从数组中删除?例如如果我按 category_id 过滤 1 项目被正确过滤,但如果我选择 category_id 2 之后,则数组内没有项目。即使我添加了一个 category_id 2
  • 如果你想过滤模板的状态,你必须在getter中进行,如果你想更新状态,你必须在mutations中进行
  • 我编辑了答案
  • 已编辑,带有下拉过滤器
  • 嘿,现在可以了。谢谢!
猜你喜欢
  • 2015-01-22
  • 2021-11-10
  • 1970-01-01
  • 1970-01-01
  • 2012-07-08
  • 1970-01-01
  • 1970-01-01
  • 2019-09-07
  • 2020-05-16
相关资源
最近更新 更多