Vue.js 2: Watch but not on initial data fetch - javascript

I'm new in the Vueniverse (using Vue.js 2) and I'm struggling with watch. On mounted, I call an API and set the radio button to the value I got from the API, so basically I have two radio buttons with values 1 and 0 (true/false).
I think the watcher works correctly, because it does trigger when the value is changed. However, I don't want it to trigger on the initial change - that's when I first set the value from the backend.
I've tried with different lifecycle hooks, such as beforeCreated, created and so on and it always triggers.
Probably it's something easy to do but I can't figure out how and don't find information on the Internet (might using the wrong keywords).
The code:
import axios from "axios";
export default {
name: 'Settings',
data: () => ({
/* set motionSensor to null initially */
motionSensor: null
}),
mounted() {
/* get the initial value from the backend, however this triggers the watcher */
axios
.get('http://localhost:8000/status.json')
.then(response => {
response.data['motionsensor'] ? this.motionSensor = "1" : this.motionSensor = "0";
})
},
watch: {
motionSensor: function(val) {
alert('Motion sensor value is now: ' + val)
}
}
}

Try to take advantage from the old value which is 2nd parameter of the watch handler :
watch: {
motionSensor: function(val, oldVal) {
if (oldVal !== null) {
alert('Motion sensor value is now: ' + val)
}
}
}

Related

React JS Updating item in State object to take effect immediately

