Vuejs2 keeping counter up to date

2019-09-16 14:14发布

问题:

I am trying to get a counter value kept up to date dynamically for a list of users in a chat room using pusher. The variable usersCount is maintained by the vue instance and served in a template. The vue debug tool shows it as undefined in the code below. I am not passing the variable correctly to the template from the instance.

chat-player-list template is called like this:

<div class="modal-body" id="sidebar-chat">

    <div class="row">
        <chat-player-list ref="users" :users="users"></chat-player-list>
    </div>

    <hr/>

    <div class="row">
        <div class="col-xs-12">
            <chat-player-messages ref="messages" :messages="messages"></chat-player-messages>
            <h5>Enemies chit chat</h5>
            <chat-player-form v-on:chatmessagesent="addMessage" :player="{{ Auth::user() }}"></chat-player-form>
        </div>
    </div>

</div>

The players all list and render OK, and additionally the rest of the conditional functions work OK. Just the usersCounter not rendering.

My chat.js code contains the main vue instance:

/**
 * Set up Vue components for chat
 */
$(document).ready(function()
{
    Vue.component('chat-player-list', require('../components/chat-player-list.vue'));
    Vue.component('chat-player-messages', require('../components/chat-player-messages.vue'));
    Vue.component('chat-player-form', require('../components/chat-player-form.vue'));

    var chatPlayer = new Vue({
        el: '#sidebar-chat',

        data: function () {
            return {
                game: game,
                messages: [],
                users: [],
                usersCount: 0
            };
        },

        created() {
            this.fetchPlayers();
            this.fetchMessages();

            Echo.join(chat_channel)
                .here((users) => {
                    $.each(users, function(index, value) {
                        $('i#online-' + users[index].id).addClass('faa-ring animated player-online');
                    });
                    this.usersCount = users.length;
                })
                .joining((user) => {
                    $('i#online-' + user.id).addClass('faa-ring animated player-online');
                    this.usersCount = this.usersCount + 1;
                })
                .leaving((user) => {
                    $('i#online-' + user.id).removeClass('faa-ring animated player-online');
                    this.usersCount = this.usersCount - 1;
                })
                .listen('ChatMessageSent', (e) => {
                    this.messages.unshift({
                        message: e.message.message,
                        player: e.player
                    });
                });
        },

        methods: {
            fetchPlayers() {
                this.users = game.progress.status.players_status;
            },

            fetchMessages() {
                axios.get(chat_get_route)
                    .then(response => {
                        this.messages = response.data;
                    });
            },

            addMessage(message) {
                this.messages.unshift(message);

                this.$nextTick(() => {
                    this.$refs.messages.scrollToTop();
                });

                axios.post(chat_send_route, message)
                    .then(response => {
                        console.log(response.data);
                    });
            }
        }
    });
});

In my template chat-player-list.vue I have:

<template>
    <div class="col-xs-12">
        <h5>Enemies online</h5>
        <span id="no-online-players" class="player-label pull-right">{{ usersCount }}</span>

        <hr/>

        <table id="new-game-opponents" class="new-game-opponents">
            <tbody>
            <tr v-for="(user, index) in users" :key="index" :class="[isPlayerTurn(user.owner_id) ? playerTurnClass : '']">
                <td class="player_number">
                    <div :class="['opponent player-' + index++]" v-once>{{ index++ }}</div>
                </td>
                <td class="player-avatar">
                    <img :id="['player_avatar-' + user.owner_id]" class="setup_player_avatar" :src="[user.avatar]" v-once/>
                </td>
                <td class="player-name">
                    <div :id="['player_nickname-' + user.owner_id]" v-once>
                        {{ user.nickname }}&nbsp;&nbsp;
                        <i :class="[isThisPlayer(user.owner_id) ? 'fa fa-user-circle-o' : 'hidden']" aria-hidden="true" style="color:DarkOrange;"></i>
                    </div>
                </td>
                <td class="player-status text-right">
                    <div v-if="isPlayerTurn(user.owner_id)">
                        <span :id="['player_turn-' + user.owner_id]" class="stage-label pull-right">{{ progress }}</span>
                    </div>
                    <div v-else>
                        <i class="fa fa-clock-o" aria-hidden="true" style="margin-right:5px;"></i>
                    </div>
                </td>
                <td class="player-status text-right">
                    <i class="fa fa-comments" :id="['online-' + user.owner_id]" aria-hidden="true"></i>
                </td>
                <td class="player-status text-right">
                    <div v-if="isHuman(user.owner_id)">
                        <i class="fa fa-desktop" aria-hidden="true"></i>
                    </div>
                    <div v-else>
                        <i class="fa fa-user" aria-hidden="true" style="margin-right:2px;"></i>
                    </div>
                </td>
            </tr>
            </tbody>
        </table>
    </div>
</template>

<script>
    export default {
        props: ['users', 'usersCount'],

        data: function () {
            return {
                playerTurnClass: 'next-player-turn',
                nextPlayerId: game.progress.next_player_id,
                progress: game.progress.status.turn_status.current_stage,
                myPlayerId: my_player.id
            }
        },

        methods: {
            isPlayerTurn(thisUserId) {
                return thisUserId == this.nextPlayerId;
            },
            isThisPlayer(thisUserId) {
                return thisUserId == this.myPlayerId;
            },
            isHuman(thisUserOwnerId) {
                return thisUserOwnerId != 'ai';
            }
        }
    };
</script>

What did I miss? Thanks!

回答1:

Data won't be passed to child component automatically, you need manually declare it as prop.

Every component instance has its own isolated scope. This means you cannot (and should not) directly reference parent data in a child component’s template. Data can be passed down to child components using props.

Updated

You should bind usersCount like <chat-player-list ref="users" :users="users" :users-count="usersCount"></chat-player-list>

:a="b" is a shorthand syntax of v-bind. It means passing current instance's data b to the child component as a prop named a. Without this, the child component cannot receive it even though prop:['a'] exists.
So just add :users-count="usersCount".

As Vamsi mentioned, usersCount should be passed as users-count, See: https://vuejs.org/v2/guide/components.html#camelCase-vs-kebab-case



标签: vuejs2