Dynamically load .mp3s in Vue.js+Nuxt.js on mounted() - vue.js

I'm tying to follow the official nuxt.js documentation to dynamically load dozens of .mp3 files as the user progresses through a quiz.I've set up my config file as follows:
nuxt.config.js (Nuxt v2.15.8):
build: {
extend ( config, ctx ) {
// Might be used for loading media files
config.module.rules.push({
test: /\.(ogg|mp3|wav|mpe?g)$/i,
loader: 'file-loader',
options: {
name: '[path][name].[ext]'
}
});
}
}
This works:
import audioFile from '~/assets/audio/sfx_Q1_1.mp3';
// Outputs /_nuxt/assets/audio/sfx_Q1_1.mp3, and the file plays as expected
console.log(audioFile);
Using an import statement works, and it gives me the correct asset path. The problem is that I need dozens of mp3s with sfx_Q#_#.mp3 format, so I need a more dynamic approach.
This doesn't work:
mounted() {
let audioURL = `~/assets/audio/sfx_Q${this.qID}_${this.aID}.mp3`;
let audio = require(audioURL);
console.log(audio);
}
With this approach, my app crashes and I get Cannot find module '~/assets/audio/sfx_Q1_1.mp3', even though it's exactly the same path as the version that worked with the import statement.
This also works
<audio v-bind:src="require(`~/assets/audio/sfx_Q${qID}_${aID}.mp3`).default"></audio>
...but this doesn't work
<audio v-bind:src="require(getAudioURL()).default"></audio>
// ... //
getAudioURL() {
return `~/assets/audio/sfx_Q${this.qID}_${this.aID}.mp3`;
}
... I get the same Cannot find module error. Why does the import break when the file path is generated dynamically inside a method? I've tried with # symbol as well, to no avail. This question had the same issue, but I've tried exactly the solutions suggested, and it doesn't help.

Related

Is it possible to use DayJs in ant design Vue (antdv) in DatePickers instead of MomentJs?

I tryed to replace momentjs in project on antdv, and find this advice:
"We also provide another implementation, which we provide with
antd-dayjs-webpack-plugin, replacing momentjs with Day.js directly
without changing a line of existing code. More info can be found at
antd-dayjs-webpack-plugin."
https://2x.antdv.com/docs/vue/faq
So then i tryed to do same steps like in instruction https://github.com/ant-design/antd-dayjs-webpack-plugin. But i just changed webpack-config.js on vue-config.js and in code:
const AntdDayjsWebpackPlugin = require('antd-dayjs-webpack-plugin');
module.exports = {
plugins: [
new AntdDayjsWebpackPlugin()
]
}
// on
const AntdDayjsWebpackPlugin = require('antd-dayjs-webpack-plugin');
module.exports = {
configureWebpack: (config) => {
config.plugins.push(
new AntdDayjsWebpackPlugin(),
);
}
}
But then i got mistake 502 Bad Gateway.
If i deleted configureWebpack mistake was still there. And then i deleted require and mistake was gone.
Also i found what in page with this plugin there was word about React but not about Vue.
So i had few questions:
Is it possible to use DayJs in antdv DatePickers? With plugins or any ways.
Is it mistake in FAQ? How i can tall about this issue (if it is)? I didnt found any method to communicate with them.

Vue.js src searching localhost instead of file system

I have an image whose src I'm trying to change when it is clicked.
<b-img :id="favorite" src="~/static/svg/favorite.svg" #click="iconClicked(favorite)" right />
And down under export default, I have
methods: {
iconClicked(name: any) {
(<HTMLImageElement> document.getElementById(name))!.src="~/static/svg/favoriteAlternate.svg";
}
}
When I run my code at localhost:3000, I get a 404 error, and the code appears to be searching localhost:3000/~/static/svg/favoriteAlternate.svg instead of my local file system.
Why might this be? What should I do to fix this?
Thanks!
There's a couple of things wrong here.
First, you shouldn't be manually interacting with the DOM like that – Vue "owns" the DOM and you should leave it to Vue to change.
When Vue compiles the template, it treats the src attribute specially by loading the file on disk it refers to through webpack. But then when you manually change the element's src attribute like that, the new file it references was never bundled by webpack so it won't load.
You need to require/import both images so they get bundled. Then swap between them using a binding on the src attribute.
Something like:
<img :src="src">
// These are not javascript files, however webpack will bundle them
// and export the correct src you should use to refer to them
import FavoriteImage from '~/static/svg/favorite.svg'
import FavoriteAltImage from '~/static/svg/favoriteAlternate.svg'
export default {
data() {
return {
src: FavoriteImage,
}
},
methods: {
iconClicked() {
this.src = FavoriteAltImage
}
}
}

