【问题标题】:Is it possible to sync Vuejs components displayed multiple times on the same page?是否可以同步在同一页面上多次显示的 Vuejs 组件?
【发布时间】:2023-03-12 23:37:01
【问题描述】:

我有一个显示项目的网页。每个项目都有一个按钮(vuejs 组件),允许用户切换(添加/删除)该项目到他的收藏。

这是组件:

<template lang="html">
        <button type="button" @click="toggle" name="button" class="btn" :class="{'btn-danger': dAdded, 'btn-primary': !dAdded}">{{ dText }}</button>
    </template>

<script>
export default {
    props: {
        added: Boolean,
        text: String,
        id: Number,
    },
    data() {
        return {
            dAdded: this.added,
            dText: this.text,
            dId: this.id
        }
    },
    watch: {
        added: function(newVal, oldVal) { // watch it
            this.dAdded = this.added
        },
        text: function(newVal, oldVal) { // watch it
            this.dText = this.text
        },
        id: function(newVal, oldVal) { // watch it
            this.dId = this.id
        }
    },
    methods: {
        toggle: function(event) {
            axios.post(route('frontend.user.profile.pop.toggle', {
                    pop_id: this.dId
                }))
                .then(response => {
                    this.dText = response.data.message
                    let success = response.data.success
                    this.dText = response.data.new_text
                    if (success) {
                        this.dAdded = success.attached.length
                        let cardPop = document.getElementById('card-pop-'+this.dId);
                        if(cardPop)
                            cardPop.classList.toggle('owned')
                    }
                })
                .catch(e => {
                    console.log(e)
                })
        }
    }
}
</script>

对于每个项目,用户还可以打开一个模式,通过单击此链接加载:

<a href="#" data-toggle="modal" data-target="#popModal" @click="id = {{$pop->id}}">
    <figure>
        <img class="card-img-top" src="{{ URL::asset($pop->img_path) }}" alt="Card image cap">
    </figure>
</a>

modal也是一个Vuejs组件:

<template>
    <section id="pop" class="h-100">
        <div class="card">
            <div class="container-fluid">
                <div class="row">

                    <div class="col-12 col-lg-1 flex-column others d-none d-xl-block">
                        <div class="row flex-column h-100">

                            <div v-for="other_pop in pop.other_pops" class="col">
                                <a :href="route('frontend.pop.collection.detail', {collection: pop.collection.slug, pop: other_pop.slug})">
                                        <img :src="other_pop.img_path" :alt="'{{ other_pop.name }}'" class="img-fluid">
                                    </a>
                            </div>

                            <div class="col active order-3">
                                <img :src="pop.img_path" :alt="pop.name" class="img-fluid">
                            </div>

                        </div>
                    </div>

                    <div class="col-12 col-lg-6 content text-center">
                        <div class="row">
                            <div class="col-12">
                                <img :src="pop.img_path" :alt="pop.name" class="img-fluid">
                            </div>

                            <div class="col-6 text-right">
                                <toggle-pop :id="pop.id" :added="pop.in_user_collection" :text="pop.in_user_collection ? 'Supprimer' : 'Ajouter'"></toggle-pop>
                            </div>
                            <div class="col-6 text-left">
                                <!-- <btnaddpopwhishlist :pop_id="propid" :added="pop.in_user_whishlist" :text="pop.in_user_whishlist ? 'Supprimer' : 'Ajouter'"></btnaddpopwhishlist> -->
                            </div>
                        </div>


                    </div>
                    <div class="col-12 col-lg-5 infos">
                        <div class="header">
                            <h1 class="h-100">{{ pop.name }}</h1>
                        </div>
                        <div class="card yellow">
                            <div class="card p-0">
                                <div class="container-fluid">
                                    <div class="row">
                                        <div class="col-3 py-2">

                                        </div>
                                        <div class="col-6 py-2 bg-lightgray">
                                            <h4>Collection:</h4>
                                            <h3>{{ pop.collection ? pop.collection.name : '' }}</h3>
                                        </div>
                                        <div class="col-3 py-2 bg-lightgray text-center">
                                            <a :href="route('frontend.index') + 'collections/' + pop.collection.slug" class="btn-round right white"></a>
                                        </div>
                                    </div>
                                </div>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </section>
