How to detect changes made in the edited field in Vue? - javascript

I have an object like this:
users: {
email: '',
password: ''
}
When user edits something, I want to detect the changes made in the key of the users object & save that key in another object(say editedFields)! How can I do this?
I tried using deep watchers like this:
watch: {
users: {
handler: function(val, oldVal) {
//
},
deep: true
}
}
But I am not sure how to get that edited key! Please help

I think this works:
watch: {
'users.email': function(newEmail, oldEmail) {
//
}
}

I think something like this might work?
data: function() {
return {
user: {
email: '',
password: ''
},
editedFields: []
}
},
watch: {
users: {
handler: function(val, oldVal) {
for (var key in val) {
if(val[key] != oldVal[key]) {
this.data.editedFields.push(key);
}
}
},
deep: true
}
}
But actually, there better way of handling this would be to create a new Vue component for each field. You can then test to see if its been updated in each component.

Related

Using XState, how can I access name of current state in an action?

I'm playing around learning XState and wanted to include an action in a machine that would just log the current state to console.
Defining a simple example machine like so, how would I go about this? Also note the questions in the comments in the code.
import { createMachine, interpret } from "xstate"
const sm = createMachine({
initial: 'foo',
states: {
foo: {
entry: 'logState', // Can I only reference an action by string?
// Or can I add arguments here somehow?
on: {
TOGGLE: {target: 'bar'}
}
},
bar: {
entry: 'logState',
on: {
TOGGLE: {target: 'foo'}
}
}
}
},
{
actions: {
logState(/* What arguments can go here? */) => {
// What do I do here?
}
}
});
I know that actions are called with context and event as arguments but I don't see a way to get the current state from either of those. Am I missing something here?
For a simple use case like yours, you could try recording the state on transition.
let currentState;
const service = interpret(machine).onTransition(state => {
if (state.value != currentState) {
// TODO: terminate timer if any and start a new one
currentState = state.value;
}
});
Then use the value in your actions.
See more here: https://github.com/statelyai/xstate/discussions/1294
Actions receive three arguments - context, event and meta. meta have property state, which is current state.
import { createMachine } from "xstate";
let metaDemo = createMachine(
{
id: "meta-demo",
initial: "ping",
states: {
ping: {
entry: ["logStateValues"],
after: { TIMEOUT: "pong" },
},
pong: {
entry: ["logStateValues"],
after: { TIMEOUT: "ping" },
},
},
},
{
delays: {
TIMEOUT: 3000,
},
actions: {
logStateValues(ctx, event, meta) {
if (meta.state.matches("ping")) {
console.log("It's PING!");
} else if (meta.state.matches("pong")) {
console.log("And now it's PONG");
} else {
console.log(
`This is not supposed to happen. State is: ${meta.state
.toStrings()
.join(".")}`
);
}
},
},
}
);

Have variable changes on button click but initially set using localStorage in vue

