Use Vimeo Player SDK with Nuxt.js - vue.js

I want to use the library like Vimeo Player SDK with Nuxt.js.
<template>
<iframe
id="video01"
ref="video01"
src="https://player.vimeo.com/video/{videoID}">
</iframe>
</template>
<script>
import Player from '#vimeo/player'
export default {
created () {
const iframe = this.$refs.video01
const player = new Player(iframe)
}
}
</script>
But I cannot get DOM.
In case of SPA, I couldn't get DOM.
So error log is here.
TypeError: You must pass either a valid element or a valid id.
In case of SSR, window cannot use.
So error log is here.
window is not defined
Can't I use Vimeo Player SDK with Nuxt.js?

Did you try the vue-vimeo-player package? It could be helpful to have something managed the Vue way.
For the SPA, you will need to wait for the element to be on the page; hence try your code in a mounted hook (created step is before anything get mounted to the DOM).
If you want SSR too, you could try to wrap the whole thing into a
<client-only>
<! --- note I used vue-vimeo-player and not the vimeo-player --->
<vue-vimeo-player ref="player" :video-id="yourVideoID"/>
</client-only>
Here is a repo that I've created to show you how simple it is to make it work: https://github.com/kissu/so-nuxt-vimeo/blob/master/pages/index.vue
It made me find that you should use the <vue-vimeo-player /> component and not <vimeo-player />.
However, I imported the Vue wrapper for Vimeo Embed Player in the demo locally in a Vue component.

Related

Vue3, vue-router, how to keep unused view mounted, with audio playback (more than keep-alive)?

