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.
Related
In my Nuxt.js project I installed vue2-editor package to be able to write articles with HTML. When I come to page, write something and press the button everything works correctly, but when I reload page, I get document is not defined error.
Here is the code:
<template>
<div>
<SideBar />
<div class='content'>
<h1>Write article</h1>
<client-only>
<VueEditor
v-model='articleContent'
/>
</client-only>
<div style='margin-top: 15px'><button #click='postArticle'>Post article</button></div>
</div>
</div>
</template>
<script>
import { VueEditor } from 'vue2-editor';
import SideBar from '../components/SideBar';
export default {
name: 'Articles',
components: {
SideBar,
VueEditor
},
data() {
return {
articleContent: null,
}
},
methods: {
postArticle() {
console.log(this.articleContent)
},
},
}
</script>
And the error looks like that:
Also in documentation I've found that for Nuxt.js projects vue2-editor should be added to modules, and I did it, but it still doesn't work:
modules: [
// https://go.nuxtjs.dev/axios
'#nuxtjs/axios',
'vue2-editor/nuxt'
],
You can try to load it dynamically:
<template>
<div>
<SideBar />
<div class='content'>
<h1>Write article</h1>
<client-only>
<VueEditor
v-model='articleContent'
/>
</client-only>
<div style='margin-top: 15px'><button #click='postArticle'>Post article</button></div>
</div>
</div>
</template>
<script>
import SideBar from '../components/SideBar';
export default {
name: 'Articles',
components: {
SideBar,
VueEditor: () => process.client ? (await import("vue2-editor")).VueEditor : ""
},
data() {
return {
articleContent: null,
}
},
methods: {
postArticle() {
console.log(this.articleContent)
},
},
}
</script>
Do follow the below steps the add that plugin into your Nuxt
There will be plugins folder just like pages and components, if not create one and add a js file into it vue2-editor.js.
Copy the below content inside vue2-editor.js
import Vue from "vue";
import Vue2Editor from "vue2-editor";
Vue.use(Vue2Editor);
Inside nuxt.config.js remove the 'vue2-editor/nuxt' from the modules and create a separate array called plugins as below
/*
** Plugins to load before mounting the App
*/
plugins: [{ src: "~/plugins/vue2-editor", mode: 'client' }],
Thats it you are done. Now you can start using it in any of the vue files like
<vue-editor placeholder="Write Something..." v-model="content"></vue-editor>
What I want to come true
I want to display an alert message considering the result of the data sent to the server.
However, since alert messages are managed by another component, it is necessary to call the component asynchronously.
The official Vue.js documentation used Vue.component, but what's the right way to do it with Nuxt.js?
Code
I want to use search.vue in success.vue
search.vue
<template>
<v-app>
<div
class="teal lighten-1 background pa-10"
>
<!-- <div
v-if="responseBook === 200"
> -->
<alert-success />
<v-sheet
width="1100px"
class="mx-auto pa-5 rounded-xl"
color="grey lighten-5"
min-height="500px"
>
<!-- 書籍検索、表示 -->
<BookPostDialog />
<!-- 選択されたデータの表示 -->
<BookPostSelected />
</v-sheet>
</div>
</v-app>
</template>
<script>
export default {
computed: {
responseBook () {
return this.$store.state.book.responseBook.status
}
}
}
</script>
<style lang="scss" scoped>
.background {
background-image: url('~/assets/images/tree.png');
background-repeat: space repeat;
}
</style>
Alert/success.vue
<template>
<v-alert type="success">
Succeeded
</v-alert>
</template>
If you want to use that kind of feature, you'll be better suited looking for something like this component: https://buefy.org/documentation/toast
Or anything like this in the jungle of CSS frameworks, pretty sure each of them have one.
Or implement it yourself, for this, you need to rely on portals.
For Vue2, this is how to do achieve it: https://portal-vue.linusb.org/guide/getting-started.html#enabling-disabling-the-portal
<portal to="destination" :disabled="true">
<p>
Your content
</p>
</portal>
If you want to show success.vue component after the connection to server (getting or posting data), you can use v-if as follows:
search.vue
<template>
<div>
<p>search compo</p>
<div v-if="this.$store.state.book.responseBook == 'ok'">
data was received.
<success />
</div>
</div>
</template>
<script>
export default {
mounted() {
this.$store.dispatch('getData')
}
}
</script>
success.vue
<template>
<div>
succeess compo
</div>
</template>
And then in your store/index.js file:
import Vuex from "vuex";
const createStore = () => {
return new Vuex.Store({
state: {
book: {
responseBook: ""
}
},
mutations: {
bookMutate(state, data) {
state.book.responseBook = data;
}
},
actions: {
getData(vuexContext) {
let vue = this;
// your request is here
setTimeout(function() {
vue.$axios.$get("https://pokeapi.co/api/v2/pokemon/ditto").then((result) => {
console.log(result);
vuexContext.commit("bookMutate", "ok");
}).catch(err => {
console.log(err);
})
}, 10000)
},
}
});
};
export default createStore;
I intentionally used setTimeout() in my action to see that the success component is loaded after the data was received. in actual situation it is better to use this action:
getData(vuexContext) {
this.$axios.$get("https://pokeapi.co/api/v2/pokemon/ditto").then((result) => {
console.log(result);
vuexContext.commit("bookMutate", "ok");
}).catch(err => {
console.log(err);
})
},
I used axios for calling the api but you can use your own method of getting data. but after that you must commit the mutation to change the state.
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
}
}
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).