merge all scroll handlers to one from all components - vue.js

I'm using Vue CLI and I have these components that has a scroll event on mounted property
comp1.vue
export default{
data(){
showSearchBar : 0
},
mounted(){
const _self = this;
document.querySelectorAll('.page__content').forEach(function(el){
el.onscroll = function() {
if (el.scrollTop >= 200) {
_self.showSearchBar = 1;
}else{
_self.showSearchBar = 0;
}
}
});
}
}
comp2.vue
export default{
data(){
showUsersList : 0
},
mounted(){
const _self = this;
document.querySelectorAll('.page__content').forEach(function(el){
el.onscroll = function() {
if (el.scrollTop >= 200) {
_self.showUsersList = 1;
}else{
_self.showUsersList = 0;
}
}
});
}
}
I know only one event declaration will work if so happens I have multiple scroll handlers on single page, then I have to group them down to a single scroll event so my question is, how do I merge those handlers into one scroll event so to make all of them work? is there a way on this in Vue?

One of the ways you could do this, is by including the logic inside your app.vue, where you also load your <router-view> tag:
We then pass the prop down to the individual pages, where we can use it:
App.vue
<template>
<div class="page__content">
<router-view :scrolled="scrolled"/>
</div>
</template>
<script>
export default{
data(){
scrolled: false
},
mounted(){
// Its better use a vue ref here
document.querySelectorAll('.page__content').forEach((el) => {
el.onscroll = () => {
if (el.scrollTop >= 200) {
this.scrolled = true;
} else {
this.scrolled = false;
}
}
});
}
}
</script>
comp2.vue
export default{
props: {
scrolled: {
type: Boolean,
required: true,
},
},
computed: {
showUsersList() {
if(this.scrolled) {
return 1;
} else {
return 0;
}
},
},
mounted(){
}
}

Related

How to forceUpdate sibling component in VueJS