React JS class component
I know there have been many posts on this subject but I can't seem to get this scenario to work.
Basically on my HandleClickSave event I want to update an item in my object in state without affecting the other values and then passing this updated oblect onto my service to get updated in the db.
The 'item' in question is the 'design' from the (unLayer) React-email-editor.
Problem is after the service is run in 'HandleClickSave' point 3 below, the receiving field 'DesignStructure' in the db is NULL every time. The other two fields are fine as these are saved to state object elsewhere.
Part of the problem is that the Email-Editor doesn't have an 'onChange' property which is where I would normally update the state. The other two values in the object are input texts and they do have an onChange which is how their state counterparts are updated.
This is the object 'NewsletterDesign':
{
"DesignId": "1",
"DesignName": "DesignLayout 1 Test",
"DesignStructure": null
}
In my React class component...
this.state = {
NewsletterDesign: {}
}
And the HandleClickSave event....
HandleClickSave () {
const { NewsletterDesign } = this.state
this.editor.saveDesign((design) => {
this.setState(prevState => ({
NewsletterDesign: {
...prevState.NewsletterDesign,
DesignStructure: design
}
}));
// Update to database passing in the object 'NewsletterDesign'. Field 'DesignStructure' in db is null every time, but other two fields are updated.
NewsletterService.UpdateCreateNewsletterDesign(NewsletterDesign)
etc....etc..
React's setState is not update immediately. read more here.
You can simply do it inside setState by
this.setState(prevState => {
const newState = {
NewsletterDesign: {
...prevState.NewsletterDesign,
DesignStructure: design
}
};
NewsletterService.UpdateCreateNewsletterDesign(newState.NewsletterDesign);
return newState;
});
The setState is an async operation. Meaning, that it's not guaranteed that the new state that you have updated will be accessible just after the state is updated. You can read more here
So in such cases, one of the way is to do the required operation first and then use the result at multiple places.
HandleClickSave () {
const { NewsletterDesign } = this.state
this.editor.saveDesign((design) => {
let newNewsletterDesign = { ...NewsletterDesign,
DesignStructure: design
};
this.setState(newNewsletterDesign);
NewsletterService.UpdateCreateNewsletterDesign(newNewsletterDesign)

Algolia vue-places: Clear input after selection

I'm using the vue-places component to render an Algolia places search input in Vue - it works brilliantly.
After a user selects / accepts a suggestion from the search dropdown, I want to clear the input and allow them to search again. Based on the standard example provided, I have tried to set form.country.label v-model value back to null in the change handler:
<template>
<places
v-model="form.country.label"
placeholder="Where are we going ?"
#change="selectLocation"
:options="options">
</places>
</template>
<script>
import Places from 'vue-places';
export default {
data() {
return {
options: {
appId: <YOUR_PLACES_APP_ID>,
apiKey: <YOUR_PLACES_API_KEY>,
countries: ['US'],
},
form: {
country: {
label: null,
data: {},
},
},
};
},
components: {
Places,
},
methods: {
selectLocation: function(event: any) {
if (event.name !== undefined) {
/**
* implementation not important
*/
this.form.country.label = null;
}
}
}
}
</script>
The selectLocation method fires as expected - but I cannot find any way to rest the input value to be empty.
How can I update a data value from a component method and have this reflected in a referencing component - in this case the Algolia places input?
The issue occurs because of how vue-places is proxying change events from the underlying implementation. When a change event is received, it broadcasts the same event and then updates the input value:
Places.vue:
this.placesAutocomplete.on('change', (e) => {
this.$emit('change', e.suggestion);
this.updateValue(e.suggestion.value);
});
This means that any attempt to set a value in our change handler will immediately be overridden.
My solution is to create a ref to the vue-places instance and then use the built-in Vue.nextTick to use the internal places.js setVal method after the call to updateValue:
methods: {
selectLocation: function(event: any) {
if (event.name !== undefined) {
// do something with the value
Vue.nextTick(() => {
// clear the input value on the next update
this.$refs.placesSearch.placesAutocomplete.setVal(null);
});
}
}
}

want to show updated status value in another component

i want to watch when a mutation called and updated a status. i make a component to show database table count when api called.
this is my store i wrote
const state = {
opportunity: ""
}
const getters = {
countOpportunity: state => state.opportunity
}
const actions = {
// count opportunity
async totalOpportunity({ commit }) {
const response = await axios.get(count_opportunity)
commit("setOpportunity", response.data)
},
}
const mutations = {
setOpportunity: (state, value) => (state.opportunity = value)
}
i want to show this getter value when this mutation called in another component name Opportunity.vue file.
i showed database count values in file name Dashboard.vue
i wrote it like this.
computed: {
...mapGetters(["countOpportunity"])
},
watch: {},
mounted() {
//do something after mounting vue instance
this.$store.watch(() => {
this.$store.getters.countOpportunity;
});
},
created() {
this.totalOpportunity();
},
methods: {
...mapActions(["totalOpportunity"])
}
and showed my view like this.
<div class="inner">
<h3>{{ countOpportunity }}</h3>
<p>Opportunities</p>
</div>
when api called and count increase shows my mutations. but my view value not updated (countOpportunity). any one can help me to fix this.
The issue here (most likely) is that the value of response.data is an object or an array. You've initially defined opportunity as '' which is not an observable object or array. You have 2 choices:
Redefine it as an empty object or array, depending on the response:
opportunity: [] // or {}
Otherwise, use Vue.set() to apply reactivity when changing it:
(Vue.set(state, 'opportunity', value))

Checkbox state does not change on click

In my Model.vue component (in a Nuxt.js and Vuetifyjs application) I have this piece of code:
<v-checkbox v-model="selected"></v-model>
In the script section of this component, I have:
computed: {
selected: {
get() {
return this.$store.state.selected
},
set(value) {
this.$store.commit('UPDATE_SELECTED', value)
}
},
},
In the store, I have this:
mutations: {
UPDATE_SELECTED: (state, value) => {
state.selected.push(value)
}
}
And the state of this store contains the selected entry as follows:
state: {
selected: []
}
AFAIK, I complied to the documentation, but when I click on the v-checkbox component, it does not check/uncheck. What is wrong?
The problem occurs because you're using an array for modeling the boolean state property named selected.
So, according to your code, at mutations side, on each mutation you're pushing a new boolean value in the latest position of an array named selected.
Also, at get side, the get() function of your computed property returns the entire array as property to be displayed, resulting in an unchecked checkbox at client side.
So, assuming you need to deal with multiple checkboxes, in order to make the current one working properly you could write it as follows:
Vuex Store
let store = new Vuex.Store({
state: {
selected: []
},
mutations: {
UPDATE_SELECTED: (state, value) => {
//mutating the 0th array position of the selected property
state.selected[0] = value
}
}
})
Computed property
computed: {
selected: {
get() {
//getting the 0-index element of the array, assuming this checkbox is the 0th
return this.$store.state.selected[0]
},
set(value) {
this.$store.commit('UPDATE_SELECTED', value)
}
}
}
Here you find a working fiddle.
Alternatively, if you need just to deal with a single checkbox in your application state, it'd be enough to model the state.selected property as a boolean. I mean, it would suffice to modify your Vuex state as follows:
state: {
selected: true //or false if you expect unchecked checkbox as default
}

Vue.js 2: Cant useTwo-way Computed Property in combination with vuex

I cant get Two-way Computed Property in combination with vuex to work.
If there are input changes I want to set getIsUnsavedData to true and "copy"/commit the changes into a new variable $store.state.authenticatedUser.changedData. After there is any change I want the input to get() its value from $store.state.authenticatedUser.changedData instead of $store.state.authenticatedUser.data to display the change.
At fist everything works like expected. If there are input changes, the changed value will be replicated in the $store.state.authenticatedUser.changedData property. The getIsUnsavedData value changes to true and the get() points to the replicated data.
There is only one bug left. Suddenly the computed property never changes although the vuex store is updating correctly. The set() function still gets called, but the get() doesn't .
<ui-textbox #change="valueChanged" v-model="userName"></ui-textbox>
// ...
computed: {
userName: {
get() {
return this.$store.getters.getIsUnsavedData ? this.$store.state.authenticatedUser.changedData.name : this.$store.state.authenticatedUser.data.name
},
set(value) {
this.$store.commit('setChangedUserData', {
key: 'name',
value: value
})
}
}
},
methods: {
valueChanged() {
this.$store.commit('setUnsavedState', true)
}
},
// ....
Try to use mine library
https://github.com/yarsky-tgz/vuex-dot
it's done for such situations, to make code footprint of setters/getters in your code much less.
<template>
<form>
<input v-model="name"/>
<input v-model="email"/>
</form>
</template>
<script>
import { takeState } from 'vuex-dot';
export default {
computed: {
...takeState('user')
.expose(['name', 'email'])
.dispatch('editUser')
.map()
}
}
</script>
(updated to reflect Andor's input)
v-model can point to a computed property but it needs to handle work a bit differently than when it just refers to data.
vue's doc
applying the section on two-way computed properties:
<ui-textbox v-model="userName"></ui-textbox>
// ...
computed: {
userName: {
get () {
return this.$store.state.obj.userName
},
set (value) {
this.$store.commit('updateUserName', value)
}
}
}
I might be missing something about what you're doing but this is how I'd start to solve the problem.

Categories

Resources