Vuex, vuelidate best practice

I have a model which I am storing in vuex. The form in question doesn’t have an explicit “Save” button, I just persist data back to the API (debounced). I was (am) struggling with the right way to do this when using validations as I don’t want to save bad data back into the store (let alone to the API, though that would of course do its own validations).

In the event this is what I ended up with (a horrible mixture of watch, validations etc.) I also, in the created hook, copy the getter value so that I can modify it.

While this (seems to) work just fine, I feel like I’m missing the right way to do things, so, I’d really appreciate any pointers.


<template>
  <div>
    <form>
      <div class="row workspace">
        <div class="col-xs-12">
          <label for="workspace">Workspace</label>
          <input id="workspace" size="30" v-model.trim="editable_workspace_value"
                 type="text">
          in. (Range 0.2–4.0)
        </div>
      </div>
    </form>
  </div>
</template>

<script>
import { between } from "vuelidate/lib/validators";

export default {
  name: "problem-form",
  created () {
    this.editable_workspace_value = this.problem.workspace_value;
  },

  data () {
    return {
      editable_workspace_value: ""
    };
  },

  props: ["problem"],

  validations: {
    editable_workspace_value: {
      between: between(0.2, 4)
    }
  },

  watch: {
    editable_workspace_value: function (value, _oldValue) {
      this.$v.editable_workspace_value.$touch();
      if (!this.$v.editable_workspace_value.$invalid) {
        this.$store.dispatch("UPDATE_PART_PROBLEM_FORMAT", {
          partId: this.problem.id,
          properties: {
            workspace_value: value
          }
        });
      }
    }
  }
};

So depends on business needs. Anecdotally, the best UI that flows with what people are expecting is to clone the data from props into an object that is your form, then have a save/cancel button that will either dispatch out or navigate/reload the form data. If you need more details on that please let me know what I could help clarify.

Right yip, makes sense. I think my client does want the “autosave” functionality rather than a button (I’d prefer a button) so, we’ll see.

re ^, after looking at this thread I’m assuming you do the copying in a watch method? I’ll have to have a play with it. My component is (currently) initialized like:

    <problem-form :problem="part"></problem-form>

Thanks heaps for the help!

Yeah, normally have something my data that does

data() {
  return {
    this.form = this.buildFormObject(val1, val2, etc);
  }
}

Then your buildFormObject can take care of cloning objects/arrays and any other weird data structures that end up being sent in as props.

Your vuelidate object will then be off of the form and you will be able to do things like check if the whole form is valid before updating off of that form key in vuelidate.

Your watch would get a little hairy b/c you will have to handle the scenario of someone submtting, then starting to type again and the result coming back from the old request being different than the current. Also handling failures or out of order requests can be painful (i.e. you are debouncing but say they type, pause,type, that sends 2 requests lets call them A and B, B rejects but a succeeds and they come back from the server in the order of B then A)

That network latency and promise resolution order is why the save button or saving on Blur is preferrrable, less edge cases.

Excellent point on the A & B ordering issue, I’ll have to have a think about that, but totally see how adding a save/blur would help massively there.

I admit I’m still confused by your

data() {
  return {
    this.form = this.buildFormObject(val1, val2, etc);
  }
}

Correct me if I’m wrong (which I imagine I am), but, that data method is only going to be called once, at least, it’s possible to have props change but not have the data method “reinitialized” (for want of a better word). I’m curious as to how you get away from having to use a watch. From the link above If you decide to manually copy them into data once during mounting that would not get updated as vue obviously can't track that. . It looks like that’s what your snippet is doing (copying on mount), but, I suspect I’m missing something. If I’m not making sense, I can try to make a jsfiddle to reproduce what I’m talking about.

No, you are correct, this is only a way to segregate in memory vs persisted. If your component will continue to live after the update you will have to handle this via a watch.

1 Like