nested property inside watch event - vue.js

I know my question maybe a duplicate but i cant find the solultion of my problem anywhere
I have an object with some key/values and i want to watch for the changes in all properties,keeping in mind all properties must have separate events here my object
data: () => ({
filters: {
All: false,
AllUSA: false,
AllSouthAmerica: false,
AllAsia: false,
AllEurope: false
}
}),
watch
watch: {
"filters.All": function(val) {
if (val) {
this.TrafficSource = Object.assign(...Object.keys(this.TrafficSource).map(k => ({
[k]: true
})));
this.filters = Object.assign(...Object.keys(this.filters).map(k => ({
[k]: true
})));
} else {
this.TrafficSource = Object.assign(...Object.keys(this.TrafficSource).map(k => ({
[k]: false
})));
this.filters = Object.assign(...Object.keys(this.filters).map(k => ({
[k]: false
})));
}
},
"filters.AllUSA": function(val) {
alert("both event being called")
this.TrafficSource = Object.assign(...Object.keys(this.TrafficSource).map(k => ({
[k]: false
})));
this.filters = Object.assign(...Object.keys(this.filters).map(k => ({
[k]: false
})));
if (val) {
this.TrafficSource.Virginia = true;
this.TrafficSource.California = true;
this.TrafficSource.Oregon = true;
} else {
this.TrafficSource.Virginia = false;
this.TrafficSource.California = false;
this.TrafficSource.Oregon = false;
}
},
deep: true
}
the problem right now is both the watch events are called event though filters.All was invoked, what am i doing wrong?

you can use a watcher with options on filters like this:
watch: {
filters: {
immediate: true,
deep: true,
handler(newValue, oldValue) {
// you can write code to tell you which property is changed since you have
access to the old and new value here
}
},
};
this kind of watcher runs immediately when your component loads and then since it has deep: true it will trigger if any property in the object its watching gets changed.
now for the code to track what actually changed you can write something like this:
// all of this code should be done in the watcher's handler
const newVals = Object.values(newValue);
const oldVals = Object.values(oldValue);
const changedVal = [];
newVals.forEach((x, i) => {
if (x !== oldVals[i]) {
const result = Object.entries(newValue).filter(y => y[1] === x).flat();
changedVal.push(result[0]);
}
});
now you have all the changed keys in the changedVal array in the handler and you can continue with your logic in the handler
Edit: also i don't think changedVal should be an array, you can set it to the changed key and write a switch...case based on that

Related

How to stop useAsyncState from running immediately?

I am trying to use the useAsyncState() from the VueUse library.
I am using the { immediate: false } option flag because I do NOT want it to run immediately.
But the code always gets run immediately, and I don't know why.
Here is my code:
import { useAsyncState } from '#vueuse/core';
const firstNameSubmit = useAsyncState(
new Promise((resolve) => {
setTimeout(() => {
console.log('Bob');
resolve();
}, 2000);
}),
undefined,
{ immediate: false }
);
const handleFirstNameSubmit = () => {
firstNameSubmit.execute();
};
And here is a reproduction: https://stackblitz.com/edit/vitejs-vite-ylzoyt/?file=src%2FApp.vue
I also tried doing the opposite and using { immediate: true }, along with excluding the options object altogether, but none of those solutions work.
The promise inside useAsyncState always runs immediately.
Once a promise is created, it cannot be postponed. In order to avoid this, it should be wrapped with a function that can be explicitly called.
It should be:
const firstNameSubmit = useAsyncState(
() => ...,
undefined,
{ immediate: false }
);
...
firstNameSubmit.execute();

Vue 3 ref access always returns undefined

Ok. so I do have this image object I get from my DB and store it into a vuex store:
// State
const state = {
userImages: [],
};
// Getters
const getters = {
getImages: (state) => {
return state.userImages;
},
getFilePathExists: (state) => (fullPath) => {
return !!state.userImages.find((item) => item.fullPath === fullPath);
},
getFileNameExists: (state) => (name) => {
return !!state.userImages.find((item) => item.name === name);
},
getImagesInFolder: (state) => (folder) => {
if(folder) {
return state.userImages.filter(im => {
if(im.folders) {
return im.folders.includes(folder)
}
return false
})
}
return state.userImages
}
};
// Mutations
const mutations = {
setImages(state, val) {
state.userImages = val;
},
};
export default {
state,
getters,
// actions,
mutations,
};
So far so good. Now if I use the '''getImages''' getter, the Object gets to be loaded into a ref:
//...
const detailImage = shallowRef({});
const showImageDetails = (image) => {
if (!showDetails.value) {
showDetails.value = true;
}
activeImage.value = image.id
detailImage.value = image;
}
The JSON has a nested Object called exif_data. If I onsole.log this, I get: Proxy {Make: 'Apple', Flash: 'Flash did not fire. No strobe return detection fun…on present. No red-eye reduction mode or unknown.', Model: 'iPhone 6', FNumber: 2.2, COMPUTED: {…}, …}.
So far so good, but when I would like to access any property of the nested Object, either with or without using .value I just get undefined.
Any ideas?

