Ember-Validations: How to handle asynchronous validation?


#1

Hi ember community,

I have been investigating Ember-Validations recently and have the built a jsbin to demonstrate the features.

See: http://emberjs.jsbin.com/iDeQABo/2/edit?output

This shows all the different levels when using Ember-Validations from basic required fields, to enforcing regex formats, more advanced things like custom validators, and then even more advanced things like remote validations and even a new proposal to simulate a similar effect of Ember-EasyForm.

Scenario 1: Performing live validation on data that can only be validated on the server. An example would be checking to see if the value a user typed in for a new username already exists. For this you must ask the server if this value is acceptable/unique. Problem 1: Based on the symptoms I’m seeing it appears ember-validations doesn’t continuously watch for changes on all the errors arrays. Because of this, the custom validator call method will return with an errors array that is not representative of the final result.

As you can see in the code comments from the jsbin, if we only push an error message to the array when the promise is resolved and server has determined the value is not acceptable, then the function has already returned and possibly with an empty errors array which incorrectly lets the form be a valid state.

In the other case, if you set a pendingw error message preemptively, assuming the form is invalid until proven otherwise this also fails. When the fulfillment handler clears the error, it still does not work because the call function has already returned the errors array hich was guaranteed to contain the pending error message. This means the form will never be valid which is worse.

Has anyone else created custom asynchronous validators? If so, how did you solve the issue i described above?

Now, I know there was intent to have remote validators in the repo but I didn’t see any examples so perhaps the creator had different plans for how these types of validators would be created. If not I think the following would work:

All validators could be implemented with nesting of promises using a combination of Deferred objects and Ember.RSVP.all. The over all idea is to keep the form invalid until every promise has resolved. In the case of local validators, these deferred objects can be resolved programmatically, and in the case of remote these will obviously be resolved naturally by the server. The major advantage i see is that this allows the core of the validation framework to work the same across both remote and local where as currently it looks like there would need to be some significant overhaul to support remote validators in a way as flexible as I’m imagining. This would also lead into lazy validators that dno’t recompute for every change, but only logical changes like when the user stops typing, wihch makes using live validation less resource intensive.

What do you think about the proposal above?

Scenario 2: You want to follow best UX practices of form validation and only show the errors after the user has been given a chance to fill out the data. This essential equates to only showing errors after focusOut instead of on initialization.

Problem 2: Yes, there is Ember-EasyForm, and yes you can write custom wrappers etc to get certain layouts, but in my opinion this too constrained. Almost always clients will want to change the form look and feel and the pre-work and understanding of wrappers required to make a simple template change is way to high.

When starting to solve his problem basic issues is that each input must have it’s own error state, but they all share the same controller which has the validation logic. The only way to allow them to have different states is to put these variables on the views. Given that background information there are two paths, one is to make custom views which is what Ember-EasyForms does and the other is by augmenting the existing views which is what I attmpted to do in the jsbin. Imo, the latter is a better alternative because it has lower barrier to entry. The whole reason handlebars, or rather, logicless templates in general are so popular is because they are still vary familiar to writing HTML so they are easily picked up but are also very empowering. I would like to keep the freedom and familiarity of using regular {{input}} helpers from ember but still have the ability to control when the validation is shown without tightly coupling to a set standard or feature set of a library.

As you can see on the bottom two fields, they have an extra parameter on the {{input}} called validateOn=focusOut. The parent view will look for these, then apply some extra properties to allow controlling when errors are shown.

This allows a more flexible, opt-in type of extension where you can still write forms just as you always did and then over time you can add ember-validations, and then add this extra flag to change the behavior of the error messages.

What do you think about this design for controlling the visibility of the error messages? Is is possible using supported Ember code?

There is a lot of hackery with hardcoded parentView and regex tricks to extract the property name from the valueBinding and dynamically applying computed properties which is technically unsupported but I assume someone more experienced in the ember world can determine if the idea is at least possible even if it’s not implemented in the way I have written.


Opinions asked: what is best way to validate/highlight Ember form elements?
#2

ember-validations is supposed to be asynchronous. However unfortunately it is actually not, as of this comment. If it were, you could return a promise from your validator and all would be good. (I’m doing it in my app, with a patched ember-validations).

See this (closed & not accepted) pull request for more information on the issue, including the patch I use to make asynchronous validation work in my app.

With my patch the validators included in ember-validations remain synchronous, but the settling of isValid becomes asynchronous. To make your uniqueness validator work asynchronously, you simply have to return a promise from the call method. It should update the errors array in a then/fail of the promise you are returning from call:

  call: function() {
    return this.save().then(...).fail(function() {
      this.errors.pushObject(your error);
    });
  }