</template>

<script>
export default {
    props: {
        id: Number
    },
    data() {
        return {
            pop: {
                collection: {

                }
            }
        }
    },
    ready: function() {
        if (this.propid != -1)
            this.fetchData()
    },
    watch: {
        id: function(newVal, oldVal) { // watch it
            // console.log('Prop changed: ', newVal, ' | was: ', oldVal)
            this.fetchData()
        }
    },
    computed: {
        imgSrc: function() {
            if (this.pop.img_path)
                return 'storage/images/pops/' + this.pop.img_path
            else
                return ''
        }
    },
    methods: {
        fetchData() {
            axios.get(route('frontend.api.v1.pops.show', this.id))
                .then(response => {
                    // JSON responses are automatically parsed.
                    // console.log(response.data.data.collection)
                    this.pop = response.data.data
                })
                .catch(e => {
                    this.errors.push(e)
                })
            // console.log('fetchData')
        }
    }
}
</script>

这是我的 app.js 脚本:

window.Vue = require('vue');

Vue.component('pop-modal', require('./components/PopModal.vue'));
Vue.component('toggle-pop', require('./components/TogglePop.vue'));

const app = new Vue({
    el: '#app',
    props: {
        id: Number
    }
});

我想同步名为toggle-pop 的组件的状态,我该如何实现呢?一个由 Blade 模板(laravel)渲染,另一个由pop-modal 组件渲染。但它们都是一样的,显示在不同的地方。

谢谢。

【问题讨论】:

    标签: vuejs2 vue-component laravel-blade


    【解决方案1】:

    您可以将状态对象作为属性传递给toggle-pop 组件。他们可以使用这个属性来存储/修改他们的状态。通过这种方式,您可以让多组组件共享状态。

    你的组件可能变成:

    <template lang="html">
      <button type="button" @click="toggle" name="button" class="btn" :class="{'btn-danger': sstate.added, 'btn-primary': !sstate.added}">{{ sstate.text }}</button>
    </template>
    
    <script>
    export default {
      props: {
        sstate: {
          type: Object,
          default: function() {
            return { added: false, text: "", id: -1 };
          }
        }
      },
      data() {
        return {};
      },
      methods: {
        toggle: function(event) {
          axios.post(route('frontend.user.profile.pop.toggle', {
            pop_id: this.sstate.id
          }))
            .then(response => {
                this.sstate.text = response.data.message
                let success = response.data.success
                this.sstate.text = response.data.new_text
                if (success) {
                    this.sstate.ddded = success.attached.length
                    let cardPop = document.getElementById('card-pop-'+this.sstate.id);
                    if(cardPop)
                        cardPop.classList.toggle('owned')
                }
            })
            .catch(e => {
              console.log(e)
            })
      }
    };
    </script>
    

    现场演示

    https://codesandbox.io/s/vq8r33o1w7

    【讨论】:

      【解决方案2】:

      如果您100% 确定所有toggle-pop 组件应始终具有相同的状态,您可以选择不将data 定义为函数。只需将其声明为对象即可。

        data: {
          dAdded: this.added,
          dText: this.text,
          dId: this.id
        }
      

      https://vuejs.org/v2/guide/components.html#data-Must-Be-a-Function 中提到

      组件的数据选项必须是一个函数,这样每个实例 可以维护返回数据对象的独立副本

      如果 Vue 没有这个规则,点击一个按钮会影响 所有其他实例的数据

      由于要同步所有toggle-pop组件实例的数据,所以不必遵循data option must be a function规则。

      【讨论】:

      • 你的想法很有趣,但不适合我的情况。事实上,我想为一对切换弹出共享相同的状态。对于我网页上的每个“项目”,都有两个具有相同确切值的切换弹出组件实例(这两个需要同步,但不需要与其他的同步)。
      猜你喜欢
      • 2017-10-21
      • 2018-02-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-11-09
      • 2017-01-17
      相关资源
      最近更新 更多