Vue import component within functional component - vue.js

I have a component called SpotifyButton in the components directory that looks like this:
<template functional>
<b-button pill size="sm" :href="props.spotifyUri" class="spotify-green">
<b-img-lazy
src="~/assets/Spotify_Icon_RGB_White.png"
height="20"
width="20"
/>
View on Spotify
</b-button>
</template>
<script lang="ts">
import Vue from 'vue';
export default Vue.extend({
name: 'SpotifyButton',
props: {
spotifyUri: {
type: String,
required: true
}
}
});
</script>
I'm able to import and use this in a component in the pages directory like so without any problem:
<template>
<spotify-button :spotify-uri="artist.uri"/>
</template>
<script lang="ts">
import Vue from 'vue';
import { Context } from '#nuxt/types';
import FullArtist from '#/types/FullArtist';
import SpotifyButton from '#/components/SpotifyButton.vue';
export default Vue.extend({
name: 'ArtistPage',
components: {
SpotifyButton
},
async asyncData({ $axios, params, error }: Context) {
try {
const artist: FullArtist = await $axios.$get(`/api/artists/${params.id}`);
return { artist };
} catch (e) {
error({ statusCode: 404, message: 'Artist not found' });
}
},
data() {
return {
artist: {
name: ''
} as FullArtist
};
}
});
</script>
However if I try to import SpotifyButton into another component in the components directory in the same way, I get the following error.
Here is the ArtistPreview component, which is in the components directory:
<template functional>
<spotify-button :spotify-uri="props.artist.uri"/>
</template>
<script lang="ts">
import Vue, { PropType } from 'vue';
import SpotifyButton from '#/components/SpotifyButton.vue';
import SimpleArtist from '#/types/SimpleArtist';
export default Vue.extend({
name: 'ArtistPreview',
components: {
SpotifyButton
},
props: {
artist: {
type: Object as PropType<SimpleArtist>,
required: true
}
}
});
</script>
Am I missing something? Why does an import that works perfectly fine in a pages directory component not work in a components directory component?

This was happening because I'm using functional components. It turns out you can't nest functional components without doing some funky workarounds. Here's the GitHub issue with a few solutions.
I went with the first solution, so my ArtistPreview component now looks something like this:
<template functional>
<spotify-button :spotify-uri="props.artist.uri"/>
</template>
<script lang="ts">
import Vue, { PropType } from 'vue';
import SpotifyButton from '#/components/SpotifyButton.vue';
import SimpleArtist from '#/types/SimpleArtist';
Vue.component("spotify-button", SpotifyButton);
export default Vue.extend({
name: 'ArtistPreview',
props: {
artist: {
type: Object as PropType<SimpleArtist>,
required: true
}
}
});
</script>

Go with:
import SpotifyButton from '~/components/SpotifyButton.vue'
With Typescript is better use another approach: Add 'nuxt-property-decorator' and follow his flow.
So, you define your component as follow:
<script lang="ts">
import { Component, Vue } from 'nuxt-property-decorator'
import SpotifyButton from '~/components/SpotifyButton.vue'
#Component({
components: {
SpotifyButton
},
})
class AnotherComponent extends Vue {
...
}
export default AnotherComponent
</script>
[Nuxt Property Decorator on Github][1]
I think is important to read the official [Nuxt Typescript documentation][2] to a proper setup.
I hope it helps!
[1]: https://github.com/nuxt-community/nuxt-property-decorator
[2]: https://typescript.nuxtjs.org/

Related

Vue 3 + vuex store is not defined

I used Vue 3 cli to install new testing ground for store and router to learn those.
Project come like this :
main.js:
import { createApp } from "vue";
import App from "./App.vue";
import router from "./router";
import store from "./store";
createApp(App).use(store).use(router).mount("#app");
store.js (just added count for testing):
import { createStore } from 'vuex'
export default createStore({
state: {
count: 0
},
mutations: {},
actions: {},
modules: {},
});
and in views:
Home.vue:
<template>
<div class="home">
<img alt="Vue logo" src="../assets/logo.png" />
<HelloWorld msg="Welcome to Your Vue.js App" />
</div>
</template>
<script>
// # is an alias to /src
import HelloWorld from "#/components/HelloWorld.vue";
export default {
name: "Home",
components: {
HelloWorld,
},
mounted() {
console.log(store.state.count)
},
};
</script>
By all that I have read I should be able to access store in component with:
mounted() {
console.log(store.state.count)
},
But i get store is not defined.
While it is obliviously imported and used in main app with index.js's:
import store from "./store";
createApp(App).use(store)
I heave spent hours on this, please advise. This is out of the box cli installation, i don't understand what they wont me to do...
You've to access it using this and prepended by $ sign:
export default {
name: "Home",
components: {
HelloWorld,
},
mounted() {
console.log(this.$store.state.count)
},
};

