I have a model class for an address where I've defined a method to return the full street (combined from two seperate properties on the address model). Now I wanted to pass such an object via props to a component and use the full street method to display it.
Unfortunately, it seems that the component loses the type of the object as the method call isn't recognized in the template...
Isn't it possible to use methods in templates?
Here's some simplified code:
Parent component:
<template>
<div>
<AddressDisplay v-bind:address="AddressModel = { street: 'Bleeker', streetNr: '1' }" />
</div>
</template>
<script lang="ts">
import Vue from 'vue';
import AddressDisplay from '#/components/AddressDisplay.vue';
export default Vue.extend({
components: { AddressDisplay },
});
</script>
Child component:
<template>
<div>
{{ address.fullStreet }}
</div>
</template>
<script lang="ts">
import Vue from 'vue';
export default Vue.extend({
props: {
address: { type: Object, required: true },
},
created() {
console.log(this.address.type);
},
});
</script>
Type definition for model:
class AddressModel implements Model {
street = '';
streetNr = '';
get fullStreet(): string {
return this.street + ' ' + this.streetNr;
}
}
What happens: nothing! There is no error message, but nothing is displayed. The console print type 'undefined'.
Why you write it like this: v-bind:address="AddressModel = { street: 'Bleeker', streetNr: '1' }"? What do you expect? Especially when you don't even import "AddressModel". Have a look at examples https://class-component.vuejs.org/guide/class-component.html#data
This will probably work:
<template>
<div>
<AddressDisplay v-bind:address="address" />
</div>
</template>
<script lang="ts">
import Vue from 'vue';
import AddressDisplay from '#/components/AddressDisplay.vue';
export default Vue.extend({
components: { AddressDisplay },
data(){ return {
address: { street: 'Bleeker', streetNr: '1' }
}}
});
</script>
Related
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/
i'm really stuck in this silly problem. i worked with many props but this one is a headache. maybe i'm missing something...!!!
i'm sending an object as prop from my page to a component (as i did many times!!) but on my component it's undefined!! i tried many ways (some simple data instead of obj, not bind prop, ...) but nothing!
BTW i'm on nuxt 2.13
here's my code:
page:
<template>
<mycomp :err="inputError" />
<template>
<script>
import mycomp from '~/components/mycomp'
export default{
components:{
'mycomp': mycomp
}
data(){
return{
inputError: {
error: false,
msg: null
}
}
},
}
</script>
and my component:
<template>
<div>
{{err}} --> this show nothing too
</div>
<template>
<script>
export default{
props:['err']
data(){
return{
//
}
},
create(){
console.log(this.err) // undefined
}
}
</script>
You are making many typo mistakes. like kerbh0lz mentioned. I urge you to validate the incoming props. It will save you a lot of time in debugging.
Anyway here is the working snippet
//App.vue
<template>
<div id="app">
<HelloWorld :err="inputError"/>
</div>
</template>
<script>
import HelloWorld from "./components/HelloWorld";
export default {
name: "App",
components: {
HelloWorld
},
data() {
return {
inputError: {
error: false,
msg: null
}
};
}
};
</script>
//HelloWorld.vue
<template>
<div class="hello">
<h1>{{ err }}</h1>
</div>
</template>
<script>
export default {
name: "HelloWorld",
props: {
err: {
type: Object
}
},
created() {
console.log(this.err);
}
};
</script>
Sandbox
I have a problem where a component within a router-view that is being kept alive does not call its activated lifecycle hook when first created. The created and mounted lifecycle hooks are being called. On a second visit, the activated hook is being called.
The scenario is quite complicated as there is a bit of nesting and slot using involved.
I've tried to create a minimal example which you can find below, or a bit more detailed on https://codesandbox.io/s/251k1pq9n.
Unfortunately, it is quite large and still not as complicated as the real code which I unfortunately cannot share.
Worse, I failed to reproduce the actual problem in my minimal example. Here, the created, mounted, and activated lifecycle hooks are all called when first visiting SlotExample.
In my real code, only the created and mounted, lifecycle hooks are called on the first visit, the activated hook is called on subsequent visits. Interestingly, all lifecycle hooks are called as expected for SlotParent.
The real code involves more nesting and makes use of slots to use layout components.
My code is using Vue 2.5.16 and Vue-Router 3.0.1 but it also doesn't work as expected in Due 2.6.7 and Vue-Router 3.0.2. I am also using Vuetify and Vue-Head but don't think think this has anything to do with my problem.
index.js.
Does anyone have an idea what I could have been doing wrong. I actually suspect a bug in vue-router
when using multiple nested slots and keep-alive but cannot reproduce.
index.js
import Vue from "vue";
import VueRouter from "vue-router";
import App from "./App.vue";
import Start from "./Start.vue";
import SlotExample from "./SlotExample.vue";
const routes = [
{
path: "/start",
component: Start
},
{
path: "/slotExample/:id",
component: SlotExample,
props: true
}
];
const router = new VueRouter({ routes });
Vue.use(VueRouter);
new Vue({
render: h => h(App),
router,
components: { App }
}).$mount("#app");
App.vue
<template>
<div id="app">
<div>
<keep-alive><router-view/></keep-alive>
</div>
</div>
</template>
SlotExample.vue
<template>
<div>
<h1>Slot Example</h1>
<router-link to="/start"><a>start</a></router-link>
<router-link to="/slotExample/123">
<a>slotExample 123</a>
</router-link>
<slot-parent :id="id">
<slot-child
slot-scope="user"
:firstName="user.firstName"
:lastName="user.lastName"/>
</slot-parent>
</div>
</template>
<script>
import SlotParent from "./SlotParent.vue";
import SlotChild from "./SlotChild.vue";
export default {
name: "slotExample",
components: { SlotParent, SlotChild },
props: {
id: {
type: String,
required: true
}
}
};
</script>
SlotParent.vue
<template>
<div>
<div slot="header"><h1>SlotParent</h1></div>
<div slot="content-area">
<slot :firstName="firstName" :lastName="lastName" />
</div>
</div>
</template>
<script>
export default {
name: "slotParent",
props: {
id: {
type: String,
required: true
}
},
computed: {
firstName() {
if (this.id === "123") {
return "John";
} else {
return "Jane";
}
},
lastName() {
return "Doe";
}
}
};
</script>
SlotChild.vue
<template>
<div>
<h2>SlotChild</h2>
<p>{{ firstName }} {{ lastName }}</p>
</div>
</template>
<script>
export default {
name: "slotChild",
props: {
firstName: {
type: String,
required: true
},
lastName: {
type: String,
required: true
}
},
created() {
console.log("slotChild created");
},
mounted() {
console.log("slotChild mounted");
},
activated() {
console.log("slotChild activated");
}
};
</script>
I think you need to put SlotChild within keep-alive block.
Take a look at vue js doc about activated hook
I have this code in file app.vue :
<template>
<div id="app">
<button v-on:click="component = 'login'">aa</button>
<component v-bind:is="component"></component>
</div>
</template>
<script>
import acceuil from './components/acceuil.vue'
import login from './components/login.vue'
export default {
name: 'app',
components: {
acceuil,
login
},
data(){
return {
component: 'acceuil'
}
}
}
</script>
How can I toggle between acceuil/login in component from a different vue file ?
You need to pass the imported dependency (the object or the name of the component as a string) to v-bind:is. You can do this by returning it in a computed function and pass it to a computed property, which you then can use in the template.
<template>
<div id="app">
<button v-on:click="isLogin = true">Show Login</button>
<component v-bind:is="currentComponent"></component>
</div>
</template>
<script>
import acceuil from './components/acceuil.vue';
import login from './components/login.vue';
export default {
name: 'app',
data () {
return {
isLogin: false
};
},
computed: {
currentComponent () {
return this.isLogin ? login : acceuil;
}
},
};
</script>
See also the documentation of dynamic components in the official docs.
I have a helper.js file with contains:
module.exports = {
getSrmColor: (color) => {
return color;
}
}
My .vue file has:
<template>
<div>
{{ recipeHelper.getSrmColor(recipe.color) }}
</div>
</template>
<script>
import recipeHelper from "./helpers.js";
export default {
name: "Recipe",
props: ["recipe"]
}
</script>
I get the following error:
Property or method "recipeHelper" is not defined on the instance but referenced during render.
Make sure to declare reactive data properties in the data option.
Make new helper instance inside your vue component, like below.
<script>
import recipeHelper from "./helpers.js";
export default {
name: "Recipe",
props: [
"recipe"
],
mounted: function() {
this.recipeHelper = recipeHelper;
}
}
</script>
I think you need to create "data value" for your import value. Could you try something like that:
<script>
import recipeHelper from "./helpers.js";
export default {
name: "Recipe",
props: ["recipe"],
data: function() {return {
recipeHelper: recipeHelper
}}
}
</script>