using Angular validation directives with Breeze blocks any input that is invalid
Asked Answered
P

4

10

If you add any of the angular directives for validation (ng-minlength, ng-maxlength, ng-pattern, etc.) to an input that is bound to a breeze entity it blocks any user input if found to be invalid.

If the value from ng-model is initially valid it shows up, but if you change the value to something invalid the input field is cleared, the model is set to null, and you can't input anything that may be initially invalid. However if you copy a valid value into the field it shows.

I would be fine with the fact that the model value is set to null when invalid if it didn't clear the input then prevent changes.

Also I have a feeling that whatever is causing this issue is also messing up ui-mask. The same thing happens there just without the angular validation directives.

here is a Plunker I found from a similar question that I modified to show my issue: http://plnkr.co/edit/dVsF7GFY65a30soLL5W8?p=preview


Edit

After many many hours of research I did find a solution that works although I am not sure of any ill side effects.

It has to do with how angular does validation in the first place by setting the $modelValue to 'undefined' if it fails any validators as it makes it's way through $parsers and $formatters.

I found this code in Angular (line 16331) that gets called by each angular validator:

function validate(ctrl, validatorName, validity, value){
  ctrl.$setValidity(validatorName, validity);
  return validity ? value : undefined;
}

I changed it to return 'value' instead of 'undefined':

function validate(ctrl, validatorName, validity, value){
      ctrl.$setValidity(validatorName, validity);

      return value;
    }

Angular still sets validation correctly. Although I am sure this isn't the best solution or even a good one.

I suspect the problem arises when Angular sets $modelValue to 'undefined' then Breeze sees that the model has changed and updates the entity which then updates the model which then clears the input and so forth... Or something like that...

I found this to be helpful in my quest. Maybe it will be helpful to one of you that knows much more than I https://github.com/angular/angular.js/issues/1412

Pippy answered 27/3, 2014 at 17:4 Comment(2)
Thanks for posting the question. I ran into same type of problem with Breeze blocking an ui-masked input. I bypassed the problem by using a temporary input model then coping it over to the entity.Flash
Well summed up - I have been stumped by this for a whileUtricle
D
3

Angular 1.3.0-rc.1 introduced the allowInvalid option for use with the ngModelOptions directive. It is essentially a formalization of the OP's hack at line 16331. The option instructs Angular to allow invalid form inputs to be written to $scope, and solves the problem neatly.

Usage:

<input type="email" ng-model-options="{allowInvalid: true}" ng-model="my_breeze_model.email"/>

See this feature request for more information: https://github.com/angular/angular.js/issues/8290.

Denice answered 14/11, 2014 at 0:0 Comment(1)
2 years later - is this still the best way of solving this issue (It does seem to work)Utricle
N
0

I'm happy to look at your plunker and see if there is something Breeze can do about this.

I'm not terribly surprised. Ng also struggles when you combine it with HTML 5 validation as I recall. You really should only use one scheme I think.

Do you disagree?

Also, have you considered the zValidate directive in the Breeze Labs breeze.directives.js? We think that is the best way to expose Breeze entity property validation errors in the view.

Norty answered 28/3, 2014 at 7:19 Comment(4)
Thanks for the response. I have only been programming a couple of months so honestly I haven't looked into which validation methods are best. I am sure you are right about using breeze exclusively for validation but that would require me to spend more time with C# and Entity Framework which I only learned enough of to set up my WebAPI and make it work happily with Breeze. I have my hands full with Javascript for now. Please see my edit for what I have found out so far on this issue. It was far too much to fit in this comment.Pippy
I was thinking about using the client side JavaScript validations (stock and custom), not server-side validation in C#. But now that you mention it, if the validations are important on the client, then they must be written on the server too ... for security reasons. You will get there in good time.Norty
I haven't tested it there. Do you have reason to believe not?Norty
Hi Ward, I ran into the problem described above - my input field uses ng-required and ng-minlength causes the input field to stop working as described. However, I have novalidate at form level which I thought was supposed to disable HTML 5 validation. When you said we should only use 1 validation scheme, isn't this how it is supposed to be done? ThanksUtricle
C
0

Another solution to consider is to use the ng-model-options attribute made available with Angular 1.3+.

In this way, you can prevent the Angular digest occurring after every keypress, and instead defer it to, for example, the 'blur' event so that the use has a chance to actually enter the valid data.

It would look like this:

<input type="email" ng-model="customer.email" ng-model-options="{ updateOn: 'blur' }">

However, this still has the limitation that if you enter invalid input, on blur the input will be cleared out and the use will have to enter it all again. Not very user friendly in my opinion, so I'll be trying out the breeze-only approach to circumvent this issue.

However, I thought this solution was also worth mentioning here.

Christopher answered 6/11, 2014 at 14:8 Comment(0)
M
0

https://docs.angularjs.org/error/ngModel/numfmt describes how Angular considers it a programming error, not a user input error, if programmatic model changes don't respect the input's validation rules.

If your model does not contain actual numbers then it is up to the application developer to use a directive that will do the conversion in the ngModel $formatters and $parsers pipeline.

Their example describes a String model value for an <input type='number'> but the same logic applies here, I think. If your input contains a minLength attribute, the scope should not get updated with strings that are too short.

So to fix it, add a custom directive to your input that pushes a custom parser into the $parsers pipeline.

For instance, the following directive will hold off writing the value of an <input type='text' minLength='4' min4> to the scope until a long enough string has been typed into it:

.directive('min4', function() {
    return {
      require: 'ngModel',
      link: function(scope, element, attrs, ngModel) {
        ngModel.$parsers.push(function(value) {
          return value && value.length >= 4 ? value : "";
        });
      }
    };
  });

This prevents the nasty interactions that otherwise occur when Breeze writes updated values back to the scope and ends up overwriting not-yet-legal state in the input.

See Plunker demo

Mainsheet answered 13/4, 2015 at 22:31 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.