Vue static assets are not accessible to a library

I am using a single file Vue component and import a face-api library. I want to use a function from that library, loadSsdMobilenetv1Model(url), which takes URL of folder, where the necessary files are located and loads them. The function however cannot fetch the files if I use #/assets/weights as url (# in Vue represents the src folder). I would like to be able to host the assets for. I'm able to read files from the assets folder folder with require('#/assets/file.json), but the library seems to need a static url.
What is the best solution in my situation? Maybe I'm missing some understanding.
Can I make it so that the assets folder is served and accessible?
Here's my component and the comments show some things I've tried:
<template>
<div>...stuff...</div>
</template>
<script>
import * as faceapi from 'face-api.js';
async function load() {
// example below: If I serve the files on a separate port with CORS allowed, the function loads files fine.
// const MODEL_URL = 'http://127.0.0.1:8081/weights/';
// example below: this does not work, but I would like this to work!
const MODEL_URL = '#/assets/weights';
// example below: also doesn't work, conscious of relative paths
// const MODEL_URL = '../assets/weights';
// example below: a file loads, but I can't just this unfortunately
// return require('#/assets/file.json')
return await faceapi.loadSsdMobilenetv1Model(MODEL_URL);
}
export default {
mounted() {
var promise = load();
promise.then((model) => {
this.model = model
}, (reject) => {
console.log(reject)
// alert(reject);
})
},
name: "Home",
data() {
return {
model: null
}
}
};
</script>
I'm not sure if it's relevant, but I set up the project with
vue create
and run the dev environment with
nmp run serve

“window is not defined” in Nuxt.js

