Flickering effect while updated - Vue Component - javascript

I've created component to search posts from API by query from URL, but if I change the query I receive strange flickering effect. Count of flicks grows by one after every change. How to solve it? Vue Devtools shows that loadPosts() is called: 1time, 2 times, 3times and so on. Where's the mistake? Reloading component gives the same effect.
<template>
<v-app>
<main-header/>
<v-layout class="mx-auto default--container">
<v-flex xs12 ma-2>
<h2 class="display-2 text-xs-center main-page--header">
<span class="text__red">W</span>yniki wyszukiwania dla:
<span class="text__red">{{this.$route.query.s}}</span>
</h2>
<article-list-sample v-for="i in articles.length" :key="`${i}`" />
</v-flex>
</v-layout>
<main-footer />
</v-app>
</template>
<script>
import MainHeader from './MainHeader';
import MainFooter from './MainFooter';
import ArticleListSample from './ArticleListSample';
import API from '../api';
export default {
components: {
ArticleListSample,
MainFooter,
MainHeader
},
name: 'search',
data: () => ({
articles: []
}),
methods: {
loadPosts() {
API.get(`posts?search=${this.$route.query.s}`)
.then(response => this.articles = response['data'])
}
},
mounted() {
this.loadPosts();
},
updated() {
this.loadPosts();
}
};
</script>
<style scoped>
</style>

To add on this, the infinite loop was created because updated was triggered whenever you were changing this.articles which would then start an other asynchronous call.

I solved it. Just need to add watcher instead of updated() method.
watch: {
'$route' () {
this.loadPosts();
}
},

Related

How to dynamically create an component and return a value programmatically in vuejs

