I am learning (tinkering with) ES6 modules and Vue.js, single file components (SFC). I built my project with the Vue CLI via the webpack-simple template. I get an error "TypeError: Cannot read property 'name' of undefined" at the line with "settings.mainAlarm.name". "npm run dev" does not throw any errors so I believe the build step is finding (and perhaps ignoring) the settings.js file. What is the best way to import reusable JavaScript into a Vue SFC?
Root.vue file:
<template>
<div id="app">
<h1>{{ msg }}</h1>
<h4>{{ alarmName }}</h4>
</div>
</template>
<script>
//const settings = mainAlarm;
import settings from './lib/settings.js'
export default {
name: 'app',
data () {
return {
msg: 'Welcome to Blah Blah Blah!',
alarmName: settings.mainAlarm.name
}
}
}
//console.log(this.alarmName);
</script>
<style>
</style>
./lib/settings.js file:
export default function () {
var rtn = {
mainAlarm: {
name: "overdueCheckAlarm",
info: { delayInMinutes: .01, periodInMinutes: .25 }
},
notificationAudioFile: "ache.mp3",
baseUrl: "www.xxx.com/xx/xxxx-xxx/"
}
return rtn;
}
Either your settings file should look like this
export default {
mainAlarm: {
name: "overdueCheckAlarm",
info: { delayInMinutes: .01, periodInMinutes: .25 }
},
notificationAudioFile: "ache.mp3",
baseUrl: "www.xxx.com/xx/xxxx-xxx/"
}
in which case, your component will work as is, or your component should look like this and you can leave the settings file alone
<script>
import settings from './lib/settings.js'
// settings.js exports a function as the default, so you
// need to *call* that function
const localSettings = settings()
export default {
name: 'app',
data () {
return {
msg: 'Welcome to Blah Blah Blah!',
alarmName: localSettings.mainAlarm.name
}
}
}
</script>
I expect it's the first option you really want (I'm not sure why you would want a unique settings object every time you use settings, which is what the code in your question would do).
Related
I tried following:
https://github.com/visualfanatic/vue-svg-loader/tree/master
but there's a version conflict with vue-template-compiler since that's used in Vue 2.
I tried:
https://github.com/visualfanatic/vue-svg-loader
but I'm missing a specific vue dependency.
I noticed there's a caveat with using typescript and you need to declare the type definition file. However, I still get "Cannot find module '../../assets/myLogo.svg' or its corresponding type declarations."
Here's what I added:
vue.config.js
module.exports = {
chainWebpack: (config) =>
{
const svgRule = config.module.rule('svg');
svgRule.uses.clear();
svgRule
.use('vue-loader-v16')
.loader('vue-loader-v16')
.end()
.use('vue-svg-loader')
.loader('vue-svg-loader');
},
configureWebpack: process.env.NODE_ENV === 'production' ? {} : {
devtool: 'source-map'
},
publicPath: process.env.NODE_ENV === 'production' ?
'/PersonalWebsite/' : '/'
}
shims-svg.d.ts
declare module '*.svg' {
const content: any;
export default content;
}
MyComponent.vue
<template>
<div>
<MyLogo />
</div>
</template>
<script lang="ts">
import * as MyLogo from "../../assets/myLogo.svg";
export default defineComponent({
name: "MyComponent",
components: {
MyLogo
},
props: {
},
setup(props)
{
return {
props
};
}
});
</script>
Actually SVGs are supported right out of the box with Vue CLI. It uses file-loader internally. You can confirm it by running the following command on the terminal:
vue inspect --rules
If "svg" is listed (it should be), then all you've got to do is:
<template>
<div>
<img :src="myLogoSrc" alt="my-logo" />
</div>
</template>
<script lang="ts">
// Please just use `#` to refer to the root "src" directory of the project
import myLogoSrc from "#/assets/myLogo.svg";
export default defineComponent({
name: "MyComponent",
setup() {
return {
myLogoSrc
};
}
});
</script>
So there's no need for any third party library—that is if your sheer purpose is only to display SVGs.
And of course, to satisfy the TypeScript compiler on the type declaration:
declare module '*.svg' {
// It's really a string, precisely a resolved path pointing to the image file
const filePath: string;
export default filePath;
}
Can't say for sure, since I haven't tried with ts, but as posted here
this should work.
declare module '*.svg' {
import type { DefineComponent } from 'vue';
const component: DefineComponent;
export default component;
}
I see you're using:
import * as MyLogo from "../../assets/myLogo.svg";
I believe that should be:
import MyLogo from "../../assets/myLogo.svg";
vue-svg-loader is not compatible with vue 3. To import svg and use it as a component, simply wrap the contents of the file in 'template'
In component:
<template>
<div class="title">
<span>Lorem ipsum</span>
<Icon />
</div>
</template>
<script>
import Icon from '~/common/icons/icon.svg';
export default {
name: 'PageTitle',
components: { Icon },
};
</script>
Webpack:
{
test: /\.svg$/,
use: ['vue-loader', path.resolve(__dirname, 'scripts/svg-to-vue.js')],
}
scripts/svg-to-vue.js:
module.exports = function (source) {
return `<template>\n${source}\n</template>`;
};
Example from fresh installed vue.js 3.2:
<img alt="Vue logo" class="logo" src="#/assets/logo.svg" width="125" height="125"/>
I have a few components, javascript, and elements that needs to be ran in a certain order.
1st - opensheetmusicdisplay.min.js which I have in my index.html file. This isn't an issue.
2nd - <div id="xml">
3rd - xml-loader.js which depends on both the "xml" div and opensheetmusicdisplay.min,js
This is the index.html:
<!DOCTYPE html>
<html lang="en">
<head>
<script rel="preload" src="<%= BASE_URL %>js/osmd/opensheetmusicdisplay.min.js"></script>
</head>
<body>
<div id="xml2">words go here</div>
<div id="app"></div>
</body>
</html>
And this is the JavaScript part I'm attempting to test:
window.onload = function() {
alert("xx == ", document.getElementById("xml2"));
}
alert("xx2 == ", document.getElementById("xml2"));
alert(JSON.stringify(opensheetmusicdisplay, null, 1));
When I run this, they both instances of "xml2" show blanks. The opensheetmusicdisplay does show data, which means it is reading from the source in the head section in index.html
It was pointed out to me in the comments that alert only take one argument. That's a mistake that I'm going to let sit for the moment. The error in the console is TypeError: document.getElementById(...) is null.
Now, this is the main.js. There are a lot of comments because of my various ideas:
// vue imports and config
import Vue from 'vue'
import App from '#/App'
import VueRouter from 'vue-router'
Vue.use(VueRouter)
Vue.config.productionTip = false
// page imports
import Notation from '#/components/Notation'
import HomePage from '#/components/HomePage'
// component imports and registration
import { FoundationCSS } from '#/../node_modules/foundation-sites/dist/css/foundation.min.css'
Vue.component('foundation-css', FoundationCSS)
import SideNav from '#/components/SideNav'
Vue.component('side-nav', SideNav);
// import * as Osmd from '#/../public/js/osmd/opensheetmusicdisplay.min.js'
// Vue.component('osmd-js', Osmd)
// import { OsmdJs } from '#/components/Osmd'
import * as XmlJs from '#/../public/js/osmd/xml-loader.js'
Vue.component('xml-js', XmlJs)
// import XLoad from '#/components/XmlLoader'
const router = new VueRouter({
mode: 'history',
routes: [
{ path: '/',
components: {
maininfo: HomePage
}
},
{ path: '/chromatic-scales/c-chromatic-scale',
components: {
maininfo: Notation// ,
// xmlloader: XLoad
}
}
]
})
new Vue({
el: '#app',
router,
template: '<App/>',
components: { App }
})
I registered XmlJs as global because this is the only way out of 100 things that actually works. I then embed it in Notation.vue like so:
<template>
<div>
<div id="xml">
{{ notation.data }}
</div>
<xml-js />
</div>
</template>
<script>
import axios from 'axios'
export default ({
data () {
return {
notation: null,
}
},
mounted () {
axios
.get('http://localhost:3000/chromatic-scales/c-chromatic-scale')
.then(result => (this.notation = result))
}})
</script>
<style scoped></style>
The last file is the meat and potatoes of what I'm trying to do. The xml-loader.js slurps the data from <div id="xml"> and does whatever magic the program does in order to render the output I want. The issue is that there doesn't seem to be anyway to wait for the stuff in {{ notation.data }}.
I am new to using vuejs and front-end javascript frameworks in general. I do recognize the code is probably not optimal at this time.
There is race condition where DOM element is not available at the time when it's accessed. The solution is to not access DOM elements created by Vue outside of it. DOM element is ready for use only after asynchronous request:
<template>
<div>
<div ref="xml" id="xml">
{{ notation.data }}
</div>
<xml-js />
</div>
</template>
<script>
import axios from 'axios'
export default ({
data () {
return {
notation: null,
}
},
async mounted () {
const result = await axios
.get('http://localhost:3000/chromatic-scales/c-chromatic-scale')
this.notation = result;
this.$nextTick(); // wait for re-render
renderXml(this.$ref.xml); // pass DOM element to third-party renderer
}})
You can import xml-loader.js into the Notation.vue as a function. Then you can simply do something like this:
mounted () {
axios.get(PATH).then(result => {
this.notation = result
let xmlResult = loadXML(result)
doSomethingWithResult(xmlResult)
}
},
methods: {
doSomethingWithResult (result) {
// do something
}
}
Code in the file:
<template>
<component v-bind:is="bbc"></component>
</template>
<script>
import bbc from './bbc.vue';
export default {
name: 'ShowRoom2',
};
</script>
./bbc.vue
<script>
export default {
name: 'bbc',
props: {
msg: String,
},
mounted() {
console.log('bbc is mounted');
},
render() {
if (this.func) this.func();
return (
<div class="bbcMyClass">
<h1>bbc: <span>Pal</span> <span>{this.msg}</span></h1>
</div>
)
}
};
</script>
To reproduce
git clone git#github.com:adamchenwei/vue-hoc-playground.git
go to src/components/ShowRoom2.vue
yarn install && yarn serve
observe error in the local browser
Yes, the scope in the template is not the same as the script scope. If you need some data, you need to declare it inside the 'component' definition part of the code. For your case, I guess the 'data' property should work
import bbc from './bbc.vue';
export default {
name: 'ShowRoom2',
data() {
return {
bbc: bbc,
};
},
};
However, the template part of your code also looks weird. Could you explain what you're trying to do ?
this is my code block from a component, im fairly new (2 days with the vue.js and I came across a medium article about MEVN ARCH.
<template>
<div class="post">
<h1>post</h1>
<div >
<!-- <p v-for="post in posts">
<span><b>{{post.title}}</b></span>
<span><b>{{post.description}}</b></span>
</p> -->
</div>
</div>
</template>
<script>
import postService from "#/services/postservice";
export default {
name: 'posts',
data () {
return {
posts: []
}
},
mounted () {
this.getPosts()
},
methods: {
async getPosts () {
const response = await postService.fetchPost()
this.posts = response.data
}
}
}
</script>
<style scoped>
</style>
This is the terminal output :
If you use this syntax:
"#/services/postservice"
You must be sure that your Webpack configuration actually contains an alias like this:
resolve: {
alias: {
'#': 'resources/assets/js(this is your custom path, dont just copy this)'
}
}
This tells Webpack what # actually resolves to. Because by default it does not mean anything. Only when you provide the Webpack configuration with this alias will this actually resolve to a complete path.
I am guessing that this is the problem here.
I have App.vue which has a template:
<template>
<div id="app">
<login v-if="isTokenAvailable()"></login>
</div>
</template>
I've declared the isTokenAvailable method in the normal way for Vue inside methods. It uses a function that I wrote in a separate js file:
<script>
import * as mylib from './mylib';
export default {
....
methods:{
isTokenAvailable: () => {
return mylib.myfunc();
}
}
}
</script>
mylib starts like this:
import models from './model/models'
import axois from 'axios'
export default function() {
// functions and constants
}
When I run the project, I get this below warning:
export 'myfunc' (imported as 'mylib') was not found in './mylib'
I gather I'm not importing or declaring a javascript module correctly... but there seem to be so many ways to do it, added with the complexity of the scoping in Vue, I'm not sure what is the right way to do it?
Why this isn't a dupe of: How do I include a JavaScript file in another JavaScript file?
That one doesn't seem to fix the problem, specifically in the context of vuejs.
I have tried this:
<script>
const mylib = require('./mylib');
...
</script>
With the function modified to: exports.myfunc = function()
Should I have some other dependency for this to work? Because I get a different error:
[Vue warn]: Error in render function:
TypeError: mylib.myfunc is not a function
Say I want to import data into a component from src/mylib.js:
var test = {
foo () { console.log('foo') },
bar () { console.log('bar') },
baz () { console.log('baz') }
}
export default test
In my .Vue file I simply imported test from src/mylib.js:
<script>
import test from '#/mylib'
console.log(test.foo())
...
</script>
After a few hours of messing around I eventually got something that works, partially answered in a similar issue here: How do I include a JavaScript file in another JavaScript file?
BUT there was an import that was screwing the rest of it up:
Use require in .vue files
<script>
var mylib = require('./mylib');
export default {
....
Exports in mylib
exports.myfunc = () => {....}
Avoid import
The actual issue in my case (which I didn't think was relevant!) was that mylib.js was itself using other dependencies. The resulting error seems to have nothing to do with this, and there was no transpiling error from webpack but anyway I had:
import models from './model/models'
import axios from 'axios'
This works so long as I'm not using mylib in a .vue component. However as soon as I use mylib there, the error described in this issue arises.
I changed to:
let models = require('./model/models');
let axios = require('axios');
And all works as expected.
I like the answer of Anacrust, though, by the fact "console.log" is executed twice, I would like to do a small update for src/mylib.js:
let test = {
foo () { return 'foo' },
bar () { return 'bar' },
baz () { return 'baz' }
}
export default test
All other code remains the same...
I was trying to organize my vue app code, and came across this question , since I have a lot of logic in my component and can not use other sub-coponents , it makes sense to use many functions in a separate js file and call them in the vue file, so here is my attempt
1)The Component (.vue file)
//MyComponent.vue file
<template>
<div>
<div>Hello {{name}}</div>
<button #click="function_A">Read Name</button>
<button #click="function_B">Write Name</button>
<button #click="function_C">Reset</button>
<div>{{message}}</div>
</div>
</template>
<script>
import Mylib from "./Mylib"; // <-- import
export default {
name: "MyComponent",
data() {
return {
name: "Bob",
message: "click on the buttons"
};
},
methods: {
function_A() {
Mylib.myfuncA(this); // <---read data
},
function_B() {
Mylib.myfuncB(this); // <---write data
},
function_C() {
Mylib.myfuncC(this); // <---write data
}
}
};
</script>
2)The External js file
//Mylib.js
let exports = {};
// this (vue instance) is passed as that , so we
// can read and write data from and to it as we please :)
exports.myfuncA = (that) => {
that.message =
"you hit ''myfuncA'' function that is located in Mylib.js and data.name = " +
that.name;
};
exports.myfuncB = (that) => {
that.message =
"you hit ''myfuncB'' function that is located in Mylib.js and now I will change the name to Nassim";
that.name = "Nassim"; // <-- change name to Nassim
};
exports.myfuncC = (that) => {
that.message =
"you hit ''myfuncC'' function that is located in Mylib.js and now I will change the name back to Bob";
that.name = "Bob"; // <-- change name to Bob
};
export default exports;
3)see it in action :
https://codesandbox.io/s/distracted-pare-vuw7i?file=/src/components/MyComponent.vue
edit
after getting more experience with Vue , I found out that you could use mixins too to split your code into different files and make it easier to code and maintain see https://v2.vuejs.org/v2/guide/mixins.html