# Vue VideoJS in UIBuilder

**URL:** https://discourse.nodered.org/t/vue-videojs-in-uibuilder/40380
**Category:** Dashboard
**Tags:** uibuilder
**Created:** [4 February 2021 14:32 UTC](https://discourse.nodered.org/t/vue-videojs-in-uibuilder/40380 "2021-02-04T14:32:53Z")
**Posts on this page:** 9
**Page:** 2

<div class="post-metadata">

### Author: ![j2l](https://avatars.discourse-cdn.com/v4/letter/j/ee59a6/32.png) [@j2l](https://discourse.nodered.org/u/j2l)
#### Post date: [6 February 2021 17:27 UTC](https://discourse.nodered.org/t/vue-videojs-in-uibuilder/40380/21 "2021-02-06T17:27:40Z")

</div>

> [@TotallyInformation](#):
>
> However, good news is that we now have a working example of an embedded video player on the uibuilder WIKI.

I agree! Being able to start Vue with Node-Red data plasticity helped by uibuilder is really great, but it's a pain to understand at first, too many details and a hard time to tackle problems. Steep learning curve!  
Hopefully a great community goes a long way 😄

---

<div class="post-metadata">

### Author: ![j2l](https://avatars.discourse-cdn.com/v4/letter/j/ee59a6/32.png) [@j2l](https://discourse.nodered.org/u/j2l)
#### Post date: [6 February 2021 17:36 UTC](https://discourse.nodered.org/t/vue-videojs-in-uibuilder/40380/22 "2021-02-06T17:36:22Z")

</div>

Actually I want to call different videos so I guess the container is the best way in my case.  
Removing the first player and moving options to inde.js

```auto
    data: {
        player: null,
        videoOptions: {
			class:"video-js",
            controls:true,
            preload:"auto",
            width:640,
            height:264,
			sources: [

```

throws new errors:

```auto
[Vue warn]: Error in mounted hook: "TypeError: The element or ID supplied is not valid. (videojs)"

(found in <Root>) vue.js:634:17
    VueJS 11
    <anonymous> http://192.168.1.236:1880/videojs/index.js:3
TypeError: The element or ID supplied is not valid. (videojs)

```

---

<div class="post-metadata">

### Author: ![TotallyInformation](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/totallyinformation/32/31_2.png) [@TotallyInformation](https://discourse.nodered.org/u/TotallyInformation)
#### Post date: [6 February 2021 17:44 UTC](https://discourse.nodered.org/t/vue-videojs-in-uibuilder/40380/23 "2021-02-06T17:44:39Z")

</div>

> [@j2l](#):
>
> Actually I want to call different videos so I guess the container is the best way in my case.

Nope, I don't think so, you are over-complecating things. A single player will show as many videos as you like (1 at a time), simply send a new one from Node-RED. Or, better still, send a list of videos with metadata from Node-RED to your front-end, use that list to build a dropdown and use the change event on the dropdown to load the appropriate video and start playing.

---

<div class="post-metadata">

### Author: ![j2l](https://avatars.discourse-cdn.com/v4/letter/j/ee59a6/32.png) [@j2l](https://discourse.nodered.org/u/j2l)
#### Post date: [6 February 2021 19:20 UTC](https://discourse.nodered.org/t/vue-videojs-in-uibuilder/40380/24 "2021-02-06T19:20:17Z")

</div>

Then I still miss something in the setup 🙂  
How would I do that?  
What about the error?

---

<div class="post-metadata">

### Author: ![UnborN](https://avatars.discourse-cdn.com/v4/letter/u/4491bb/32.png) [@UnborN](https://discourse.nodered.org/u/UnborN)
#### Post date: [6 February 2021 19:43 UTC](https://discourse.nodered.org/t/vue-videojs-in-uibuilder/40380/25 "2021-02-06T19:43:50Z")

</div>

> [@j2l](#):
>
> ```auto
> Error in mounted hook: "TypeError: The element or ID supplied is not valid. (videojs)"
> 
> ```

The error gives a clear indication where to look.  
You mentioned that you removed the first video player component from Julian's example.  
But in index.js in mounted() the code tries to initialize a player in an element with id 'my-video' and none is found.

```auto
 app.player = videojs('my-video')
        console.log(app.player)

```

I didnt study the code in depth but try use an `id="my-video"` on the second player that you have in your modified code.

---

<div class="post-metadata">

### Author: ![j2l](https://avatars.discourse-cdn.com/v4/letter/j/ee59a6/32.png) [@j2l](https://discourse.nodered.org/u/j2l)
#### Post date: [7 February 2021 18:33 UTC](https://discourse.nodered.org/t/vue-videojs-in-uibuilder/40380/26 "2021-02-07T18:33:54Z")

</div>

Thanks @UnborN  
In index.html, I'd like to keep `<video-player :options="videoOptions"/>` to understand the uibuilder-Vue mechanic.  
So here's my index.js:

```auto
'use strict'

new Vue({
    el: '#app',
    
    components: {
        'video-player': httpVueLoader('video-player.vue'),
    }, // --- End of components --- //
    
    data: {
        player: null,
        videoOptions: {
			id:"video-player",
			class:"video-js",
            controls:true,
            preload:"auto",
            width:640,
            height:264,
			sources: [
				{
					src:
						'//vjs.zencdn.net/v/oceans.mp4',
					  type: 'video/mp4'
				}
			]
		},
		videoUrl: '',
		
        startMsg : 'Vue has started, waiting for messages',
        feVersion : '',
        counterBtn : 0,
        inputText : null,
        inputChkBox : false,
        socketConnectedState : false,
        serverTimeOffset : '[unknown]',
        imgProps : { width: 75, height: 75 },

        msgRecvd : '[Nothing]',
        msgsReceived: 0,
        msgCtrl : '[Nothing]',
        msgsControl : 0,

        msgSent : '[Nothing]',
        msgsSent : 0,
        msgCtrlSent : '[Nothing]',
        msgsCtrlSent: 0,

        isLoggedOn : false,
        userId : null,
        userPw : null,
        inputId : '',
    }, // --- End of data --- //
    computed: {
        hLastRcvd: function() {
            var msgRecvd = this.msgRecvd
            if (typeof msgRecvd === 'string') return 'Last Message Received = ' + msgRecvd
            else return 'Last Message Received = ' + this.syntaxHighlight(msgRecvd)
        },
        hLastSent: function() {
            var msgSent = this.msgSent
            if (typeof msgSent === 'string') return 'Last Message Sent = ' + msgSent
            else return 'Last Message Sent = ' + this.syntaxHighlight(msgSent)
        },
        hLastCtrlRcvd: function() {
            var msgCtrl = this.msgCtrl
            if (typeof msgCtrl === 'string') return 'Last Control Message Received = ' + msgCtrl
            else return 'Last Control Message Received = ' + this.syntaxHighlight(msgCtrl)
        },
        hLastCtrlSent: function() {
            var msgCtrlSent = this.msgCtrlSent
            if (typeof msgCtrlSent === 'string') return 'Last Control Message Sent = ' + msgCtrlSent
            //else return 'Last Message Sent = ' + this.callMethod('syntaxHighlight', [msgCtrlSent])
            else return 'Last Control Message Sent = ' + this.syntaxHighlight(msgCtrlSent)
        },
    }, // --- End of computed --- //
    methods: {
        increment: function(event) {
            console.log('Button Pressed. Event Data: ', event)

            // Increment the count by one
            this.counterBtn = this.counterBtn + 1
            var topic = this.msgRecvd.topic || 'uibuilder/vue'
            uibuilder.send( {
                'topic': topic,
                'payload': {
                    'type': 'counterBtn',
                    'btnCount': this.counterBtn,
                    'message': this.inputText,
                    'inputChkBox': this.inputChkBox
                }
            } )

        }, // --- End of increment --- //

        doLogon: function() {
            uibuilder.logon( {
                'id': this.inputId,
            } )
        }, // --- End of doLogon --- //

        doLogoff: function() {
            uibuilder.logoff()
        }, // --- End of doLogon --- //

        // return formatted HTML version of JSON object
        syntaxHighlight: function(json) {
            json = JSON.stringify(json, undefined, 4)
            json = json.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;')
            json = json.replace(/("(\\u[a-zA-Z0-9]{4}|\\[^u]|[^\\"])*"(\s*:)?|\b(true|false|null)\b|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?)/g, function (match) {
                var cls = 'number'
                if (/^"/.test(match)) {
                    if (/:$/.test(match)) {
                        cls = 'key'
                    } else {
                        cls = 'string'
                    }
                } else if (/true|false/.test(match)) {
                    cls = 'boolean'
                } else if (/null/.test(match)) {
                    cls = 'null'
                }
                return '<span class="' + cls + '">' + match + '</span>'
            })
            return json
        }, // --- End of syntaxHighlight --- //
    }, // --- End of methods --- //

    // Available hooks: beforeCreate,created,beforeMount,mounted,beforeUpdate,updated,beforeDestroy,destroyed, activated,deactivated, errorCaptured

    /** Called after the Vue app has been created. A good place to put startup code */
    created: function() {
        // Example of retrieving data from uibuilder
        this.feVersion = uibuilder.get('version')

        / **** REQUIRED** Start uibuilder comms with Node-RED @since v2.0.0-dev3
         * Pass the namespace and ioPath variables if hosting page is not in the instance root folder
         * e.g. If you get continual `uibuilderfe:ioSetup: SOCKET CONNECT ERROR` error messages.
         * e.g. uibuilder.start('/uib', '/uibuilder/vendor/socket.io') // change to use your paths/names
         * @param {Object=|string=} namespace Optional. Object containing ref to vueApp, Object containing settings, or String IO Namespace override. changes self.ioNamespace from the default.
         * @param {string=} ioPath Optional. changes self.ioPath from the default
         * @param {Object=} vueApp Optional. Reference to the VueJS instance. Used for Vue extensions.
         */
        uibuilder.start(this) // Single param passing vue app to allow Vue extensions to be used.

        //console.log(this)
    },

    /** Called once all Vue component instances have been loaded and the virtual DOM built */
    mounted: function(){
        //console.debug('[indexjs:Vue.mounted] app mounted - setting up uibuilder watchers')
        
        var app = this // Reference to `this` in case we need it for more complex functions

        app.player = videojs('video-player')
        console.log(app.player)

        // If msg changes - msg is updated when a standard msg is received from Node-RED over Socket.IO
        // newVal relates to the attribute being listened to.
        uibuilder.onChange('msg', function(msg){
            //console.info('[indexjs:uibuilder.onChange] msg received from Node-RED server:', msg)
            app.msgRecvd = msg
            app.msgsReceived = uibuilder.get('msgsReceived')
            
            if ( msg.payload.src ) {
                app.player.src(msg.payload)
                app.player.ready(function() {
                    app.player.play();
                })
            }
        })

        //#region ---- Debug info, can be removed for live use ---- //

        /** You can use the following to help trace how messages flow back and forth.
         * You can then amend this processing to suite your requirements.
         */

        // If we receive a control message from Node-RED, we can get the new data here - we pass it to a Vue variable
        uibuilder.onChange('ctrlMsg', function(msg){
            //console.info('[indexjs:uibuilder.onChange:ctrlMsg] CONTROL msg received from Node-RED server:', msg)
            app.msgCtrl = msg
            app.msgsControl = uibuilder.get('msgsCtrl')
        })

        /** You probably only need these to help you understand the order of processing
         * If a message is sent back to Node-RED, we can grab a copy here if we want to
         */
        uibuilder.onChange('sentMsg', function(msg){
            //console.info('[indexjs:uibuilder.onChange:sentMsg] msg sent to Node-RED server:', msg)
            app.msgSent = msg
            app.msgsSent = uibuilder.get('msgsSent')
        })

        /** If we send a control message to Node-RED, we can get a copy of it here */
        uibuilder.onChange('sentCtrlMsg', function(msg){
            //console.info('[indexjs:uibuilder.onChange:sentCtrlMsg] Control message sent to Node-RED server:', msg)
            app.msgCtrlSent = msg
            app.msgsCtrlSent = uibuilder.get('msgsSentCtrl')
        })

        /** If Socket.IO connects/disconnects, we get true/false here */
        uibuilder.onChange('ioConnected', function(connected){
            //console.info('[indexjs:uibuilder.onChange:ioConnected] Socket.IO Connection Status Changed to:', connected)
            app.socketConnectedState = connected
        })
        /** If Server Time Offset changes */
        uibuilder.onChange('serverTimeOffset', function(serverTimeOffset){
            //console.info('[indexjs:uibuilder.onChange:serverTimeOffset] Offset of time between the browser and the server has changed to:', serverTimeOffset)
            app.serverTimeOffset = serverTimeOffset
        })

        /** If user is logged on/off */
        uibuilder.onChange('isAuthorised', function(isAuthorised){
            //console.info('[indexjs:uibuilder.onChange:isAuthorised] isAuthorised changed. User logged on?:', isAuthorised)
            //console.log('authData: ', uibuilder.get('authData'))
            //console.log('authTokenExpiry: ', uibuilder.get('authTokenExpiry'))
            app.isLoggedOn = isAuthorised
        })

        //#endregion ---- Debug info, can be removed for live use ---- //

    } // --- End of mounted hook --- //

}) // --- End of app1 --- //

// EOF

```

And in Video-Player.vue:

```auto
<template>
    <div>
        <video ref="videoPlayer" id="video-player" class="video-js"></video>
    </div>
</template>
...

```

As you can see, no more my-video  
But still, I get the same error.

I just don't get how to inject `<video>` options from NR.  
Would you or @TotallyInformation please show it to me?

---

<div class="post-metadata">

### Author: ![UnborN](https://avatars.discourse-cdn.com/v4/letter/u/4491bb/32.png) [@UnborN](https://discourse.nodered.org/u/UnborN)
#### Post date: [7 February 2021 20:24 UTC](https://discourse.nodered.org/t/vue-videojs-in-uibuilder/40380/27 "2021-02-07T20:24:43Z")

</div>

One way to control the `<video-player :options="videoOptions"/>` which is a sub-component to the main Vue app is to give it a ref (reference) so you can select and then control that element.

` <video-player ref="myVideo" :options="videoOptions"/>`

Then you can comment out the lines `// app.player = videojs('my-video')` as these were used for creating a video player for the `<video id="my-video" ...` example.

Now for accessing and controlling the vue component player modify the lines in uibuilder.onChange ..

```auto
uibuilder.onChange('msg', function(msg){
            console.info('[indexjs:uibuilder.onChange] msg received from Node-RED server:', msg)
            console.log("myVideo", app.$refs.myVideo.player) // log the nested player

            let myVideo = app.$refs.myVideo.player // save the nested player in variable 
            app.msgRecvd = msg
            app.msgsReceived = uibuilder.get('msgsReceived')
            
            if ( msg.payload.src ) {   
                myVideo.src(msg.payload) // change src for player
                myVideo.ready(function() {
                    myVideo.play(); // play
                })
            }
        })

```

---

<div class="post-metadata">

### Author: ![j2l](https://avatars.discourse-cdn.com/v4/letter/j/ee59a6/32.png) [@j2l](https://discourse.nodered.org/u/j2l)
#### Post date: [8 February 2021 08:52 UTC](https://discourse.nodered.org/t/vue-videojs-in-uibuilder/40380/28 "2021-02-08T08:52:19Z")

</div>

Thank you very much @UnborN !  
As I said, I'm learning Vue using uibuilder 🙂  
You spotted on the mechanic that is still blurry for me.

---

<div class="post-metadata">

### Author: ![system](https://us1.discourse-cdn.com/flex026/uploads/nodered/original/1X/d073cd938eafa2e558d7c2cd59003b3ef4963033.png) [@system](https://discourse.nodered.org/u/system)
#### Post date: [22 February 2021 08:52 UTC](https://discourse.nodered.org/t/vue-videojs-in-uibuilder/40380/29 "2021-02-22T08:52:54Z")

</div>

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.

[Previous page](https://discourse.nodered.org/t/vue-videojs-in-uibuilder/40380.md?page=1)