Can you please help me with my problem I want to create a plugin customized for my project so I and my team can easily apply this alert component without designing the dialog box on every page and every vuejs file?
My goal is to create an alert component plugin where we don't have to initialise the component on our vuejs files. e.g just calling.
customise_alert.fire({ title: 'Confirmation message'})
.then((result) => {
if(result.value == 1) console.log("option 1 is pressed")
});
I already tried many different approaches but this is the closest I can get to my goal.
Note: I'm using Laravel with vuejs
This is my code so far:
PopUpComponent.vue
<template>
<div>
<v-dialog v-model="dialog_confirmation" max-width="400" content-class="c8-page">
<v-card>
<v-card-title class="headline">
<v-icon v-show="icon_show" :color="icon_color">{{ icon }}</v-icon> {{ title }}
<v-icon>mdi-close</v-icon>
</v-card-title>
<v-card-text>
<div style="margin:10px;" v-html="message"></div>
</v-card-text>
<v-card-actions>
<v-spacer />
<v-btn color="primary" depressed tile small #click="updateValue(1)">Yes</v-btn>
<v-btn color="primary" depressed tile small #click="updateValue(2)">No</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</div>
</template>
<script>
export default {
name: 'Cirrus-Alert-Confirmation',
props: {
title: {default: ''},
message: {default: ''},
icon_color: {default: 'while'},
icon: {default: 'warning'},
icon_show: {default: false}
},
data() {
return {
dialog_confirmation: false
}
},
mounted() {
},
methods: {
fire: function() {
this.dialog_confirmation = true;
},
updateValue: function(value) {
this.$emit('input', value);
this.dialog_confirmation = false;
}
},
watch: {
},
}
</script>
global.js
import Vue from 'vue'
import PopUpComponent from "../components/elements/PopUpComponent";
Vue.prototype.$filters = Vue.options.filters;
var cirrus_alert = Vue.extend(CirrusPopUpComponent);
Vue.prototype.$alert = cirrus_alert;
main.js
import '../../plugins/global';
import Vue from 'vue'
import FormTemplate from '../../components/modules/Templates/FormTemplate.vue';
new Vue({
el: '#app',
SuiVue,
vuetify,
store,
components : {
'main-template-component' : FormTemplate,
}
}).$mount('#app')
home.blade.php - short version (w/o the other declarations)
<main-template-component></main-template-component>
FormTemplate.vue (the function which triggers the alert)
let alert = new this.$alert({
propsData: { title: 'Sample' }
});
alert.$mount();
alert.fire({ title: 'Confirmation message'})
.then((result) => {
if(result.value == 1) console.log("option 1 is pressed")
});
Thank you heaps in advance.
I think vue mixins could help you through this problem. As it's docs represents :
Mixins are a flexible way to distribute reusable functionalities for Vue components. A mixin object can contain any component options. When a component uses a mixin, all options in the mixin will be “mixed” into the component’s own options.
You could create a mixin folder and inside it, you could have your reusable files like this alert and just import it wherever you want to use it like below :
<script>
import alert from '../../mixins/alert'
export default {
name: 'Component1',
mixins:[alert],
</script>

Update props in component through router-view in vue js

I want to pass a boolean value from one of my views in a router-view up to the root App.vue and then on to one of the components in the App.vue. I was able to do it but I am facing an error:
Avoid mutating a prop directly since the value will be overwritten whenever the parent component re-renders. Instead, use a data or computed property based on the prop's value. Prop being mutated: "drawer"
Below is my code:
Home.vue:
<template>
<div class="home" v-on:click="updateDrawer">
<img src="...">
</div>
</template>
<script>
export default {
name: "Home",
methods:{
updateDrawer:function(){
this.$emit('updateDrawer', true)
}
};
</script>
The above view is in the router-view and I am getting the value in the App.vue below:
<template>
<v-app class="">
<Navbar v-bind:drawer="drawer" />
<v-main class=" main-bg">
<main class="">
<router-view v-on:updateDrawer="changeDrawer($event)"></router-view>
</main>
</v-main>
</v-app>
</template>
<script>
import Navbar from '#/components/Navbar'
export default {
name: 'App',
components: {Navbar},
data() {
return {
drawer: false
}
},
methods:{
changeDrawer:function(drawz){
this.drawer = drawz;
}
},
};
</script>
I am sending the value of drawer by binding it in the navbar component.
Navbar.vue:
<template>
<nav>
<v-app-bar app fixed class="white">
<v-app-bar-nav-icon
class="black--text"
#click="drawer = !drawer"
></v-app-bar-nav-icon>
</v-app-bar>
<v-navigation-drawer
temporary
v-model="drawer"
>
...
</v-navigation-drawer>
</nav>
</template>
<script>
export default {
props:{
drawer:{
type: Boolean
}
},
};
</script>
This will work one time and then it will give me the above error. I appreciate if any one can explain what should I do and how to resolve this issue.
In Navbar.vue you are taking the property "drawer" and whenever someone clicks on "v-app-bar-nav-icon" you change drawer to be !drawer.
The problem here is that you are mutating (changing the value) of a property from the child side (Navbar.vue is the child).
That's not how Vue works, props are only used to pass data down from parent to child (App.vue is parent and Navbar.vue is child) and never the other way around.
The right approach here would be: every time, in Navbar.vue, that you want to change the value of "drawer" you should emit an event just like you do in Home.vue.
Then you can listen for that event in App.vue and change the drawer variable accordingly.
Example:
Navbar.vue
<v-app-bar-nav-icon
class="black--text"
#click="$emit('changedrawer', !drawer)"
></v-app-bar-nav-icon>
App.vue
<Navbar v-bind:drawer="drawer" #changedrawer="changeDrawer"/>
I originally missed the fact that you also used v-model="drawer" in Navbar.vue.
v-model also changes the value of drawer, which again is something we don't want.
We can solve this by splitting up the v-model into v-on:input and :value like so:
<v-navigation-drawer
temporary
:value="drawer"
#input="val => $emit('changedrawer', val)"
>
Some sort of central state management such as Vuex would also be great in this scenario ;)
What you could do, is to watch for changes on the drawer prop in Navbar.vue, like so:
<template>
<nav>
<v-app-bar app fixed class="white">
<v-app-bar-nav-icon
class="black--text"
#click="switchDrawer"
/>
</v-app-bar>
<v-navigation-drawer
temporary
v-model="navbarDrawer"
>
...
</v-navigation-drawer>
</nav>
</template>
<script>
export default {
data () {
return {
navbarDrawer: false
}
},
props: {
drawer: {
type: Boolean
}
},
watch: {
drawer (val) {
this.navbarDrawer = val
}
},
methods: {
switchDrawer () {
this.navbarDrawer = !this.navbarDrawer
}
}
}
</script>
Home.vue
<template>
<div class="home">
<v-btn #click="updateDrawer">Updrate drawer</v-btn>
</div>
</template>
<script>
export default {
name: 'Home',
data () {
return {
homeDrawer: false
}
},
methods: {
updateDrawer: function () {
this.homeDrawer = !this.homeDrawer
this.$emit('updateDrawer', this.homeDrawer)
}
}
}
</script>
App.vue
<template>
<v-app class="">
<Navbar :drawer="drawer" />
<v-main class="main-bg">
<main class="">
<router-view #updateDrawer="changeDrawer"></router-view>
</main>
</v-main>
</v-app>
</template>
<script>
import Navbar from '#/components/Navbar'
export default {
name: 'App',
components: { Navbar },
data () {
return {
drawer: false
}
},
methods: {
changeDrawer (newDrawer) {
this.drawer = newDrawer
}
}
}
</script>
This doesn't mutate the drawer prop, but does respond to changes.

List isn't dynamically displayed in vue.js

I'm new to vue.js and learning on my own with the vue doc, youtube videos and such. I've been searching for a while and looking at youtube tutorials and haven't found an answer so far, hope you guys will be able to help me.
So here's my issue, I'm building a web app and I need to display a list of objects dynamically but it doesn't show the first time I'm loading that page. I have to go to some other route and come back to see it, so I guess I'm misunderstanding some life cycle or something from that field of expertise...
I'm using the vuex to store and retrieve my data as seen below :
import Vue from 'vue';
const state = {
journees: {},
};
const getters = {
getJourneeList(state) {
return state.journees;
}
};
const mutations = {
GET_LIST(state, journees) {
state.journees = journees;
}
};
const actions = {
getJourneesUser({commit}) {
Vue.axios.get('/journee/')
.then( res => {
commit('GET_LIST', res.data)
})
.catch((err) => console.log(err))
}
};
export default {
state,
getters,
mutations,
actions
};
And then I'm getting it in my vue like this:
<template>
<v-container>
<v-card v-for="heure in heures" :key="heure._id">
<v-card-title>{{ heure }}</v-card-title>
</v-card>
</v-container>
</template>
<script>
export default {
name: "TimeList",
data() {
return {
heures: this.$store.getters.getJourneeList,
}
},
created() {
this.$store.dispatch('getJourneesUser');
}
}
</script>
You need to use mapState and use it inside computed value because then computed value will response to change in state. You do not need getter but if you want here is the version with getter. It should be like this if your store module called journees:
without getter
<template>
<v-container>
<v-card v-for="heure in journees" :key="heure._id">
<v-card-title>{{ heure }}</v-card-title>
</v-card>
</v-container>
</template>
<script>
import { mapState } from "vuex";
export default {
name: "TimeList",
computed: {
...mapState(["journees"])
},
created() {
this.$store.dispatch("getJourneesUser");
},
};
</script>
with getter
<template>
<v-container>
<v-card v-for="heure in getJourneeList" :key="heure._id">
<v-card-title>{{ heure }}</v-card-title>
</v-card>
</v-container>
</template>
<script>
import { mapGetters } from "vuex";
export default {
name: "TimeList",
computed: {
...mapGetters(["getJourneeList"])
},
created() {
this.$store.dispatch("getJourneesUser");
},
};
</script>

Bind drawer state in Vuetify when nav-drawer and app-bar is different components

now i have two components in my Dashboard:
Dashboard
<template>
<v-app>
<Toolbar :drawer="app.drawer"></Toolbar>
<Sidebar :drawer="app.drawer"></Sidebar>
</v-app>
</template>
<script>
import Sidebar from './components/layouts/Sidebar'
import Toolbar from './components/layouts/Toolbar'
import {eventBus} from "./main";
import './main'
export default {
components: {
Sidebar,
Toolbar,
},
data() {
return {
app: {
drawer: null
},
}
},
created() {
eventBus.$on('updateAppDrawer', () => {
this.app.drawer = !this.app.drawer;
});
},
}
</script>
Sidebar
<template>
<div>
<v-navigation-drawer class="app--drawer" app fixed
v-model="drawer"
:clipped="$vuetify.breakpoint.lgAndUp">
</v-navigation-drawer>
</div>
</template>
<script>
import {eventBus} from "../../main";
export default {
props: ['drawer'],
watch: {
drawer(newValue, oldValue) {
eventBus.$emit('updateAppDrawer');
}
},
}
</script>
Toolbar
<template>
<v-app-bar app>
<v-app-bar-nav-icon v-if="$vuetify.breakpoint.mdAndDown"
#click="updateAppDrawer">
</v-app-bar-nav-icon>
</v-app-bar>
</template>
<script>
import {eventBus} from "../../main";
export default {
props: ['drawer'],
methods: {
updateAppDrawer() {
eventBus.$emit('updateAppDrawer');
}
}
}
</script>
So now i have an infinite loop because when i press on icon in app-bar - watch in Sidebar, Sidebar understanding it like changes and starts a loop, updating drawer value in Dashboard, then Sidebar
catching changes in watch and starts new loop.
Also i have this warning, but it is another question.
[Vue warn]: Avoid mutating a prop
directly since the value will be overwritten whenever the parent
component re-renders. Instead, use a data or computed property based
on the prop's value. Prop being mutated: "drawer"
Thanks.
I have similar issue and able to solve by following this link
https://www.oipapio.com/question-33116
Dashboard
<template>
<v-app>
<Toolbar #toggle-drawer="$refs.drawer.drawer = !$refs.drawer.drawer"></Toolbar>
<Sidebar ref="drawer"></Sidebar>
</v-app>
</template>
Sidebar
<template>
<v-navigation-drawer class="app--drawer" app fixed v-model="drawer" clipped></v-navigation-drawer>
</template>
<script>
export default {
data: () => ({
drawer: true
})
}
</script>
Toolbar
<template>
<v-app-bar app>
<v-app-bar-nav-icon #click.stop="$emit('toggle-drawer')">
</v-app-bar-nav-icon>
</v-app-bar>
</template>

Multiple named components with different state

So I am trying to use multiple instances of a component inside a parent component. I am currently using a Vuetify.js tab-control to display each component.
For each child component I want to load different dynamic data from a external Rest API.
My preferd way to approach this is to pass a prop down to child's so that they can figure out what data to load. I have a vuex store for each type of child, but would like to reuse template for both, since the data is identical besides values.
Parent/Wrapper
<template>
<div>
<v-tabs fixed-tabs>
<v-tab>
PackagesType1
</v-tab>
<v-tab>
PackagesType2
</v-tab>
<v-tab-item>
<packages packageType="packageType1"/>
</v-tab-item>
<v-tab-item>
<packages packageType="packageType2"/>
</v-tab-item>
</v-tabs>
</div>
</template>
<script>
import Packages from './Packages'
export default {
components: {
Packages
}
}
</script>
Named child component
<template>
<v-container fluid>
<v-data-table v-if="packageType1"
:headers="headers"
:items="packageType1"
>
<div>
//ITEM TEMPLATE
</div
</v-data-table>
<v-data-table v-else-if="packagesType2"
:items="packagesType2"
>
<div>
//ITEM TEMPLATE
</div
</v-data-table>
</v-container>
</template>
<script>
import { mapGetters } from 'vuex'
export default {
data () {
return {
name: 'packages'
}
},
props: ['packageType'],
computed: {
...mapGetters('packageModule1', ['packageType1']),
...mapGetters('packageModule2', ['packagesType2'])
},
methods: {
getPackageByType () {
if (this.packageType === 'packageType1') {
this.getPackageType1()
return
}
this.getPackageType2()
},
getPackageType1 () {
this.$store.dispatch('chocoPackagesModule/getPackageType1')
.then(_ => {
this.isLoading = false
})
},
getPackageType2 () {
this.$store.dispatch('packagesType2Module/getPackageType2')
.then(_ => {
this.isLoading = false
})
}
},
created () {
this.getPackageByType()
}
}
</script>
Right now it renders only packageType1 when loading.
How can I easily differ those types without duplicating everything?
Should I just hard differ those types? Make separate child-components and role with it that way? I am new to this, but would love as much reuse as possible.

Categories

Resources