Personal Vue 3 component package missing template or render function

I recently uploaded my own Vue 3 component to NPM to make it usable for others. When using it in other projects it gives this warning:
[Vue warn]: Component is missing template or render function.
at <VueToggle id="1" title="Toggle me" >
at <App>
What could be the reason this is happening? The way I am building and publishing the app is by running this code.
import VueToggle from "./components/VueToggle";
export default {
install(app) {
app.component("vue-toggle", VueToggle);
}
};
And then running this build command in my package.json:
"build-library": "vue-cli-service build --target lib --name vue-toggle-component ./src/install.js",
How I use my component in a different project:
<template>
<VueToggle id="1" title="Toggle me"/>
</template>
<script>
import VueToggle from 'vue-toggle-component';
export default {
name: 'App',
components: {
VueToggle,
}
}
</script>
The component itself:
<template>
<section class="wrapper" :class="{dark: darkTheme}" :title="title">
<input
:id="id"
:name="name"
v-model="toggleState"
class="toggle"
type="checkbox"
#click="toggleState = !toggleState"
/>
<label :for="id" class="toggler" :style="[toggleState && {'background': activeColor}]"/>
<span class="title" v-text="title" #click="toggleState = !toggleState"/>
</section>
</template>
<script>
export default {
name: 'VueToggle',
props: {
activeColor: {type: String, default: '#9FD6AE'},
darkTheme: {type: Boolean, default: false},
id: {type: String, required: true},
name: {type: [String, Boolean], default: false},
title: {type: String, required: true},
toggled: {type: Boolean, default: false},
},
data() {
return {
toggleState: this.toggled
}
},
}
</script>
The package: https://www.npmjs.com/package/vue-toggle-component
The following concerns a new project using Vue 3 & Typescript created with Quasar CLI (v2 beta). Although I'm getting the same error reported by the OP, my source layout might be different as I'm not using single-file components.
[Vue warn]: Component is missing template or render function.
I resolved the above issue by specifying the vue file as the component source. I typically split my components into separate vue and ts files.
The related fragment:
MyComponent: require("./components/My.vue").default
In context:
export default defineComponent({
name: "App",
components: {
MyComponent: require("./components/My.vue").default
},
setup() {
...
To quiet the linters, I have the following es-lint comments
export default defineComponent({
name: "App",
components: {
// eslint-disable-next-line #typescript-eslint/no-unsafe-assignment, #typescript-eslint/no-unsafe-member-access, #typescript-eslint/no-var-requires
MyComponent: require("./components/My.vue").default
},
Ideally, the import statement would be used instead of the inline require assignment.
The problem is that you are trying to import 'vue-toggle-component' like a Vue component, when your library is exporting a Vue plugin (made up of an install function that declares your component).
There are two way to fix this.
Solution 1
Remove the component import entirely.
// component.vue (separate project)
<template>
<VueToggle id="1" title="Toggle me"/>
</template>
<script>
export default {
name: 'App'
}
</script>
Then import your library plugin and styles in index.js of the separate project. You should activate the plugin using Vue.use().
// index.js (separate project)
import { createApp } from "vue";
import App from './App.vue';
import VueToggleComponent from 'vue-toggle-component';
import '#vue-toggle-component/dist/style.css';
const app = createApp(App);
app.mount('#app');
app.use(VueToggleComponent);
Your component should now be imported by default into the project and can be used from anywhere.
Solution 2
Add anonymized exports for each component for individual importing.
// install.js
import VueToggle from "./components/VueToggle";
export default {
install(app) {
app.component("vue-toggle", VueToggle);
}
};
export { default as VueToggle } from "./components/VueToggle";
Then import the component as a non-default export in your separate project.
// component.vue (separate project)
<template>
<VueToggle id="1" title="Toggle me"/>
</template>
<script>
import { VueToggle } from 'vue-toggle-component';
export default {
name: 'App',
components: {
VueToggle,
}
}
</script>
Finally, install your library's styles.
// index.js (separate project)
import { createApp } from "vue";
import App from './App.vue';
import '#vue-toggle-component/dist/style.css';
const app = createApp(App);
app.mount('#app');
Conclusion
Personally, I think Solution #2 is more flexible and intuitive to use.
When you use your component, replace
import VueToggle from 'vue-toggle-component';
with
import VueToggle from 'vue-toggle-component.vue';
Or if component users use webpack, they may specify in config:
resolve: {
extensions: ['.vue', '.ts', '.js']
}

Why won't my first Vue component compile? / How to load vue-formio module?

I'm new to both Vue and Form.io, so there is something simple I'm missing here. I'm getting the error "Module not found: Error: Can't resolve 'vue-formio'" in this Form.vue component:
<template>
<formio src="https://rudtelkhphmijjr.form.io/demographics" v-on:submit="onSubmitMethod" />
</template>
<script>
import { Formio } from 'vue-formio';
export default {
components: {
formio: Formio
},
methods: {
onSubmitMethod: function(submission) {
console.log(submission);
}
}
};
</script>
This was an iteration of original Formio instruction that said "embed a form within your vue application, create a vue component using [this] formio component":
<template>
<formio :src="formUrl" v-on:submit="onSubmitMethod" />
</template>
<script>
import { Formio } from 'vue-formio';
export default {
data: function() {
// Load these from vuex or some other state store system.
return {
formUrl: "https://rudtelkhphmijjr.form.io/demographics"
}
},
components: {
formio: Formio
},
methods: {
onSubmitMethod: function(submission) {
console.log(submission);
}
}
};
</script>
But this too also returned the "Module not found: Error". Here is my App.vue:
<template>
<div id="app">
<Form />
</div>
</template>
<script>
import Form from './components/Form.vue'
export default {
name: 'app',
components: {
Form
}
}
</script>
I set up the basic project using Vue CLI and used npm install --save vue-formio before firing it up. Newbie help greatly appreciated!
I've also just noticed that vue-formio is not registered (as a dependency?) in package.json so perhaps that is related.
In documentation import { Form } from 'vue-formio';
so you should replace your import on 6 line to import { Form: Formio } from 'vue-formio';

Vue components - recursive rendering or what is the problem here?

I'm trying to make a new Vue app with 2 components but the components don't render.
The error is - "Unknown custom element: - did you register the component correctly? For recursive components, make sure to provide the "name" option."
I read quite a bit on the problem but could not identify the problems in the code unlike with others' codes.
Seems OK to me, not the first app with components I've written :/
App:
require('../../lib/jquery.event.drag-2.2/jquery.event.drag-2.2');
require('../../lib/jquery.event.drag-2.2/jquery.event.drag.live-2.2');
require('../../lib/jquery.event.drop-2.2/jquery.event.drop-2.2');
require('../../lib/jquery.event.drop-2.2/jquery.event.drop.live-2.2');
import Vue from 'vue';
import Axios from 'axios';
Vue.prototype.$http = Axios;
import tournamentCourtManager from
'../../components/tournament/courtManager/courtManager';
import tournamentScheduleButton from
'../../components/tournament/tournamentScheduleButton';
import { store } from "../../store/store";
new Vue({
el: '#tournamentMatchSettingsApp',
store,
components: { 'tournamentCourtManager' : tournamentCourtManager,
'tournamentScheduleButton' : tournamentScheduleButton }
});
tournamentCourtManager:
<template>
<button type="submit" class="btn btn-info">
dadada
</button>
<script>
export default {
name: 'tournamentScheduleButton',
data() {
return {}
},
mounted: function mounted() {
},
methods: {
}
}
</script>
courtManager:
<template>
<div id="tournamentCourtManager">
..
</div>
</template>
courtManager JS:
export default {
name: 'tournamentCourtManager',
components: {
'match-cell': matchCell
},
data() {
return {
};
},
....
}
And the code that prompts the error -
<tournamentschedulebutton></tournamentschedulebutton>
<tournamentcourtmanager></tournamentcourtmanager>
Because you have named the components like 'tournamentCourtManager' in the components object, they must be named like <tournament-court-manager> in the template.

How to access an Vue component within another component?

I'm using the single file components, but I can't access a component via another component, follow what I've tried...
<template>
<div id="containerPrincipal" #click.stop="teste">
...
<template>
<script>
/*Other component*/
import flex_div from './elementos/Div.vue'
export default {
name: 'containerPrincipal',
methods : {
teste () {
componente = new flex_div().$mount();
console.log(componente);
}
},
components: {
flex_div
}
}
</script>
Error
_Div2.default is not a constructor
How can I fix this?
To resolve I had to instantiate the Vue again and reference the component...
<template>
<div id="containerPrincipal" #click.stop="teste">
...
<template>
<script>
/*Other component*/
import Vue from 'vue'
import flex_div from './elementos/Div.vue'
let flexdiv = Vue.component('divFlex', flex_div);
export default {
name: 'containerPrincipal',
methods : {
teste () {
let componente = new flexdiv().$mount();
console.log(componente);
}
},
components: {
flexdiv
}
}
</script>
I might be wrong but isn't this what you want to do?
<template><
<div id="containerPrincipal" #click.stop="teste">
<flex-div ref="flex_div"></flex-div>
</div>
<template>
<script>
/*Other component*/
import Vue from 'vue'
import flexdiv from './elementos/Div.vue'
export default {
name: 'containerPrincipal',
methods : {
teste () {
let componente = this.$refs.flex_div
console.log(componente);
}
},
components: {
flexdiv
}
}
</script>