Nuxt error window is not defined when refreshing the page - javascript

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>

Related

Logic behind loading Views with vuex dependencies vueJS

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>

Bind element inside a for loop Vue not working properly

In the following Vue Component I want to loop through dwarfs array. And as long as I am in the current component, everything is fine (TEST) and also all the following properties are correct.
Currenct_Component.vue :
<template>
<div>
<h2>Stamm: {{ tribeName }}</h2>
<div class="card-container">
<div class="card" style="width: 18rem;" v-for="dwarf in dwarfs" :key="dwarf.name">
<!-- TEST -->
<p>{{dwarf}}</p>
<!-- CHILD COMPONENT -->
<app-modal
:showModal="showModal"
:targetDwarf="dwarf"
#close="showModal = false"
#weaponAdded="notifyApp"
/>
<!-- <img class="card-img-top" src="" alt="Card image cap">-->
<div class="card-body">
<h3 class="card-title" ref="dwarfName">{{ dwarf.name }}</h3>
<hr>
<ul class="dwarf-details">
<li><strong>Alter:</strong> {{ dwarf.age }}</li>
<li><strong>Waffen:</strong>
<ul v-for="weapon in dwarf.weapons">
<li><span>Name: {{ weapon.name }} | Magischer Wert: {{ weapon.magicValue }}</span></li>
</ul>
</li>
<li><strong>Powerfactor:</strong> {{ dwarf.weapons.map(weapon => weapon.magicValue).reduce((accumulator, currentValue) => accumulator + currentValue) }}</li>
</ul>
<button class="card-button" #click="showModal = true"><span class="plus-sign">+</span> Waffe</button>
</div>
</div>
</div>
<button id="backBtn" #click="onClick">Zurück</button>
</div>
</template>
<script>
import Modal from './NewWeaponModal.vue';
export default {
data() {
return {
showModal: false,
}
},
components: { appModal : Modal },
props: ['tribeName', 'dwarfs'],
methods: {
onClick() {
this.$emit('backBtn')
},
notifyApp() {
this.showModal = false;
this.$emit('weaponAdded');
}
},
}
</script>
But when I bind the element dwarf to the Child Component <app-modal/> it changes to the next dwarf in the array dwarfs (TEST) - (So as the result when i add a new weapon in the modal-form it gets added to the second dwarf...):
Child_Component.vue :
<template>
<div>
<div class="myModal" v-show="showModal">
<div class="modal-content">
<span #click="$emit('close')" class="close">×</span>
<h3>Neue Waffe</h3>
<!-- TEST -->
<p>{{ targetDwarf }}</p>
<form>
<input
type="text"
placeholder="Name..."
v-model="weaponName"
required
/>
<input
type="number"
placeholder="Magischer Wert..."
v-model="magicValue"
required
/>
<button #click.prevent="onClick">bestätigen</button>
</form>
</div>
</div>
</div>
</template>
<script>
export default {
data() {
return {
weaponName: '',
magicValue: '',
}
},
props: ['showModal', 'targetDwarf'],
methods: {
onClick() {
if(this.weaponName !== '' &&
Number.isInteger(+this.magicValue)) {
let newData = {...this.dwarf};
newData['weapons'] = [
...this.dwarf['weapons'],
{
"name": this.weaponName,
"magicValue": Number.parseInt(this.magicValue)
},
];
this.$http.post("https://localhost:5019/api", newData)
.then(data => data.text())
.then(text => console.log(text))
.catch(err => console.log(err));
this.$emit('weaponAdded');
} else {
alert('You should fill all fields validly')
}
},
}
}
</script>
It looks like you have the <app-modal/> component inside of the v-for="dwarf in dwarfs" loop, but then the control for showing all of the modal components created by that loop is just in one variable: showModal. So when showModal is true, the modal will show each of the dwarfs, and I'm guessing the second dwarf's modal is just covering up the first one's.
To fix this, you could move the <app-modal /> outside of that v-for loop, so there's only one instance on the page, then as part of the logic that shows the modal, populate the props of the modal with the correct dwarf's info.
Something like this:
<div class="card-container">
<div class="card" v-for="dwarf in dwarfs" :key="dwarf.name">
<p>{{dwarf}}</p>
<div class="card-body">
<button
class="card-button"
#click="() => setModalDwarf(dwarf)"
>
Waffe
</button>
</div>
</div>
<!-- Move outside of v-for loop -->
<app-modal
:showModal="!!modalDwarfId"
:targetDwarf="modalDwarfId"
#close="modalDwarfId = null"
#weaponAdded="onDwarfWeaponAdd"
/>
</div>
export default {
//....
data: () => ({
modalDwarfId: null,
)},
methods: {
setModalDwarf(dwarf) {
this.modalDwarfId = drawf.id;
},
onDwarfWeaponAdd() {
//...
}
},
}
You could then grab the correct dwarf data within the modal, from the ID passed as a prop, or pass in more granular data to the modal so it's more "dumb", which is the better practice so that the component isn't dependent on a specific data structure. Hope that helps
Courtesy of #Joe Dalton's answer, a bit alternated for my case:
<div class="card" style="width: 18rem;" v-for="dwarf in dwarfs" :key="dwarf.name">
...
<button class="card-button" #click="setModalDwarf(dwarf)"><span class="plus-sign">+</span> Waffe</button>
<div>
<app-modal
:showModal="showModal"
:targetDwarf="currentDwarf"
#close="showModal = false"
#weaponAdded="notifyApp"
/>
<script>
import Modal from './NewWeaponModal.vue';
export default {
data() {
return {
showModal: false,
currentDwarf: null,
}
},
components: { appModal : Modal },
props: ['tribeName', 'dwarfs'],
methods: {
setModalDwarf(dwarf) {
this.currentDwarf = dwarf;
this.showModal = true;
},
...
}
</script>

VUE template problem to center text with text-center

I have problem to center h1 tag category.title with bootstrap property "text-center" in vue.js component, the title appears in left side. Everything else, for example category.description is centering correctly. It looks like only title refuses to be influenced by any align.
<template>
<div class="row" v-for="category in laravelData">
<h1 class="text-center">{{ category.title }}</h1>
<p class="text-center pb-3">{{ category.description }}</p>
<div class="col-sm-12 col-md-6 col-lg-6" v-for="project in category.project">
<div class="projects-thumb">
<img :src="/img/ + photo.filename" class="img-responsive" alt="" v-for="photo in project.photo">
<h3 class="pt-3">{{ project.title }}</h3>
<p class="mt-2 p-3">{{ project.description }}</p>
</div>
</div>
</div>
</template>
<script>
export default {
data() {
return {
category: {},
laravelData: {},
id: '',
succmsg: true,
showmodal: false,
actionmsg: '',
}
},
methods: {
projectList() {
var id = _.last( window.location.pathname.split( '/' ) );
axios.get('/api/category/'+ id).then((response) => {
this.laravelData = response.data;
});
}
},
mounted() {
this.projectList();
}
}
</script>
Maybe the problem is because of several loops in this template?

router click.native passes wrong parameter in Vue

I am trying to pass an account to a removal method in Vue. I output the account right before the router-link and it shows the correct one, but outputting account in the method shows a different one.
HTML
// OUTPUTTING
{{chosenAccount}}
// ROUTER CLICK
<router-link #click.native="remove(chosenAccount)" to="/summary/" text="summary"><span cursor: pointer;" title="Summary"></span></router-link>
JS
methods:{
remove(account){
console.log('removed ',account);
this.removeAccountData(account);
},
},
The console.log in remove shows my other account and not the one I have selected on the page.
Am I missing anything?
Edit:
<template>
<div id="container" >
<div id="header">
<router-link #click.native="remove(chosenAccount, $event)" to="/summary/" text="summary"><span cursor: pointer;" title="Summary"></span></router-link>
</div>
<div id="content" >
<div id="settings" class="text-center" style="margin-left: -4px;">
<h3 style="color:#1173BD;"><span v-text="chosenAccount.network_name"></span></h3>
<!-- OUTPUTS ACCOUNT #2 (CORRECT) IN LOCAL STORAGE-->
{{chosenAccount}}
<h4>has been removed</h4>
<h3>
<router-link #click.native="remove(chosenAccount, $event)" to="/summary/" text="summary"><span cursor: pointer;" title="Summary"></span></router-link>
</h3>
</div>
</div>
<div id="footer" style="text-align:center;">
<router-link to="/" text="add_new_account"><span aria-hidden="true"> Add New Account</span></router-link>
</div>
</div>
</template>
<script>
import axios from "axios"
import {dataGetter} from "#/datagetter/dataGetter";
export default{
name:"removed",
mixins:[dataGetter],
data(){
return{
}
},
methods:{
getNewData(){
this.getData();
},
remove(account, event){
// OUTPUTS ACCOUNT #1 (INCORRECT) IN LOCAL STORAGE
console.log(account);
},
},
computed:{
networkName(){
let account = this.account;
if( account != undefined){
return account.network_name;
}
return "";
},
}
}
</script>

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