swimlane/ngx-datatable, How can I kick the cellClass function?

The cellClass function is not called when the component properties change.
How do I kick a rowClass orcellClass?
#Component({
...,
template: `<ngx-datatable [rowClass]="rowClass"></ngx-datatable>`
})
class SomeComponent {
someVariable = true;
rowClass = (row) => {
return {
'some-class': (() => { return this.someVariable === row.someVariable })()
};
}
}
Related
https://github.com/swimlane/ngx-datatable/issues/774
I was able to solve it by changing this.rows.
https://swimlane.gitbook.io/ngx-datatable/cd
this.rows = [...this.rows];
If you are using a store, you need to cancel the immutable attribute.
Example
#Input() set list(list: Record<string, unknown>[]) {
if (list.length) {
// If the search results are reflected in the table.
// And 20 items are loaded at a time.
if (list.length === 20) {
this.rows = list.map((item) => ({ ...item }));
// Load more items
} else {
const newRows = list.map((item) => ({ ...item })).slice(this.rows.length);
this.rows = this.rows.concat(newRows);
}
}
}

Why my vue js data member is not getting updated?

Data part
data () {
return {
containsAd: true
}
},
Method that manipulates the data member containsAd
updated () {
let _this = this
window.googletag.pubads().addEventListener('slotRenderEnded', function (event) {
if (event.slot.getSlotElementId() === 'div-gpt-ad-nativead1') {
_this.containsAd = !event.isEmpty // this is false
console.log('Ad Exists? ', _this.containsAd)
}
})
},
Just to check if the value has changed or not.
check () {
let _this = this
setTimeout(function () {
console.log('Current Value', _this.containsAd)
}, 5000)
}
Resulting Output
I think doing the event listener in the mounted hook will sort your issue.
data() {
return {
containsAd: true
};
},
mounted() {
window.googletag.pubads().addEventListener('slotRenderEnded', event => {
if (event.slot.getSlotElementId() === 'div-gpt-ad-nativead1') {
this.containsAd = ! event.isEmpty // this is false
console.log('Ad Exists?', this.containsAd);
}
});
}
Also using es6 shorthand function will avoid you having to set _this.

Computed property not updating on props changes

I can't get a computed property to update, when a nested property in a passed prop object is changed.
this.favourite is passed via props, but the computed property is not updating when this.favourite.selectedChoices.second.id and this.favourite.selectedChoices.first.id is changed.
Any ideas of how to make this reactive?
Here's the computed property:
isDisabled() {
const hasMultipleChoices = this.favourite.choices.length
? this.favourite.choices[0].value.some(value => value.choices.length) :
false;
if (hasMultipleChoices && !this.favourite.selectedChoices.second.id) {
return true;
} else if (this.favourite.choices.length && !this.favourite.selectedChoices.first.id) {
return true;
}
return false;
}
TESTED
In my test.vue
props: {
variant: {
type: String,
default: ''
}
}
const myComputedName = computed(() => {
return {
'yellow--warning': props.variant === 'yellow',
'red--warning': props.variant === 'red',
}
})
test.spec.js
import { shallowMount } from '#vue/test-utils'
import test from '#/components/test.vue'
let wrapper
//default values
function createConfig(overrides) {
let variant = ''
const propsData = { variant }
return Object.assign({ propsData }, overrides)
}
//test
describe('test.vue Implementation Test', () => {
let wrapper
// TEARDOWN - run after to each unit test
afterEach(() => {
wrapper.destroy()
})
it('computed return red if prop variant is red', async (done) => {
const config = createConfig({ propsData: { variant: 'red' } })
wrapper = shallowMount(test, config)
wrapper.vm.$nextTick(() => {
//checking that my computed has changed, in my case I want to matchanObject
expect(wrapper.vm.myComputedName).toMatchObject({
'red--warning': true
})
//check what your computed value looks like
console.log(wrapper.vm.myComputedName)
done()
})
})
//TEST 2 Variant, this time instead red, lets say yellow
it('computed return yellow if prop variant is red', async (done) => {
const config = createConfig({ propsData: { variant: 'yellow' } })
wrapper = shallowMount(test, config)
wrapper.vm.$nextTick(() => {
//checking that my computed has changed, in my case I want to matchanObject
expect(wrapper.vm.myComputedName).toMatchObject({
'yellow--warning': true
})
//check what your computed value looks like
console.log(wrapper.vm.myComputedName)
done()
})
})
})
for more info, this page helped me.
https://vuejsdevelopers.com/2019/08/26/vue-what-to-unit-test-components/
The reason why the computed property didn't update, was because I created the id object of both this.favourite.selectedChoices.second and this.favourite.selectedChoices.first, after the component was rendered. Declaring the id objects before render was the solution.