24

I was going through this answer on SO : Is there a proper way of resetting a component's initial data in vuejs?

However, the current method is not allowed now and VueJS prohibits changing the $data var.

As you can see here in this https://github.com/vuejs/vue/issues/2873 ( $data is not allowed to be modified. )

So if I try the above method, I am getting a VueJS warning:

[Vue warn]: Avoid replacing instance root $data. Use nested data properties instead.

Here is my JS code,

function initialState () {
        return {
            h2: 0,
            ch4: 0,
            c2h6: 0,
            c2h4: 0,
            c2h2: 0,
            c3h8: 0,
            c3h6: 0,
            co: 0,
            co2: 0,
            o2: 0,
            n2: 0,
            selected: ''
        }
    }
    export default {
        name: 'something',
        data () {
            return initialState() // This is working fine 
        },
        computed: {
            tdcg: function () {
                // some logic here...
            }
        },
        methods: {
            resetFields: function () {
                this.$data = initialState() // --> This is what I want to achieve!
            }
        }
    }

So what is the correct and the easiest way of re initialising my data?

Community
  • 1
  • 1
Sankalp Singha
  • 4,461
  • 5
  • 39
  • 58

5 Answers5

55

You can use Object.assign to iterate through all properties and assign them:

export default {
    data () {
        return {
            h2: 0,
            // other attributes...
        };
    },
    methods: {
        resetFields () {
            Object.assign(this.$data, this.$options.data.call(this));
        }
    }
}

Here's a demo fiddle: https://jsfiddle.net/797yyvtz/


Note: I'm using this.$options.data to call the original data method again to get a fresh copy of the data. No need for a separate initialState function. The data method is the initial state function.

Joseph Silber
  • 214,931
  • 59
  • 362
  • 292
  • 1
    Caution, this solution does not bind the context into `data`. So if you are using `this` into your `data` method you may want to apply the context with : `Object.assign(this.$data, this.$options.data.apply(this))` – Ifnot Jun 16 '17 at 10:26
  • This solution will apply the component context available during the `resetFields()` call, so context is available here. Or are you talking using `this` on `data()` a bad practice ? (as `this` is the vue instance you can use `$store` and other stuff here, wich is unavailable without a `apply(this)`) – Ifnot Jun 23 '17 at 10:08
  • Is there any way to remove the validation message on reset the form? – Sineth Lakshitha Sep 10 '21 at 13:17
3

Did you try iterating through the initialState object and setting it again? Here is the sample code:

function initialState() {
    return {
        h2: 0,
        ch4: 0,
        // and so on... finally
        selected: ''
    }
}

export default {
    name: 'something',
    data: function() {
        return initialState()
    },
    computed: {
        // ...
    },
    methods: {
        resetFields: function() {
            // Fetch the initialState object locally, so we do not have to call the function again
            let initialData = initialState();
            // Iterate through the props
            for (let prop in initialData) {
                // Reset the prop locally.
                this[prop] = initialData[prop];
            }
        }
    }
}

In my limited experiments locally, it does seem to work. Let me know your thoughts on this method.

Mani
  • 23,635
  • 6
  • 67
  • 54
  • Yes, it works flawlessly in my local testing. But it feels a bit weird to do `this[prop]`, as we are so used to seeing `this.prop` everywhere else. If we subscribe to the philosophy of "everything is an object in javascript", then it looks ok. – Mani Oct 31 '16 at 12:57
  • I know. This is working but it does not look like an optimal solution to me. The problem is all the tutorials that I have found on the web about Vue tends to use the 1.0 or earlier. And so many changes have been done in the 2.0. – Sankalp Singha Oct 31 '16 at 13:04
  • 1
    I agree. Lets wait for a better answer, maybe someone can point us to a new method which also *feels right*. Even I am not happy with this method, I would rather elaborately reset every single data property directly in my method, instead of iterating blindly through available props and using the weird `this[prop]` to set data. – Mani Oct 31 '16 at 13:08
  • This will not work if you are also watching to modify the model. It will trigger a response that may not give the model the initial state – Stefano Borini Apr 28 '17 at 09:01
2

You can try this:

  1. Initialize your form data properties with required fields. (As seen in STEP 1)
  2. Create another data field that will be used to clone the form data you want to reset (As seen in STEP 2).
  3. Clone the form data required (STEP 3)
  4. Write you reset method (STEP 4)
  5. Use any where you prefer (STEP 5)

export default {
  // Initialize your data
  data() {
    return {
      // Initialize the form field (STEP 1)
      formFields: {
        name: '',
        email: '',
        password: '',
        moreData: {
          field1: '',
          field2: [],
        },
      },
      // Create an object property used for cloning (STEP 2)
      formFieldsCopy: {},
    };
  },

  // When the DOM is mounted copy the
  // formField you want to a temporary field
  // You can use lodash ._clone or ES6 spread operator (STEP 3)
  mounted() {
    this.formFieldsCopy = { ...this.formFields
    };
  },
  methods: {
    // Write the function to reset the form (STEP 4)
    resetFormFields() {
      this.formFields = { ...this.formFieldsCopy
      };
    },
    submit() {
      // Do you normal Axios requests here
      // and call you reset function. (STEP 5).
      this.resetFormFields();
    },
  },
};
Ikechi
  • 33
  • 8
1

Wrap all the data into a dict with a key called "data" or other thing. Then you can re-initialize whole data by set this.data = {xx: yy}, or directly change one data item like this.data.h2 = 2.

function initialState () {
    return {
        h2: 0,
        ch4: 0,
        c2h6: 0,
        c2h4: 0,
        c2h2: 0,
        c3h8: 0,
        c3h6: 0,
        co: 0,
        co2: 0,
        o2: 0,
        n2: 0,
        selected: ''
    }
}
export default {
    name: 'something',
    data () {
        return {data: initialState()} // This is working fine 
    },
    computed: {
        tdcg: function () {
            // some logic here...
        }
    },
    methods: {
        resetFields: function () {
            this.data = initialState() // --> This is what I want to achieve!
        }
    }
}
  • Have you even read my question above? I cannot make head or tails out of your answer. Please read the question carefully before posting your answers. – Sankalp Singha Oct 31 '16 at 15:04
  • @SankalpSingha yes, i have read your question carefully. The code i paste is nested data properties way. The code change is very small, and you seems to didn't understand vue very well, so u didn't get the point. – seaify - Freelancer Oct 31 '16 at 15:40
  • 1
    @SankalpSingha check this jsfiddle example, https://jsfiddle.net/seaify/konv9h78/ , and try input and click reset button – seaify - Freelancer Oct 31 '16 at 15:43
1

My solution:

mounted(){
    this.saveData() // you can load if you need previous data
},
methods: {
    saveData(){
        localStorage.setItem(this.$options.name,JSON.stringify(this.$data));
    },
    loadData(){
        if (localStorage.getItem(this.$options.name) !== null) {
            let data= JSON.parse(localStorage.getItem(this.$options.name));
            Object.keys(data).forEach((key)=>{this[key] = data[key];});
        }
    }
}

When you need you can load or save them again

Mohsen
  • 4,049
  • 1
  • 31
  • 31