Logic behind loading Views with vuex dependencies vueJS - javascript

I have an API call that loads on my App.vue that populates my vuex store's state. The App.vue by default loads a Home.vue that displays images based on the store's state. The images load, but throw a lot of console errors before the state is populated. I'm not sure what the best logic is, in general, to delay loading VIEWS before the data they depend on is finished loading. Existing answers make sense for components but I can't bind data to the router-view, and don't think passing it in params makes sense. I'm a n00b.
//App.vue
<template>
<div id="app">
<div id="nav">
<div id="nav-logo"></div>
<div id="nav-links">
<router-link to="/">Home</router-link>
<router-link to="/pokemon">Pokemon</router-link>
</div>
</div>
<router-view></router-view>
</div>
</template>
<script>
export default {
created(){
this.init();
},
methods:{
init(){
this.$store.dispatch('fetchPokemon', 'gen1');
}
}
}
</script>
//Home.vue
<template>
<div>
<div id="pokemonAFront">
<img :src="pokeImg[randomA].sprites.front_default" alt="">
</div>
<div id="pokemonABack">
<img :src="pokeImg[randomA].sprites.back_default" alt="">
</div>
</div>
</template>
<script>
export default {
data(){
return{
randomA: Math.floor(Math.random()* 20),
},
computed:{
pokeImg(){
return this.$store.state.pokemon
}
}
</script>
//store/index.js
state:{
pokemon:[]
},
mutations: {
SET_POKEMON(state, pokemon){
state.pokemon = pokemon;
}
},
actions: {
fetchPokemon(context, currentGen){
context.commit('SET_POKEMON', pokeData.fetchPokemon(currentGen)) //api call
}
}
What logic works best for delaying loading views/components when their vuex dependencies haven't loaded yet?

The simplest is to render the dom only after the pokeImg gets populated from API:
<div v-if="pokeImg.length > 0">
<div id="pokemonAFront">
<img :src="pokeImg[randomA].sprites.front_default" alt="">
</div>
<div id="pokemonABack">
<img :src="pokeImg[randomA].sprites.back_default" alt="">
</div>
</div>

Related

Nuxt error window is not defined when refreshing the page

Everything works fine but when i reload the page i get the error window is not defined
I have added it to plugins and I have tried both ssr:false and mode:'client'
I think the problem is Nuxt doesn't apply the config to the Vue when reloading the page and use the server side to render the code.
plugins: [
{ src: '~/plugins/lightbox.js', ssr:false }
],
this is the plugin file
// plugins/lightbox.js
import GLightbox from 'glightbox'
import '../node_modules/glightbox/dist/css/glightbox.css'
this is a single page that is using the Glightbox library
_post.vue
<template>
<div class="main">
<div class="wrapper">
<div class="container">
<h2 class="heading">{{ data.title }}</h2>
<p>{{ data.description }}</p>
</div>
<div>
<div class="gallery">
<div v-for="item in data.images" :key="item.id">
<!-- <img class="image" :src="item.image" alt=""> -->
<a #mouseenter.prevent="setLightBox" :href="item.image" class="glightbox">
<figure>
<img :src="item.image" class="image">
</figure>
</a>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import GLightbox from 'glightbox'
export default {
// transition:'slide-fade',
async asyncData({ $axios, params }) {
const data = await $axios.$get(`api/v1/projects/${params.post}`)
console.log(data)
return { data }
},
data(){
return {
lightbox: ''
}
},
methods: {
setLightBox(){
this.lightbox = GLightbox({
touchNavigation: true,
loop: false,
})
},
toggleLightBox(){
this.lightbox.open()
}
}
}
</script>

[Vue warn]: Error in callback for watcher "childItems": "TypeError: Cannot read property 'tag' of undefined"

I am currently working on the Store page of a webapp I am building using Axios to fetch product data and then display it in a vue template. All the backend works and the front end renders successfully as well, however Vue gives a Warning twice in the console stating:
Error in callback for watcher "childItems": "TypeError: Cannot read property 'tag' of undefined"
To combat this, I have tried wrapping the v-for for iterating products in a v-if that is set to true by the async getProducts function which sends a get request for the product data to display. I have tried moving around where the getter function is, placing it in Mounted, beforeMount, created, used vue-async-computed and all have rendered fine but none have gotten rid of the error.
Here is my store.vue file:
<template>
<div id="store">
<section>
<b-tabs>
<div class="container">
<div v-if="fetchComplete">
<b-tab-item v-for="(product, index) in products" :label="product.categories[0].title" :key="index">
<div class="card">
<div class="card-image">
<figure class="image">
<img :src="'http://localhost:1339'+ product.product_color_imgs[0].img[0].url" :alt="product.title">
</figure>
</div>
<div class="card-content">
<p class="title is-4">{{product.title}}</p>
</div>
</div>
</b-tab-item>
</div>
</div>
</b-tabs>
</section>
</div>
</template>
<script>
import axios from 'axios';
export default {
data() {
return {
products: [],
fetchComplete: false
}
},
async beforeCreate() {
await axios({
method:'get',
url:'http://localhost:1339/products'
}).then(res => {
this.products = res.data;
this.fetchComplete = true;
}).catch(err => {
console.log(err);
})
}
}
</script>
In this version I used beforeCreate but I have tested it with the other life-cycle functions. My guess is that the promise from beforeCreate returns after the page has rendered the first time.
Note: I am relatively new to Vue.js so it is possible I may have overlooked something
Thank you in advance!
This is caused by placing the <div> tags inside of the <b-tabs> component but ouside of a <b-tab-item>. It seems that <b-tabs> requires that no element other than a <b-tab-item> be placed in the slot root.
Try moving the divs outside of that component like:
<div class="container">
<div v-if="fetchComplete">
<b-tabs>
<b-tab-item v-for...>
...
</b-tab-item>
</b-tabs>
</div>
</div>

VUE- How do I put the values inside of components imported?

I remember I have seen once how to put the values in the html text area after importing components in VUE.
I'm not sure there is a way to do that or I just remember things in a wrong way.
my code is as below.
<template>
<div class="container">
<div class="row">
<Heading></Heading>
</div>
<hr>
<div class="row">
<div class="col-xs-12 col-sm-6">
<ul class="list-group">
<comp v-for='(value,index) in listing' :key='index'>{{value}}</comp>
</ul>
</div>
<serverstat></serverstat>
</div>
<hr>
<div class="row">
<footing></footing>
</div>
</div>
</template>
<script>
import Heading from './assets/Heading.vue';
import comp from './assets/comp.vue';
import serverstat from './assets/serverstatus.vue';
import footing from'./assets/footing.vue';
export default {
data() {
return {
listing: ['max','toms','judy','michael','dumdum']
}
},
components: {
Heading,comp,serverstat,footing
},
};
</script>
<style>
</style>
-comp-
<template>
<li class="list-group-item">
</li>
</template>
<script>
export default {
}
</script>
<style>
</style>
After I render this,
it doesn't show {{value}}. It only shows blank .
How do I insert the {{value}} within the html element?
Thank you in advance.
Since you are entering a value inside of a component, you can render it by using a slot in your component like this:
<template>
<li class="list-group-item">
<slot />
</li>
</template>
<comp v-for='(value,index) in listing' :key='index'>
<slot>{{ value }} </slot>
</comp>
Then in comp component use slot as
<slot/>
Not including the approach for props as you don't want to use that. Use the link above to learn more about slots.
When you use v-for it calls all the value from an array and :key='index' defines each object row from an array. If your object listing consists of firstname, lastname as your object then the value you want to print will be {{value.firstname}}. You are missing object name in value.
Can you try this once :
<comp v-for='(value,index) in listing' :key='index'>{{value.index}}</comp>

VueJS independent components events

I have a component that emit event through a bus as indicated below. The same component need to be included twice on another component. I want the events emitted to populate different variables;
//component.vue
<template>
<div>
Hello there?
<a #click="changed">New</a>
<ol>
<li v-for="option in list">
<div class='row justify-content-start'>
<div class='col-sm-6'><input v-model="option.value" type='text' placeholder="key"/></div>
<div class='col-sm-6'><input v-model="option.name" type='text' placeholder="Name"/></div>
</div>
</li>
</ol>
</div>
</template>
<script>
export default{
props:['options','iscolumn'],
data(){
return {list:this.options,item:{name:'',value:''}}
},
methods:{
changed(){
$bus.$emit('add-option',this.item,this.iscolumn);
}
}
}
</script>
/** root.vue **/
<template>
<div>
<h3>Rows</h3>
<div><rows :options="rows" :iscolumn="false"/></div>
<h3>Columns</h3>
<div><rows :options="columns" :iscolumn="true" /></div>
</div>
</template>
<script>
export default{
components:{'rows':require('./component')},
data(){
return {
columns:[],rows:[]
}
},
created(){
this.$bus.$on('add-option',(option,iscolumn)=>{
if (is_column) {this.columns.push(option);}
else this.rows.push(option);
})
}
}
</script>
When I click on the New from root both columns and rows get populated.
Looking for case where each of the component are independent, can't understand how they are sharing variables.
Any assistance will be appreciated.
Assign unique key attributes to the rows components:
<template>
<div>
<h3>Rows</h3>
<div><rows key="rows1" :options="rows" :iscolumn="false"/></div>
<h3>Columns</h3>
<div><rows key="rows2" :options="columns" :iscolumn="true" /></div>
</div>
</template>

How to pass object data from parent to child components?

I would like to know how to make the contents object visible in the video.vue component.
This is the Video.vue components. This component is where I want to access the content object that is defined in the Home.vue component.
<template>
<div>
<h1>{{title}}</h1>
</div>
</template>
<script>
export default {
data() {
return {
title: 'Video Section'
}
}
}
</script>
This is the Home.vue component:
<template>
<div class="container">
<div class="column is-one-third" v-for="content in contents.results" :content="content" :key="content.id">
<div v-show="loaded" class="loader"></div>
<div class="card" >
<div class="card-image">
<figure class="image">
<img :src="imageUrl + content.backdrop_path" alt="Image">
</figure>
</div>
<div class="card-content">
<div class="media">
<div class="media-left">
<figure class="image is-25x25">
<img id="poster-image" :src="imageUrl + content.poster_path" alt="Image">
</figure>
</div>
<div class="media-content">
<p id="movie-title" class="title is-4 no-padding">{{content.original_title}}</p>
<p><span class="title is-6"><i class="fas fa-star">{{" " + content.vote_average}}</i></span></p>
<p class="subtitle is-6"><i class="fas fa-calendar-alt">{{" " + content.release_date}}</i></p>
</div>
</div>
<div class="content">
{{ content.overview }}
<div class="background-icon"><span class="icon-twitter"></span></div>
</div>
<div id="footer-card-icons">
<i class="fas fa-info-circle"></i>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
export default{
data: () => ({
contents: [],
baseurl: 'https://api.themoviedb.org/3',
apikey: '16667866c29ba1bc29e687b4892b8d5c',
imageUrl: 'https://image.tmdb.org/t/p/w1280',
loaded: true,
}),
created: function(){
this.fetchData();
},
methods:{
fetchData: function(){
console.log('fetch data')
this.$http.get(this.baseurl + '/discover/movie?api_key=' +
this.apikey + '&sort_by=popularity.desc').then(response =>{
this.contents = response.body;
this.loaded = false;
});
}
}
}
</script>
As Emile’s commented, you should use props in order to be able to pass data from parent to child component.
In your Home.vue, add:
<videos :content=“contents”></videos>
But if your contents data type is Array:
<videos v-for=“content in contents” :key=“content.id” :content=“content”></videos>
Notice that if you use v-for to loop a component, you need to add key attribute also.
Finally, in your Video.vue, you need to define the props like below:
<template>
<div>
<p>{{ content.overview }}</p>
</div>
</template>
<script>
export default {
props: [‘content’],
data() {
return {
title: 'Video Section'
}
}
}
</script>
Remember, props are reactive. It will respond to any updates or changes in content.
UPDATE: It seems you have not properly declared your component. See codepen link. Better if you could declare any other components as Single File Components as explained here.

Categories

Resources