Angular Reactive forms Validate a control from directive - javascript

I have a number input in an Angular Reactive form, I got a directive to limit the number of decimals, and that are working fine, I am looking to implement auto-correct as part of that directive, which is also kind of working but not the validation.
<input type="number" formControlName="TestPercentage" id="TestPercentage" max="100" step="0.25" numeric [decimals]="2"/>
ts file
this.form.addControl(formConstants.markupPercentage, new FormControl('', [Validators.min(0), Validators.max(100)]);
I am using the decimal number directive from https://gist.github.com/ahmeti/5ca97ec41f6a48ef699ee6606560d1f7 and the part where I am changing value
if (Number(currentValue) > Number(this.el.nativeElement.max)) {
this.el.nativeElement.value = parseFloat(this.el.nativeElement.max).toFixed(this.decimals);
this.el.nativeElement.updateValueAndValidity();
}
this one is making the value to max value but not removing the invalid class/ not revalidating or the new value assigned in the directive is not getting validated.
How can I trigger the validation once I changed/ corrected the element value?

The way I resolved this issue is by passing the abstract control to the directive and setting value using setvalue method, which resolved the issue.

Related

Angular 2 - Binding a form control value inside a directive

I am a newbie in angular 2 here. Just want to ask if how can I bind the [ngFormControl] value inside a directive? I tried using#HostBinding('value') get value {return this.el.value} but it just updates the nativeElement's value, not the [ngFormControl]'s value.
I am creating a number only directive wherein it accepts a configuration on what type of number to accept (e.g negative or positive, with or without decimal). If negative is allowed, I will allow the dash symbol (-) to be inputted. But when the user only inputs a dash and no tailing numbers, once the input loses its focus (blur event fires), I updated the nativeElement's value with empty string. The view is blank but the [ngFormControl] still has the value of '-'.
Here is my code for blur event:
#HostListener('blur', ['$event']) onBlur($event){
if(isNaN(this.el.value)){
this.el.value = '';
}
}
What I want to accomplish is for the directive to edit or modify or update the value of the [ngFormControl] from where the directive is attached to.
Thank you! =)

Angular: Cannot retrieve value of ng-model when ng-minlength is not satisifed

Given:
<input type="password" ng-model="ctrl.fields.password" ng-minlength="8" />
{{ ctrl.fields.password.length }}
Nothing shows up until you've met the minimum length as required by the validation. That is, if the length of the password is less than 8, then the value of the model is null. Removing ng-minlength solves that problem.
This is quite unexpected behavior, as the validity of the field should have no bearing on the access to the model's value.
Is there a way to work around this in Angular 1.5? I want to show the user the length of their password as they type, but since the value is null until it's at least 8 characters, then I can't.
This is the way Angular $validators work: if validatiion fails, value is not passed from view to model, and model is reset.
Here's a good introduction to how Angular built-in validation works:
http://blog.thoughtram.io/angularjs/2015/01/11/exploring-angular-1.3-validators-pipeline.html
If you don't like this, angular-ui-validate is a good package that works in a non-disruptive way (i.e. model is always updated and you decide on what to do with the validation result).

Binding 2 inputs in Angularjs issue

I have 2 inputs.
<input type="number" ng-model="samount" ng-change="getSR(samount)">
<input type="number" ng-model="ramount" ng-change="getRS(ramount)">
in controller
$scope.getSR = function(amount) {
$scope.ramount = parseFloat((amount*5.25).toFixed(3));
};
$scope.getRS = function(amount) {
$scope.samount = parseFloat((amount/5.25).toFixed(3));
};
I enter some data in input 1 and do some calculations and update value of input 2 with function in controller. This works, but when I enter data in input 2 reverse calculate and update input 1. For some reason, input 1 value is not updating. I used $scope.$watch to see if input 1 is updated and it is updating in $watch, but not in view.
If I reload and start with input 2 it works, input 1 updates, but typing input 1 doesn't update again. they are in same scope but ng-change is different functions.
$scope.$apply() in each function gives error apply already in progress.
Is there way to fix this issue?
EDIT: added example code
First of all, your code works https://jsfiddle.net/5m18jfy9/
Anyway, I think you miss the rule of dot. When you binding data to your model in scope, use object as namespace. For example:
ng-repeat='carname' //this could brake 2-way data binding in some point
ng-repeat-'car.name' //this will always update properly your model and template

AngularJS Scope Add value to a model

I am working a submit form and I have a hidden input field. I want to set the value with AngularJS, however it will be sent with the form, so it looks like this:
<input type="hidden" ng-model="formData.articleId">
How exactly can I put a value to it with AngularJS? I tried adding ng-value="article._id" to it which added a value field with the id of the article, but it did not parse on submit.
I also tried grabbing this model with AngularJS, but again it did not work:
$scope.formData.articleId = data.article._id;
It said: Can not find articleId of undefined
Am I using a wrong approach?
Try:
$scope.formData = { articleId: data.article._id };
You should use the angular implementation of Form rather than relying on a normal submit with an angular interpolated value
https://docs.angularjs.org/api/ng/directive/form

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

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
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.
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.
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.
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

Categories