xpepermint / vue-rawmodel

RawModel.js plugin for Vue.js v2. Form validation has never been easier!

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Saving only changed and valid fields

michaelsorich opened this issue · comments

I am looking to use vue-contextable to facilitate saving of only the fields that are both changed and valid. To do this I will need to:

  1. have an efficient method of querying whether any fields in the model are both changed and valid (e.g. to disable save button in a similar way to using hasErrors()). I was hoping to add a version of isChanged() which includes evaluation of isValid() in addition to whether the value has changed from the initial value.
  2. to extend the commit method of the model and fields to only commit fields that are valid and to return the names and values of only the fields that have been commited in a form that can be dispatched via a rest api for syncing changes with the database backend.

I had considered extending the ReactiveModel and Field classes and base the code for the new methods on the existing methods implemented (isChanged and commit). However, it is not clear to me how I would indicate for vue-contextable to use these extended classes instead of the existing classes. The other alternative is to define these new methods as instanceMethods on the Schema – I can see how this is done, but it would seem more complex to implement due to the need to handle nested models.

If you have any suggestions on the best approach to implement this functionality, I would appreciate the advice.

I'm sure we can come up with an idea or two :). I'm not sure I completely understand your use case so I would be glad if you can provide more details on why and how. I'm sure you're not the only one with this request so let us continue with the discussion.

have an efficient method of querying whether any fields in the model are both changed and valid (e.g. to disable save button in a similar way to using hasErrors()). I was hoping to add a version of isChanged() which includes evaluation of isValid() in addition to whether the value has changed from the initial value.

This is already built into contextable.js and can be done like this:

let m = new MyModel();

if (m.isChanged() && m.isValid()) {
  // ...
}

These methods should be efficient enough because they just execute as many if checks as you have fields defined on your model schema.

to extend the commit method of the model and fields to only commit fields that are valid and to return the names and values of only the fields that have been commited in a form that can be dispatched via a rest api for syncing changes with the database backend.

I would need a bit more explanation for this part.

I had considered extending the ReactiveModel and Field classes and base the code for the new methods on the existing methods implemented (isChanged and commit). However, it is not clear to me how I would indicate for vue-contextable to use these extended classes instead of the existing classes. The other alternative is to define these new methods as instanceMethods on the Schema – I can see how this is done, but it would seem more complex to implement due to the need to handle nested models.

In Contextable everything is overridable. Contextable is actually an extended objectschema.js and the ReactiveModel is a good example of how to extend the library. And yes ... extending these classes is the recommended way.

Is that helpful? Are you looking for a way to PATCH data?

I will eventually have a rather large model and I intend to regularly autosave the model via a rest api. I intend to send only the fields and values that have changed since the last save which typically will only be a small fraction of the model (I could send a serialized version of the complete model and let the server sort it out, but I am hoping that I can do so on the client and only send the pertinent changes).

The presence of one or more fields that are currently invalid will not prevent saving. Currently I am intending that fields that are invalid will not be sent to the server for saving, but other valid changed fields will be sent (even though some fields are invalid).

Thus, “if (m.isChanged() && m.isValid())” isn’t quite what I wish as it will only be true if all fields are valid. As long as there is at least one field that is both changed and valid then the result should be true (i.e. there is data that can be sent to the server as a partial update/PATCH). I am happy to implement this as a new method on extended model and field classes so this is not too much of an issue if I can work out how to properly extend ReactiveModel and Field.

In vue-contextable, the construction of the ReactiveModel instance is not explicit. For example, I cannot see something like ‘let m = ReactiveModel(…)’ - if so it would be trivial to substitute my extended version of the ReactiveModel.

Based on the example, my understanding of the standard setup is:

  1. A contextable context object is made (const context = new Context())

  2. Then a schema is passed in which appears to create a contextable.Model instance (e.g. context.defineModel('User', new Schema(…)) which is then stored in the context object. It would not seem easy to replace Model with an extended version at this stage without rewriting context.defineModel and createModel.

It is possible to access the Model from the context but not to set the model which seems to make it difficult to replace it with an extended version at this stage (e.g. class Modelv2 extends context[‘Lease’]{…}; context[‘Lease’] = Modelv2).

  1. I presume that somewhere in the initialisation process (not sure where) the defineReactiveModel function from vue-contextable is called which retrieves the model from the context, extends it (to become a ReactiveModel), and then stores it in the vm variable. It is not clear to me how I would override the ReactiveModel definition (which is itself embedded in a defineReactiveModel function) in order to replace it with an extended version which would be called by the appropriate function in the course of initialization.

There is likely a much simpler way of extending the functionality of ReactiveModel and Field but I am not seeing it at the moment. Any hints would be appreciated.

Let's continue the discussion here: rawmodel/framework#4

I've upgraded underlying packages and I think you will now be able to do partial save/commit.

I was thinking about something like this (not tested but it should work):

new Schema({
  ...
  instanceMethods: {
    async $patch () {
      // validate fields
      await this.$validate({quiet: true});
      // create patch data object
      let data = this.filter(({path, field}) => {
        if (field.isChanged() && field.isValid()) {
          field.commit();
          return true;
        }
      });
      // send data to the server
      await fetch('/api', {method: 'POST', body: JSON.stringify(data)})
        .then((r) => r.json())
        .then((r) => this.$applyErrors(r.errors));
    }
  }
});

Please let me know if that works for you.