I want to use vue-router, but then keep a currently unused view mounted in the DOM. Something like Vue's keep-alive but with just hiding the unused view, instead of unmounting it.
The reason is, this view has an audio player in it, and I want to keep it playing. On unmount, it's stopping. Here's how it looks currently:
<!-- To keep the audio within the media player component running,
simply keep all components alive over route changes -->
<router-view v-slot="{ Component }">
<keep-alive include="Play">
<component :is="Component" />
</keep-alive>
</router-view>
I would like to replace the keep-alive with something like keep-mounted (but hidden). How can I do that?
Notes:
Of course I already have tried using Vue's keep-alive, as suggested here: https://stackoverflow.com/a/66297873/79485, and it's working as advertised. However, It's not enough, I need to keep the component mounted, not just kept in memory.
I have found https://github.com/Akryum/vue-router-multi-view, but this is vue2 only.
I have found a solution for the specific problem at hand, being the interrupted audio playback. As suggested by #Thomas I have placed the audio element completely out of the DOM, using the Web Audio API.
Here's how I create the audio context with the element, right at component creation:
data: () => ({
//...
/** The audio context to use
audioContext: new AudioContext({
latencyHint: 'interactive',
}),
audioElement: document.createElement('audio'),
}),
I never add the audio element to the DOM, but use it with the received element handle as usual:
/** Handles the setup of the audio graph outside the mounted lifespan.
* #devdoc The audio element is intentionally not added to the DOM, to keep it unaffected of unmounts during vue-router route changes.
*/
created() {
this.audioElement.src = this.src;
//.....
},
With this, although the VueJs3 view gets lifted from the DOM with keep-alive, the audio is unaffected of this.
I then subsequently destroy the audio context and the audio element at the unmounted() lifecycle event.
This works very well. You may review the complete code at https://github.com/suterma/replayer-pwa/blob/a5acd8dc3b5a277bca6d9baf9459fb70eea2e1a5/src/components/TrackAudioApiPlayer.vue

How to properly load Bootstrap5's Masonry into Nuxt?

I am trying to use the Masonry plugin with Bootstrap5 and NuxtJS. When I follow the example here
https://getbootstrap.com/docs/5.0/examples/masonry/ and incorporate it into my own codesandbox, I notice that my demo is not in the correct masonry format. See the gaps? My sandbox
My example:
Bootstrap's example:
What do I need to do to get my demo into format shown on the Bootstrap Masonry example page?
I checked how to load the script from a CDN either globally or locally. It was working but at one condition: you needed to NOT start on the masonry page.
Meaning that if you loaded the app on a specific page, then moved to the one with the masonry it was working. But not if you started on this specific page. So, a pretty subpar solution.
This article was really helpful to understand how to wait until the CDN script is fully loaded: https://vueschool.io/articles/vuejs-tutorials/how-to-load-third-party-scripts-in-nuxt-js/
Then I realized that we are far better installing it directly as an NPM dependency. Therefore, I proceeded to the masonry repo. Found a great message on how to setup the whole thing in Nuxt.
And after a removal of some useless stuff and some modern dynamic import, here we are
<template>
<main>
<h1>Bootstrap and Masonry</h1>
<div class="row" id="masonry">
<!-- ... -->
</main>
</template>
<script>
export default {
async mounted() {
if (process.browser) {
let { default: Masonry } = await import('masonry-layout')
new Masonry('#masonry', { percentPosition: true })
}
},
}
</script>
The final solution is looking pretty well and there is not a lot of code. On top of that, the code is properly loaded. And you can load it on a click or any other event.

createApp({}).mount('#app') clears #app's child elements in vue3

So I'm trying to add Vue3 to an existing asp.net core project. What I'd like to happen is for my razor app to render as normal, then use custom vue components to give my frontend a more reactive feel. However, when I mount an empty vue app to my wrapper div (parent of all other body content), it seems to be deleting all innerHTML of that wrapper div, completely removing all server rendered body content.
In my _Layout.cshtml file, I'm wrapping all content in a div with id 'app'.
<body>
<div id='app'>
#RenderBody()
</div>
<script src="~/js/vue-app/dist/js/chunk-vendors.76316534.js"></script>
<script src="~/js/vue-app/dist/js/app.bf4c5ba9.js"></script>
</body>
in main.js
import { createApp } from 'vue'
const vueApp = createApp({}).mount('#app');
// component definitions below
With the app set up like this, when I run my .net project I see a blank white browser window instead of the razor compiled html that I expect. In Vue2, it was possible to do this:
const vueApp = new Vue({
el: '#app',
data: {
....
},
methods: {
....
}//, etc
});
Which would result in the app being rendered as normalthe vue app bound to #app, making vue available to the child content (model binding, vue click handling, etc).
I've tried playing around with the isHydrate optional parameter on mount(), but it causes no change in the result.
Am I missing something here? How do you slowly migrate an existing project to use vue3 if you can't mount the app without clearing content? Any guidance is much appreciated.
Thank you
Notes:
vue-next runtime-dom source If this method is the mount method getting called, I'm not sure why container.innerHTML would not be getting set in the component. {} is not a function, and render/template is not defined for it.
vue-next runtime-core apiCreateApp source If this is the method getting called....I have no idea.
Update
Vue 3, without template renderer, will not be able to handle the templates after it has been compiled. To fix that, you can import vue/dist/vue.esm-browser (and vue.runtime.esm-browser.prod for prod), instead of the default vue. This will allow run-time component rendering.

undefined data error on page reload Nuxt.js

I'm currently developing a universal app using Nuxt.js, the data on most of the pages is retrieved from an API using a fetch hook as well as vuex store. I started noticing errors on page reload/refresh and sometimes when I visit a page from the navbar. The page error is:
TypeError: Cannot read property 'data' of undefined
where data is an object retrieved from an API. I have searched around the internet for this and found it has something to do with data not being loaded or page rendering whilst the data is not fully retrieved. i have found a work around by using a v-if on my template to check if the data is set then display the contents. my question is if there is a way to achieve this, i have tried using the async/await keywords but it doesn't help and i feel like the v-if method is not the best way to handle it.
edit: solved by using $fetchState.pending when using fetch()
If in your template you display right away the data you retrieve from the API, then indeed using the v-if is the right way to do.
If you are using the new fetch() hook, then in your template you can use $fetchState.pending to check if the loading has finished, for example:
<div v-if="$fetchState.pending">
<p> Content is loading... </p>
</div>
<div v-else>
<p> Content has loaded! {{data}}</p>
</div>
<script>
export default{
data(){
return{
data: null
}
}
async fetch(){
this.data = await getSomeAPI
}
}
</script>

Vue.js - Embed Swagger UI inside a Vue component?

I have a form in my Vue component which uploads the api file. Now I want to render the contents of the file like this:
I have imported swagger client library: https://github.com/swagger-api/swagger-ui.
Now, here
is an example of how you do it in a static page. But I need to do it inside a Vue component (or Quasar, specifically), so I do it like that:
Register swagger-ui inside my register components file:
<link rel="stylesheet" type="text/css" href="swagger-ui.css">
Now it is available as:
this.swaggerUI({})
anywhere in my components. Inside my component I have a div in a template to render the api file:
<template>
<q-form>here lies q-file element, submit button and other stuff</q-form>
<div id="swagger-ui"></div>
</template>
In the mentioned question he had something like:
<script>
window.onload = function() {
const ui = SwaggerUIBundle({
url: "https://yourserver.com/path/to/swagger.json",
dom_id: '#swagger-ui',
presets: [
SwaggerUIBundle.presets.apis,
SwaggerUIStandalonePreset
]
})
window.ui = ui
}
</script>
Here's the difference: first of all, no window.onload, I must render it on submit button. Then, I deal with an uploaded file stored in my model, so no URL here. Now, I don't get how to make it work with locally stored file, when I try with the remote url, it gives me:
vue.esm.js?a026:628 [Vue warn]: Error in v-on handler: "Invariant Violation: _registerComponent(...): Target container is not a DOM element."
I was getting a similar error (Target container is not a DOM element) trying to use a static swagger spec. Instead of using window.onload, I found that Vue has the mounted() function, so this Vue 3 file worked for me:
<template>
<div class="swagger" id="swagger"></div>
</template>
<script>
import SwaggerUI from 'swagger-ui';
import 'swagger-ui/dist/swagger-ui.css';
export default {
name: "Swagger",
mounted() {
const spec = require('../path/to/my/spec.json');
SwaggerUI({
spec: spec,
dom_id: '#swagger'
})
}
}
</script>
This one appeared to be a simple yet very unobvious typo: in windows.onload function:
dom_id: '#swagger-ui',
must instead be
dom_id: 'swagger-ui',
without hash sign, that's it!