Why can't apollo get data in first request? - vue.js

I am using props to get the Id of birds.
When I first start the app and click to enter the second-page, data won't load.
It gives the following error;
JS: [Vue warn]: Error in render: "TypeError: Cannot read property 'name' of undefined"
JS:
JS: found in
JS:
JS: ---> <Detail> at components/Detail.vue
JS: <Frame>
JS: <Root>
But after I click on button second time it loads properly.
I have created a repo showing the problem. Please check it here:
github repo
Working graphql playground
<template>
<Page>
<StackLayout>
<Label :text="bird.name" textWrap="true" />
<Button text="get data" #tap="getData" />
</StackLayout>
</Page>
</template>
<script>
import { gql } from "apollo-boost";
export default {
props: ["birdID"],
apollo: {
bird: {
query: gql`
query($id: ID!) {
bird(id: $id) {
id
name
}
}
`,
variables() {
return {
id: this.birdID,
};
},
},
},
methods: {
getData() {
console.log("bird data: ", this.bird);
},
},
mounted() {},
};
</script>
<style></style>
Is there a better way to get data?
Answer was correct
I have updated github repo with solution.
https://github.com/kaanguru/query-data-from-prop

The issue is that your template
<Label :text="bird.name" textWrap="true" />
attempts to display bird.name before your query has completed.
The Vue Apollo documentation shows a simple work-around by defining some initial defaults
You can initialize the property in your vue component's data hook
data: () => ({
bird: {
name: null
}
}),
Alternately, make use of the Loading state feature to conditionally render your components
<Label v-if="$apollo.loading" text="loading" textWrap="true" />
<StackLayout v-else>
<Label :text="bird.name" textWrap="true" />
<Button text="get data" #tap="getData" />
</StackLayout>

Related

Vue: unexpected token: ':' in image path

I'm passing an image path to a child Vue component but get:
unexpected token: ':' in https://path.to.image/image.jpg
I've created a Vue sandbox that illustrates the problem.
HTML
<div class="js-product-map">
<product-map
:logo="https://path.to.image/image.jpg"
/>
</div>
Component
<template>
<div>
<img :src="image" />
</div>
</template>
<script>
export default {
props: {
logo: {
type: String,
required: true,
},
},
computed: {
image() {
return this.logo;
},
},
};
</script>
What am I doing wrong?
Can you clarify what it is you're trying to do exactly? Because it's not entirely clear to me.
The error you're getting is coming from this:
<div class="js-product-map">
<product-map
:logo="https://path.to.image/image.jpg"
/>
</div>
It's complaining about the : on the logo attribute. But I question why you're doing it this way to begin with. Typically you only want a single selector in index.html as the root of your app. Unless you're trying to have multiple Vue instances.
The way that you have your ProductMap component set up in your sandbox seems like it would accomplish what you're trying to do in index.html.

Cannot read properties of undefined (reading 'id')

I'm using #storybook/vue to make my storybook and I'm trying to embed a view component in my mdx files but I get these errors:
"Cannot read properties of undefined (reading 'id')" and "The above error occurred in the component".
I've followed the documentation and searched on different forums but I can't find it.
my code :
button.docs.mdx
import { Canvas, Meta, Story } from '#storybook/addon-docs';
import Table from "#/stories/stories-component/Table.vue";
<Meta title="Bootstrap Custom Button Documentation" />
# Test
<Canvas>
<Story name="test table">
{() => {
return {
components: { Table },
template: `<Table></Table>`
};
}}
</Story>
</Canvas>
Table.vue
<template>
<p>test component </p>
</template>
<script>
export default {
name:"Table",
props: {}
}
</script>

parent component is not getting data from child in Nuxt app

This is driving me crazy so I hope that anyone can help.
I made a Nuxt app with #nuxt/content and I'm using Netlify-CMS to create content. That all seems to work fine. However I'm trying to display a component that contains a loop of the MD-files that I have, but in the index.vue nothing of the loop is displayed.
I know (a little) about props and $emit, but as I am not triggering an event this dosen't seem to work.
Component code:
<template>
<section>
<h1>Releases</h1>
<li v-for="release of rfhreleases" :key="release.slug">
<h2>{{ release.artist }}</h2>
</li>
</section>
</template>
<script>
export default {
components: {},
async asyncData({ $content, params }) {
const rfhreleases = await $content('releases', params.slug)
.only(['artist'])
.sortBy('createdAt', 'asc')
.fetch()
return {
rfhreleases,
}
},
}
</script>
And index.vue code:
<template>
<div>
<Hero />
<Releases />
<About />
<Contact />
</div>
</template>
<script>
export default {
head() {
return {
script: [
{ src: 'https://identity.netlify.com/v1/netlify-identity-widget.js' },
],
}
},
}
</script>
If I place my component code as part of index.vue, everything work, but I would love to avoid that and thats why I'm trying to place the loop in a component.
As stated on the Nuxt documentation:
This hook can only be placed on page components.
That means asyncData only works on components under pages/ folder.
You have several options:
You use fetch instead. It's the other asynchronous hook but it's called from any component. It won't block the rendering as with asyncData so the component it will instanciated with empty data first.
You fetch your data from the page with asyncData and you pass the result as a prop to your component
<template>
<div>
<Hero />
<Releases :releases="rfhreleases" />
<About />
<Contact />
</div>
</template>
<script>
export default {
async asyncData({ $content, params }) {
const rfhreleases = await $content('releases', params.slug)
.only(['artist'])
.sortBy('createdAt', 'asc')
.fetch()
return {
rfhreleases,
}
},
}
</script>