I have created a component which have two child components AddContactList and ViewContactLists, Here I need to forceUpdate ViewContactList when new entry inserted from AddContactList component
This is AddContactList components script
<script>
export default {
data() {
return {
fields: {},
errors: {},
success: false,
loaded: true,
}
},
methods: {
submit() {
if (this.loaded) {
this.loaded = false;
this.success = false;
this.errors = {};
console.log('Loading..');
axios.post('/submit', this.fields).then(response => {
this.fields = {}; //Clear input fields.
this.loaded = true;
this.success = true;
console.log('done..');
// --Here I need to update ViewContactList component
}).catch(error => {
this.loaded = true;
if (error.response.status === 422) {
console.log(error.response.data.errors)
this.errors = error.response.data.errors || {};
}
});
}
},
},
}
</script>
This is ViewContactList components script
<script>
import pagination from 'laravel-vue-pagination'
export default {
name:"ContactList",
components:{
pagination
},
data(){
return {
ContactList:{
type:Object,
default:null
}
}
},
mounted(){
this.list()
},
methods:{
async list(page=1){
await axios.get(`/getContactLists?page=${page}`).then(({data})=>{
this.ContactList = data
}).catch(({ response })=>{
console.error(response)
})
}
}
}
</script>
You can simply achieve this by emitting an event on successful save to the parent and then from parent component you can invoke the contact list component method with the help of ref.
Live demo :
Vue.component('childone', {
props: ['childmsg', 'childOneRef'],
template: `<p>{{ childmsg }} <button #click="$emit('savesuccess')">Add Contact</button></p>`
});
Vue.component('childtwo', {
props: ['childmsg', 'childoneref'],
template: '<p>{{ childmsg }}</p>',
methods: {
getupdtedList() {
console.log('Contact List call');
}
}
});
var app = new Vue({
el: '#app',
methods: {
callViewContactListCompMethod() {
this.$refs.contactListRef.getupdtedList();
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<childone #savesuccess="callViewContactListCompMethod" childmsg="This is a child 1 message">
</childone>
<childtwo ref="contactListRef" childmsg="This is a child 2 message">
</childtwo>
</div>
If your parent component is ContactComponent
ContactComponent
|--AddContactList
|--ViewContactList
When you insert contact in add contact list emit the #addList event to the parent component.
Then pass the contact list as a props to ViewContactList.
when the props is changed, the ViewContactList component will be re-rendered automatically.

How to react data changes in vue component?

I am trying to build a component that should update the string using setInterval.
Here is the code of the component.
<script>
export default {
data() {
return {
offers: [
'1!',
'2!',
'3!',
],
current_index: 0,
indexInterval: null,
}
},
created() {
this.updateToastIndex()
},
beforeDestroy() {
clearInterval(this.indexInterval)
},
methods: {
updateToastIndex() {
const indexInterval = setInterval(() => {
this.$nextTick(() => {
if (this.current_index === 2) {
this.current_index = 0
console.log(this.offers[this.current_index], 'changedprev')
} else {
this.current_index = this.current_index + 1
console.log(this.offers[this.current_index], 'changed')
}
})
}, 1000)
this.indexInterval = indexInterval
},
},
}
</script>
<template>
<div>{{ offers[current_index] }}</div>
</template>
I can see the current_index in updateToastIndex function getting updated but current_index in template is not getting updated.
Please suggest some solution

How to monitor the data changes of the parent component in the child component

When the data of the parent component changes, how to realize real-time monitoring and respond in the child component.
You can watch props when props changes:
<div id="app">
<childcomponent :myprop="text"></childcomponent>
<button #click="text = 'New text'">Change text</button>
</div>
Watch for changes in ChildComponent using watch method!
new Vue({
el: '#app',
data: {
text: 'This is Text'
},
components: {
'childcomponent' : {
template: `<p>{{ myprop }}</p>`,
props: ['myprop'],
watch: {
myprop: function(newValue, oldValue) {
console.log('Prop changed and new value is: ', newVal, ' | old value: ', oldValue);
}
}
}
}
});
https://v2.vuejs.org/v2/api/#watch
props: ["opType", "editData"],
watch: {
editData: {
immediate: true,
handler(newVal, oldVal) {
if (this.editData.pryType === "MOBN") {
this.infoIndex = 0;
} else if (this.editData.pryType === "EMAL") {
this.infoIndex = 1;
} else if (this.editData.pryType === "SVID") {
this.infoIndex = 2;
} else if (this.editData.pryType === "BBIN") {
this.infoIndex = 3;
if (this.getListSeconed) {
this.getkList();
this.getListSeconed = false;
}
}
},
deep: true
}
},
Can try like this.

Why does Vuetify Autocomplete not selecting the data being set?

I use Vuetify autocomplete as a reusable component to display a list of jobs in key/value pairs. In creating record, the component works fine but when editing where data should be filled in, the data has the value but not showing on the component.
JobDropdownSelector.vue:
................................................................................................................................................
<template>
<v-autocomplete
v-model="job"
label="Job Designation"
item-value="id"
item-text="name"
return-object
:items="jobs"
#change="onChange"
>
</v-autocomplete>
</template>
<script>
export default {
props: {
selectedJob: {
type: Object,
default: null
}
},
data() {
return {
jobs: [],
job: null
};
},
methods: {
getDataFromApi() {
axios.get("api/jobs")
.then(response => {
this.jobs = response.data.data;
})
.catch(error => {
console.log(error);
reject();
});
},
onChange(e) {
this.job = e;
this.$emit("onChange", e);
}
},
watch: {
selectedJob: {
deep: true,
immediate: true,
handler(newValue, oldValue) {
this.job = newValue;
}
}
},
mounted() {
this.getDataFromApi();
}
};
</script>
EditForm.vue:
................................................................................................................................................
<template>
<div>
<JobDropdownSelector
v-model="job"
:selectedJob="job"
#onChange="onChangeJob"
>
</JobDropdownSelector>
</div>
</template>
<script>
import JobDropdownSelector from "../components/JobDropdownSelector";
export default {
components: {
JobDropdownSelector
},
data() {
return {
job: null
};
},
methods: {
onChangeJob(e) {
this.job = e;
},
getInitialJob() {
axios.get("api/jobs/22").then(response => {
this.job = response.data.data;
});
}
},
mounted() {
this.getInitialJob();
}
};
</script>
Display
Console
It looks like you overriding job twice: on the onChangeJob method and via v-model on JobDropdownSelector.
You can try use input event to update modek instead of emitting additional event.
JobDropdownSelector.vue
methods: {
onChange(e) {
// You don't need set job because it's connected via v-model.
// this.job = e;
this.$emit("input", e);
}
}
This will cause that job in EditForm.vue should update automatically after dropdown change.

setInterval() in Vue makes my this-variable undefined

Helloo, so I'm making a basic clock in Vue and for some reason with this code I get the initial time value into bazinga, but once it updates it states that it is undefined. I managed to "make it work" if I define now and other variables inside the setInterval() function as well, but this does not seem like a good choice. How come this.bazinga becomes undefined after the first iteration?
<template>
<div id="clock">
<h2>The time is now</h2>
<p>{{ bazinga }}</p>
</div>
</template>
<style>
</style>
<script>
export default {
data(){
const now = new Date()
const hours = now.getHours()
const minutes = now.getMinutes()
const seconds = now.getSeconds()
return{
bazinga: now
}
},
created() {
setInterval(() => {
console.log(this.bazinga)
this.bazinga = this.now
console.log(this.bazinga)
}, 1000)
},
destroyed() {
clearInterval(this.now)
}
}
</script>
All your const variables inside the data property are not registered as component's data, they are just variables accessible in the data block.
I assume you want to get an updated new Date() everytime you call this.now?
I'd suggest something like that:
<script>
export default {
data(){
return{
now: new Date();
}
},
computed {
hours: function() {
return this.now.getHours()
}
minutes: function() {
return this.now.getMinutes()
},
seconds: function() {
return this.now.getSeconds()
}
},
created() {
setInterval(() => {
console.log(this.bazinga)
this.updateNow()
console.log(this.bazinga)
}, 1000)
},
updateNow() {
this.now = new Date()
}
destroyed() {
clearInterval(this.now)
}
}
</script>
Every call to updateNow() will get a new Date, and update all the computed as well. :)
<template>
<div id="clock">
The Time is Now {{bazinga}}
</div>
</template>
<script>
export default {
data(){
return {
hours: 0,
minutes: 0,
seconds: 0,
hoursDisplay: 0,
secondIterator: 1000,
hourTime: 12,
}
},
mounted() {
this.$options.timer = window.setTimeout(this.updateDateTime, this.secondIterator);
},
destroyed() {
window.clearTimeout(this.$options.timer);
},
methods: {
updateDateTime() {
const now = new Date();
this.hours = now.getHours();
this.minutes = this.zeroPad(now.getMinutes());
this.seconds = this.zeroPad(now.getSeconds());
this.hoursDisplay = this.hours % this.hourTime || this.hours;
this.$options.timer = window.setTimeout(this.updateDateTime, this.secondIterator);
},
zeroPad: function (time) {
return (parseInt(time, 10) >= 10 ? '' : '0') + time;
}
},
computed: {
amPm: function () {
if (this.hours) {
return this.hours >= 12 ? 'PM' : 'AM';
}
},
bazinga: function () {
if (this.amPm) {
return [this.hoursDisplay, this.minutes, this.seconds].join(':') + ' ' + this.amPm;
}
},
}
}
</script>
I made a clock component here, but basically you always want to use data props inside your components and access them with this.someproperty. You can also use watchers or computed properties to return them manipulated like i have done here.