I am trying to setup a button that changes a data value in Vue but also have it set using localStorage initally. This way I can have it keep the previous state it was in before a page refresh. Below is the code I'm using and I'm able to get it to work but know that it would be preferable to use the computed section but haven't been able to get that to work properly.
Would anyone know what is going wrong?
My button is triggered using the testing method and the variable in question is isGrid.
export default {
data() {
return {
option: 'default',
}
},
components: {
FileUploader,
},
mixins: [
visibilitiesMixin,
settingsMixin
],
props: {
vehicleId: {
type: Number,
required: true,
default: null,
}
},
computed: {
...mapState([
'isLoading',
'images',
'fallbackImageChecks',
'selectedImages'
]),
isGrid: {
get() {
return localStorage.getItem('isGrid');
},
},
imagesVModel: {
get() {
return this.images;
},
set(images) {
this.setImages(images);
}
},
selectedImagesVModel: {
get() {
return this.selectedImages;
},
set(images) {
this.setSelectedImages(images);
}
},
removeBgEnabled() {
return this.setting('nexus_integration_removebg_enabled') === 'enabled';
},
},
mounted() {
this.loadImages(this.vehicleId);
},
methods: {
testing() {
if (this.isGrid === 'false' || this.isGrid === false) {
localStorage.setItem('isGrid', true);
this.isGrid = true;
console.log(this.isGrid);
console.log(localStorage.getItem('isGrid'));
} else {
localStorage.setItem('isGrid', false);
this.isGrid = false;
console.log('b');
console.log(this.isGrid);
console.log(localStorage.getItem('isGrid'));
}
},
}
I suggest you use vuex with vuex-persistedstate.
https://www.npmjs.com/package/vuex-persistedstate

How can use computed for v-model?

I got data from API using computed. "UserModule.userInfo.usrEmail" is the state in my vuex. Like below:
data() {
return {
vModel: {
email: {
value: "",
},
}
}
}
computed: {
email:{
get: function(){
return UserModule.userInfo ? UserModule.userInfo.usrEmail : "";
},
set : function(email){
this.vModel.email.value = email
}
},
}
And then show it to user like below:
<input v-model="email"></input>
User can edit email and also cancel their edit process and return to their previous data but in edit everything was correct but when i want to cancel this process my previous data did not show in the input and i saw my new data which is not correct i want to cancel it. This is my cancel method on input:
resetInput(input) {
this.vModel.email.value = this.email
},
"this.email" refer to my computed which is get data from API.
How can i write this cancel process correctly and see my previous data in input tag?
so you can use this solution:
data() {
return {
useGet :{
email: true,
},
}
}
in your method:
resetInput(input) {
this.useGet.email = true
},
and in your computed:
email: {
get: function () {
if (this.useGet.email) {
return UserModule.userInfo ? UserModule.userInfo.usrEmail : ""
}
return ""
},
set: function (email) {
this.useGet.email = false
}
},
this is because of your UserModule.userInfo.usrEmail.
this state does not update.
your get in computed will be work when your UserModule.userInfo.usrEmail changes.

How can I refactor repetitive conditional Vue.js code?

I have this code in my Vue.js component:
mounted() {
if (localStorage.dobDate) {
this.form.dobDate = localStorage.dobDate;
}
if (localStorage.dobMonth) {
this.form.dobMonth = localStorage.dobMonth;
}
if (localStorage.dobYear) {
this.form.dobYear = localStorage.dobYear;
}
},
watch: {
"form.dobDate": {
handler: function(after, before) {
localStorage.dobDate = after;
},
deep: true
},
"form.dobMonth": {
handler: function(after, before) {
localStorage.dobMonth = after;
},
deep: true
},
"form.dobYear": {
handler: function(after, before) {
localStorage.dobYear = after;
},
deep: true
}
Ask you can see it can get very repetitive, if for example I had a large form, and I don't want to do this for every field. Is there a way I can approach this to make it more DRY? Is there a way I can make it more dynamic for any field in a form for example?
In the mounted hook create an array of localStorage fields ["dobDate","dobMonth","dobYear"] and loop through it using forEach method, for each field localStorage[fieldName] check if it's defined using conditional operator, if it's defined assign it to the correspondant field name in the form data property else pass to the next element:
mounted(){
["dobDate","dobMonth","dobYear"].forEach(field=>{
localStorage[field]?this.form[field]=localStorage[field]:{};
})
}
In the watch property watch the form object deeply (watch its nested fields) then loop through its keys by doing the reciprocal operation made in mounted hook :
watch: {
form: {
handler: function(after, before) {
Object.keys(after).forEach(key=>{
localStorage[key]=after[key]
})
},
deep: true
}
}
Here is another approach with multiple (no deep) watchers.
data: {
form: {},
dateFields: ['dobDate', 'dobMonth', 'dobYear']
},
mounted() {
for (const dateField of this.dateFields) {
if (localStorage[dateField])
this.$set(this.form, dateField, localStorage[dateField])
}
},
created() {
for (const dateField of this.dateFields) {
this.$watch('form.' + dateField, function(after, before) {
localStorage[dateField] = after;
});
}
}
I ignore if it's more or less efficient than only one deep watcher. It may depends on the way your data change.
I'm sure you must have reasons for using localStorage for saving form data in localStorage, so with this code, you can pass the whole form object to localStorage and can retrieve that. in this case, any change in form would make this watch run
mounted() {
if (localStorage.form) {
this.form = localStorage.form
}
},
watch: {
"form": {
handler: function(after, before) {
localStorage.form = after;
},
deep: true
}
}

How to watch dynamic data fields in Vue.js

so i am trying to figure out whether this is possible, i am using a prop to dynamically name a field in data. So essentially i want to add a watcher to the dynamically added data field. Is this possible?
export default {
props: {
type: {
type: String,
required: true
}
},
data() {
return {
[this.type]: {
place_type: "house"
}
}
},
watch: {
"[this.type].place_type": function(val) {
console.log(val);
}
}
}
Here is my attempt, but it does not work with that syntax. Any ideas? Thanks

Categories