NativeScript-vue custom switch not working

I'm trying to make a re-usable custom Switch component, but my v-model is not working. Here's the situation:
My component correctly emits the tap event
My component updates its data correctly
However, the parent data doesn't get updated despite being hooked to the child with a v-model
Here are some snippets showing my setup:
// MY COMPONENT
<template>
<Switch
dock="right"
backgroundColor="red"
offBackgroundColor="yellow"
v-model="model"
/>
</template>
<script>
export default {
name: "SettingsSwitch",
props: {
value: {
type: Boolean,
required: true
}
},
data() {
return {
model: this.value
};
},
watch: {
model: function(value) {
this.$emit("tap", value);
}
}
};
</script>
In the example below, I have 2 Switches:
- A normal one that works and whose data gets updated
- The one linked to my child component and the data does not get updated
// My parent view
<template>
<ViewWrapper viewTitle="Change your settings" pageColor="tertiary">
<StackLayout class="content-wrapper">
<StackLayout class="category-wrapper">
<DockLayout class="field-wrapper" stretchLastChild="true">
<Switch v-model="myCheck" dock="right" #tap="test" />
<StackLayout>
<Label text="Mon label" class="field-label" />
<Label text="Ma valeur actuelle" class="field-value" />
</StackLayout>
</DockLayout>
<DockLayout class="field-wrapper" stretchLastChild="true">
<SettingsSwitch v-model="myCheck2" #tap="test2" />
<StackLayout>
<Label text="Mon label" class="field-label" />
<Label text="Ma valeur actuelle" class="field-value" />
</StackLayout>
</DockLayout>
</StackLayout>
</StackLayout>
</ViewWrapper>
</template>
<script>
import { ViewWrapper } from "#/components";
import SettingsSwitch from "./SettingsSwitch";
export default {
name: "Settings",
components: { ViewWrapper, SettingsSwitch },
data() {
return {
myCheck: false,
myCheck2: false
};
},
methods: {
test() {
console.log(this.myCheck);
},
test2(v) {
console.log("emit", v); // <--- Value changes each time
console.log("model", this.myCheck2); // <--- Value never changes
}
}
};
</script>
I've tried playing around with different setups, like removing the watch and directly calling a method that does the $emit but it doesn't seem to fix the issue
Any thoughts?
So I managed to fix my issue. My mistake was that I was emitting tap instead of input in my component. I feel stupid but I'm leaving this up instead someone struggles like I did
The bottom line is:
You can use v-model on any tag, but in order to update its value, it will need to receive an "input" event with some data. That's why my child component must perform this.$emit("input", value);

conditionnal rendering component vue.js

In my vue.js app I would like to rendering some html element only if I have selected item before.
If selected item then I displayed it again into small mini card version.
This seems to be very easy to do that with this code :
<div v-if="objSelected">
<p>test</p>
<mini-card v-bind:item="objSelected"></mini-card>
</div>
But the v-if mechanism works because it display well the p tag into this. But never display the mini-card component
Also I trying that :
<mini-card v-if="objSelected" v-bind:item="objSelected"></mini-card>
but the result is the same...
My mini-card component works into another component if I render this directly on the mounted component like this :
<mini-card v-bind:item="obj1"></mini-card>
The issue seems to be dealing with dynamic rendering component.
But why this simple dynamic rendering code doesn't work ?
What can I do for displaying component dynamically or after user action ?
EDIT :
declaration of Test component which contain mini-card (and display statically) :
import MiniCard from "./mini-card"
export default
{
name: "Test",
components: {MiniCard},
declaration of Game component which contain mini-card (and don't displayed dynamically) :
import MiniCard from "./mini-card"
export default
{
name: "game",
components: {MiniCard},
My instance vue declaration :
new Vue({
components: { App },
router,
store,
template: '<App/>'
}).$mount('#app')
I use the vue-router which display other component without included those into this declaration.
Finally
My problem become to this line for render image into component (if component call statically, this component with image run but if I call that dynamically it generate error) :
<img :src="require('#/assets/' + this.item.logo)" width="50" height="50" alt="logo"></img>
Issue say :
[Vue warn]: Error in render: "Error: Cannot find module './'"
Impossible to render dynamic image with vuejs ?
<img :src="require('#/assets/' + this.item.logo)" width="50" height="50" alt="logo"></img>
Try something like this:
<img :src="`#/assets/${item.logo}`" width="50" height="50" alt="logo"></img>
To updating your component with dynamic data:
// extend and register in one step
Vue.component('mini-card', {
//Props camelCase in JS
props: {
item: {
type: Object
}
},
// show your data
template: `<strong>{{ item.name }}</strong>`
});
new Vue({
el: '#container',
data: {
selected: null,
items: [{ name: "Water" }, { name: "Fire" }, { name: "Cold" }]
},
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="container">
<select v-model="selected">
<option
v-for="item in items"
:value="item">
{{ item.name }}
</option>
</select>
<mini-card :item="selected" />
</div>