I get an error porting from Vue.js to Nuxt.js.
I am trying to use vue-session in node_modules. It compiles successfully, but in the browser I see the error:
ReferenceError window is not defined
node_modules\vue-session\index.js:
VueSession.install = function(Vue, options) {
if (options && 'persist' in options && options.persist) STORAGE = window.localStorage;
else STORAGE = window.sessionStorage;
Vue.prototype.$session = {
flash: {
parent: function() {
return Vue.prototype.$session;
},
so, I followed this documentation:
rewardadd.vue:
import VueSession from 'vue-session';
Vue.use(VueSession);
if (process.client) {
require('vue-session');
}
nuxt.config.js:
build: {
vendor: ['vue-session'],
But I still cannot solve this problem.
UPDATED AUGUST 2021
The Window is not defined error results from nodejs server side scripts not recognising the window object which is native to browsers only.
As of nuxt v2.4 you don't need to add the process.client or process.browser object.
Typically your nuxt plugin directory is structured as below:
~/plugins/myplugin.js
import Vue from 'vue';
// your imported custom plugin or in this scenario the 'vue-session' plugin
import VueSession from 'vue-session';
Vue.use(VueSession);
And then in your nuxt.config.js you can now add plugins to your project using the two methods below:
METHOD 1:
Add the mode property with the value 'client' to your plugin
plugins: [
{ src: '~/plugins/myplugin.js', mode: 'client' }
]
METHOD 2: (Simpler in my opinion)
Rename your plugin with the extension .client.js and then add it to your plugins in the nuxt.config.js plugins. Nuxt 2.4.x will recognize the plugin extension as to be rendered on the server side .server.js or the client side .client.js depending on the extension used.
NOTE: Adding the file without either the .client.js or .server.js extensions will render the plugin on both the client side and the server side. Read more here.
plugins: ['~/plugins/myplugin.client.js']
There is no window object on the server side rendering side. But the quick fix is to check process.browser.
created(){
if (process.browser){
console.log(window.innerWidth, window.innerHeight);
}
}
This is a little bit sloppy but it works. Here's a good writeup about how to use plugins to do it better.
Its all covered in nuxt docs and in faq. First you need to make it a plugin. Second you need to make your plugin client side only
plugins: [
{ src: '~/plugins/vue-notifications', mode: 'client' }
]
Also vendor is not used in nuxt 2.x and your process.client not needed if its in plugin with ssr false
In Nuxt 3 you use process.client like so:
if (process.client) {
alert(window);
}
If you've tried most of the answers here and it isn't working for you, check this out, I also had the same problem when using Paystack, a payment package. I will use the OP's instances
Create a plugin with .client.js as extension so that it can be rendered on client side only. So in plugins folder,
create a file 'vue-session.client.js' which is the plugin and put in the code below
import Vue from 'vue'
import VueSession from 'vue-session'
//depending on what you need it for
Vue.use(VueSession)
// I needed mine as a component so I did something like this
Vue.component('vue-session', VueSession)
so in nuxt.config.js, Register the plugin depending on your plugin path
plugins:[
...
{ src: '~/plugins/vue-session.client.js'},
...
]
In index.vue or whatever page you want to use the package... import the package on mounted so it is available when the client page mounts...
export default {
...
mounted() {
if (process.client) {
const VueSession = () => import('vue-session')
}
}
...
}
You can check if you're running with client side or with the browser. window is not defined from the SSR
const isClientSide: boolean = typeof window !== 'undefined'
Lazy loading worked for me. Lazy loading a component in Vue is as easy as importing the component using dynamic import wrapped in a function. We can lazy load the StepProgress component as follows:
export default {
components: {
StepProgress: () => import('vue-step-progress')
}
};
On top of all the answers here, you can also face some other packages that are not compatible with SSR out of the box and that will require some hacks to work properly. Here is my answer in details.
The TLDR is that you'll sometimes need to:
use process.client
use the <client-only> tag
use a dynamic import if needed later on, like const Ace = await import('ace-builds/src-noconflict/ace')
load a component conditionally components: { [process.client && 'VueEditor']: () => import('vue2-editor') }
For me it was the case of using apex-charts in Nuxt, so I had to add ssr: false to nuxt.config.js.

intern dojo loader issue

I'm trying to setup intern for my project, a Dojo/JS project, and the server is not Node... I get a loader issue, which seems to be due to dojo.has using Dojo loader... The require wrapper suggested in here did not work for me.
I get the error below:
> node node_modules/intern/client.js config=tests/intern
Defaulting to "console" reporter
dojo/text plugin failed to load because loader does not support getText
TypeError: undefined is not a function
at Object.load (lib/dojo/dojo/text.js:199:6)
Below are my intern configuration and the test file:
/tests/intern.js: (config file)
loader: {
packages: [ { name: 'visitorsPortal', location: 'portals/visitor' },
{ name: 'dojo', location: 'lib/dojo/dojo'},
{ name: 'dijit', location: 'lib/dojo/dijit'},
{ name: 'portalLib', location: 'portals/lib'} ]
},
suites: [ 'tests/uitests' ],
tests/uitests:
define([
'intern!tdd',
'intern/chai!assert',
'portals/visitor/views/MyModule'
], function (test, assert, MyModule) {
// empty for now...
});
This has nothing to do with dojo/has and everything to do with the dojo/text plugin requiring functionality that only exists within the Dojo 1 loader when being used server-side.
If you are attempting to test software that relies on any non-standard AMD loader functionality, you will need to use the non-standard loader, or override those modules with alternative copies that are compatible with other loaders.
In this specific case, your easiest path forward is to use the geezer edition of Intern, since it includes the old Dojo loader which contains these non-standard extensions. The best path forward is to remap the dojo/text module to another compatible module that does not need anything special in the loader in order to retrieve the data:
// in intern.js
// ...
loader: {
map: {
'*': {
'dojo/text': 'my/text'
}
}
},
// ...
I struggled with the same problem yesterday, but thanks to C Snover's answer here and the question you're linking to, I did make some progress.
I added the map directive to the intern.js loader config (as C Snover suggests).
// in intern.js
// ...
loader: {
map: {
'*': {
'dojo/text': 'my/text'
}
}
},
// ...
For the my/text module, I just copied dojo/text and added an else if clause to the part that resolves the getText function:
if(has("host-browser")){
getText= function(url, sync, load){
request(url, {sync:!!sync}).then(load);
};
} else if(has("host-node")){
// This was my addition...
getText = function(url, sync, load) {
require(["dojo/node!fs"], function(fs) {
fs.readFile(url, 'utf-8', function(err, data) {
if(err) console.error("Failed to read file", url);
load(data);
});
});
};
} else {
// Path for node.js and rhino, to load from local file system.
// TODO: use node.js native methods rather than depending on a require.getText() method to exist.
if(require.getText){
getText= require.getText;
}else{
console.error("dojo/text plugin failed to load because loader does not support getText");
}
}
However, even though the tests were running in intern via node, the host-node value wasn't being set. That was fixed by setting dojo-has-api in my intern.js config:
define(["intern/node_modules/dojo/has"], function(has) {
has.add("dojo-has-api", true);
return { /* my intern config as normal */ };
});
I'll admit I don't understand 100% what I've done here, and with the copy/pasting it's not exactly pretty, but it serves as a temporary fix for my problem at least.
Note: This did introduce another set of issues though: Since Dojo now knows that it's running in node, dojo/request no longer tries to use XHR. I was using sinon.js to mock my xhr requests, so this had to be changed.