Компонент реквизиты и данные не обновляются родителем - PullRequest
0 голосов
/ 17 апреля 2019

Я сделал кодовый блок моей проблемы здесь https://codepen.io/stevemr/pen/VNQbYe

У меня есть корневой экземпляр Vue, который поддерживает реквизиты для компонента, VideoPlayer. В моем корневом экземпляре есть метод setVideo, который прямо сейчас присваивает некоторые фиктивные значения.

Вот объект, который я использую в данных корневого экземпляра:

video: {
    drive: '',
    filename: '',
    mediaType: '',
},

Вот функция setVideo:

setVideo: function() {
    // Get the drive, filename, and mediaType
    this.video.drive = 'hdd1';
    this.video.filename = 'game-of-thrones_s01e04.mp4';
    this.video.mediaType = 'show';

    // Hide all modals and trigger the display of the video player
    Event.trigger('hideModal');
    Event.trigger('displayVideoPlayer');
},

Класс Event - это просто оболочка для основных событий Vue:

window.Event = new class {
    constructor() {
        this.vue = new Vue();
    }

    trigger(event, data = null) {
        this.vue.$emit(event, data);
    }

    listen(event, callback) {
        this.vue.$on(event, callback);
    }
};

Вот DOM, где инициализирован мой компонент VideoPlayer:

<video-player
    v-bind:drive="video.drive"
    v-bind:filename="video.filename"
    v-bind:media-type="video.mediaType"
></video-player>

И, наконец, вот мой компонент VideoPlayer:

<template>
    <div>
        <div id="movie-container">
            <div
                class="video-loader top-most"
                v-if="showVideoPlayer && !loaded"
            ></div>
            <video
                id="video-player"
                ref="video"
                v-if="showVideoPlayer && src !== ''"
                class="top-most"
                v-bind:class="{ hidden: !loaded }"
                v-on:click="togglePlay"
                controls
                autoplay
            >
                <source v-bind:src="src" v-bind:type="videoType"></source>
            </video>
        </div>

        <div id="time-range-container" v-if="showTimeRange">
            <input
                id="time-range"
                ref="timeRange"
                type="range"
                min="0"
                v-bind:max="duration"
                step="30"
                v-model:value="currentTime"
            />
        </div>
    </div>
</template>

<script>
    export default {
        props: [
            'drive',
            'filename',
            'mediaType',
        ],

        data() {
            return {
                currentTime: 0,
                duration: 0,
                loaded: false,
                showTimeRange: false,
                showVideoPlayer: false,
            }
        },

        computed: {
            src: function() {
                if(this.filename !== '') {
                    return
                        '/video/' + this.drive +
                        '/' + this.mediaType +
                        's/' + this.filename;
                }

                return '';
            },

            videoType: function() {
                var ext = this.filename.split('.')[1];
                var type = '';

                switch(ext) {
                    case 'mk4':
                    case 'm4v':
                        type = 'webm';
                        break;
                    case 'avi':
                        type = 'ogg';
                        break;
                    default:
                        type = ext;
                }

                return 'video/' + type;
            },
        },

        created() {
            Event.listen('displayVideoPlayer', this.display);
        },

        methods: {
            display: function() {
                if(this.src === '') {
                    return;
                }

                this.showVideoPlayer = true;
                this.loaded = false;

                var self = this;

                setTimeout(function() {
                    var interval = setInterval(function() {
                        var video = self.$refs.video;
                        if(video.readyState > 0) {
                            self.loaded = true;
                            self.duration = Math.round(video.duration);
                            self.currentTime = video.currentTime;
                            clearInterval(interval);
                        }
                    }, 500);
                }, 800);
            },

            togglePlay: function() {
                var video = this.$refs.video;

                if(video.paused) {
                    video.play();
                }

                if(!video.paused) {
                    video.pause();
                }
            },
        },
    }
</script>

Когда вызывается setVideo, он должен установить реквизиты компонента VideoPlayer на фиктивные значения, после чего должен отображаться видеоплеер. Но вместо этого, когда происходит событие displayVideoPlayer, для компонентов компонента все еще устанавливаются значения по умолчанию (пустые строки). Самое главное, что вычисляемое свойство src не обновляется до вызова метода display, поэтому функция display сразу же возвращается, ничего не делая.

Похоже, что реквизиты и данные моего компонента не обновляются, хотя я могу видеть с помощью инструментов dev, что они есть. Как будто это происходит недостаточно быстро или что-то в этом роде.

Я попытался сделать src частью данных компонента и установить его в функции отображения с помощью другой функции setSrc. Но случилось то же самое.

Я также пытался переместить Event.listen ('displayVideoPlayer', this.display); в mount () вместо create () тоже ничего не исправил.

Если вы посмотрите на кодовую ручку, то при первом нажатии кнопки для запуска функции setVideo должен отображаться компонент видеопроигрывателя, вместо этого требуется 2 щелчка.

1 Ответ

0 голосов
/ 18 апреля 2019

Кажется, проблема в состоянии гонки между Vue, обновляющим значение, и Вы вызываете display метод:

display: function() {
  console.log(this.src) // ""
  setTimeout(() => console.log(this.src)) // "/video/hdd1/shows/game-of-thrones_s01e04.mp4"
  if(this.src === '') {
    return
  }

Это означает, что вы вызываете метод display до обновления значения.

Один из способов решить проблему - добавить задержку перед вызовом. display метод:

setVideo: function() {
  this.video.drive = 'hdd1'
  this.video.filename = 'game-of-thrones_s01e04.mp4'
  this.video.mediaType = 'show'
  setTimeout(() => {
    Event.trigger('displayVideoPlayer')
  })

Но я думаю, что в будущем это может вызвать больше проблем. Если вы хотите положиться на реквизит, вы должны вместо этого использовать шаблон наблюдателя:

watch: {
  src (src) {
    if(src === '') {
      return
    }
    // ... display
  }
}

Или передавайте эти значения через событие, а не на реквизитах вроде:

Event.trigger('displayVideoPlayer', this.video)
...