Angular - using ngModel.$parsers.push and forms - angularjs

I am using ngModel.$parsers.push to change the user input:
ngModel.$parsers.push(function (value) {
//some logic here - changing the vaue
//set the new value
ngModel.$setViewValue(value);
// renders the input with the new viewValue
ngModel.$render();
return ture; //in any case
});
It works fine but it makes the form field unvalid and shows form.invalid.parse under it.
I have tried to return the value or nothing but it still does not work.
Any ideas?
Thanks.

From AngularDoc
Returning undefined from a parser means a parse error occurred. In
that case, no $validators will run and the ngModel will be set to
undefined unless ngModelOptions.allowInvalid is set to true. The parse
error is stored in ngModel.$error.parse.
Thus, try this in your input field
<input ng-model-options="{ allowInvalid: true }" />

Related

Angular can I have 2 ng-pattern to validate the same filed

My current input looks like this
<input type="email" name="email"
ng-pattern="emailRegex"
ng-change="emailChanged()"
required />
My ng-pattern="ctrl.emailRegex" validates if an email is valid or not
/^[a-zA-Z0-9._%+-]+#[a-zA-Z0-9.-]+\.[a-zA-Z]{1,63}$/;
But I would like to block info#, admin#, help#, sales# emails, so I changed the regex to
/^(?!(?:info|admin|help|sales)#)[a-zA-Z0-9._%+-]+#[a-zA-Z0-9.-]+\.[a-zA-Z]{1,63}$/
So far so good, but I would like to show
Invalid email
to "invalid#!!!!!.com!"
and
info#, admin#, help#, sales# emails are not allowed
to info#test.com
How can I have 2 ng-pattern in the same input?
Thanks
You can validate only one pattern for an input. And, even if you can sort of do it somehow by using a directive, it would be too dirty a solution. Instead, I would recommend validating the input against regex(es) inside the function of ng-change and use formName.inputName.$setValidity to set custom validity of the input. This lets you have a fallback if one pattern is passed.
So, for example, ctrl.emailChanged could probably have something like this,
ctrl.emailChanged = function() {
var emailPattern = /^[a-zA-Z0-9._%+-]+#[a-zA-Z0-9.-]+\.[a-zA-Z]{1,63}$/;
var customValidateEmail = /^(?!(?:info|admin|help|sales)#)[a-zA-Z0-9._%+-]+#[a-zA-Z0-9.-]+\.[a-zA-Z]{1,63}$/;
if(!emailPattern.test(ctrl.registrationForm.email)) {
// Invalid email
} else if (customValidateEmail.test(ctrl.registrationForm.email)) {
// handle accordingly
}
// rest of the things
...
}
Alternatively, you can move the validation logic to another function and just call it from emailChanged.

angular ng-model don't update on dirty value

i would like to know if it's possible to 'prevent' an update of an form field (input, select....) that it's dirty due to user interaction.
If i update the ng-model primitive (ie: set pippo from 1 to X) the input
will get updated even if i manually edit the control.
It's possible to prevent that?
demo page:
http://plnkr.co/edit/ClXoS7YVcEDtcApsNpde
the input field count UP every X secs.
if i enter "AAA" i would like that update STOP, because
the input it's "dirty" due to user interaction.
You can use ngModelController to tell if the input is dirty. https://docs.angularjs.org/api/ng/directive/ngModel
Modified your plunker to http://plnkr.co/edit/oJMrQZLugnzisqdXScvN?p=preview
The update function looks like
this.update = function() {
if(!$scope.frmPippo.pippo.$dirty)
$scope.pippo += 1;
}
add ngChange attribute on input element,
Update Plunker
<input type=text ng-model="pippo" ng-change="userChanged()"/>
//controller
$scope.pippo = 1;
$scope.isUserChanged = false;
$scope.counter= 1;
$scope.userChanged = function(){
$scope.isUserChanged = true;
}
this.update = function() {
if(!$scope.isUserChanged){
$scope.pippo += 1;
}
}
$interval(this.update, 3000);
userChanged would be called when user typing, but would not be called when pippo changed by other methods in controller, so we could set a flag in userChanged to indicate whether the input has been overwrite by user
If I got the question maybe this is what you are trying to do?
$scope.$watch("pippo", function(){
if($scope.pippotmp != $scope.pippo)
$scope.useredited = true;
});
UPDATED Answer
plunker
you could keep the model separated from that input changes... in a temporary variable
I might get the question wrong, but if I understand you correctly, then ng-model-option is what you are looking for.
For example, you can update the model only when the user leave the input
ng-model-options="{ updateOn: 'blur' }"
You can read more about it in the doc: https://docs.angularjs.org/api/ng/directive/ngModelOptions

Ucwords filter in angularjs throwing error

Something strange is happening. Actually I am assigning a title from asynchronous call and I'm applying a ucwords filter on the title. It is giving me the proper output but throwing the error first and after then showing the proper value.
HTML snippet:
<h1 ng-show="defaultProduct.Campaign.title">{{ defaultProduct.Campaign.title | ucwords }}</h1>
Filter Snippet
app.filter("ucwords", function () {
return function (input){
input = input.toLowerCase().replace(/\b[a-z]/g, function(letter) {
return letter.toUpperCase();
});
return input;
}
})
Please note defaultProduct.Campaign.title is assigning from AJAX call. It initializes after ajax success. In my console it is throwing the error first and after ajax success call it is showing proper output.
If input is show me first title then output will be Show Me First Title. But why it is throwing the error first ? I'm thinking to use $timeout in filter but it would not a good way of doing that. Can anyone suggest me best way ?
Below is the error :
Error: input is undefined
filters are evaluated on each digest cycle
Initially the value of defaultProduct.Campaign.title is not defined that is going to decide async call. At that time your custom filter is gets called before setting defaultProduct.Campaign.title value. Filter tries to input.toLowerCase() which return input is undefined where as input value is not defined. You should typically handle this scenarios inside your filter itself.
Filter
app.filter("ucwords", function () {
return function (input){
if(input) { //when input is defined the apply filter
input = input.toLowerCase().replace(/\b[a-z]/g, function(letter) {
return letter.toUpperCase();
});
}
return input;
}
})

Input is invalid even when it's error is empty

I'm trying to create my own validation for password confirm, and putting my error on $error. this is my code:
html:
<input ng-model="user.password2" type="password" name="password2" required
ng-keyup="confirmPassword(user.password, user.password2)">
<div ng-messages="register.password2.$error" ng-if="register.password2.$dirty">
<div ng-message="required">Password is required</div>
<div ng-message="passwordsDontMatch">Passwords don't match</div>
</div>
JS:
$scope.confirmPassword = function (pass1, pass2) {
if (angular.isUndefined(pass1) || angular.isUndefined(pass2) || pass1.trim() != pass2.trim()) {
$scope.register.password2.$error["passwordsDontMatch"] = true;
} else {
delete $scope.register.password2.$error["passwordsDontMatch"];
}
console.log($scope.register.password2.$error);
};
it looks like it's working. when the passwords are the same, the message is not displayed and indeed the $error object is empty. But the input is still invalid: ($scope.register.password2.$invalid == true)
you can see what I'm talking about in this plunkr: http://plnkr.co/edit/ETuVqsdSaEBWARvlt4RR?p=preview
try 2 identical passwords. the message will disappear but when you blur from the input, it's still red because internally it's $invalid
The problem probably comes from the fact that you're not typing a password in the first field that matches your regex pattern. The first password is thus undefined, since it doesn't respect the ng-pattern validation rule.
That said, you shouldn't modify the $error array directly. Instead, you should set the validity of the field using $setValidity(). That will not only set and remove the error automatically, but also deal with the $invalid/$valid properties, add and remove the CSS classes, etc.
var valid = !((angular.isUndefined(pass1) || angular.isUndefined(pass2) || pass1.trim() != pass2.trim()));
$scope.register.password2.$setValidity("passwordsDontMatch", valid);
Here's a working example. But remember to enter a valid password in the first place.
Also, instead of implementing this check with ng-keyup, you should make it a directive, which would add a validator to the validators of the form input. This would make sure the check is made whatever the way the second password is entered (i.e. via copy/paste using the mouse only, or simply by prepopulating the form programmatically.

issues with select field's empty option

We are facing multiple issues with select field's empty option.
My rendering code is as follows:
<select ng-switch-when="select" id="{{field.name}}" ng-model=data[field.name] >
<option ng-repeat="option in field.options" value="{{option.value}}">{{option.description}}
</select>
Here our UI is dynamically generated and options also populates dynamically using REST APIs. The Select field is bound to a data object that also populated using REST calls that might have blank value initially.
Issue
It automatically adds a empty option where it is not required and disappear after selecting any non-empty option.
Also it is showing following error for few select fields
TypeError: Cannot call method 'prop' of undefined
at selectDirective.link.ngModelCtrl.$render (lib/angular/angular.js:20165:47)
at Object.ngModelWatch (lib/angular/angular.js:16734:14)
at Scope.$get.Scope.$digest (lib/angular/angular.js:11800:40)
at Scope.$get.Scope.$apply (lib/angular/angular.js:12061:24)
at done (lib/angular/angular.js:7843:45)
at completeRequest (lib/angular/angular.js:8026:7)
at XMLHttpRequest.xhr.onreadystatechange (lib/angular/angular.js:7982:11)
I have doubt that is use following code in angular
function setupAsSingle(scope, selectElement, ngModelCtrl, selectCtrl) {
ngModelCtrl.$render = function() {
var viewValue = ngModelCtrl.$viewValue;
if (selectCtrl.hasOption(viewValue)) {
if (unknownOption.parent()) unknownOption.remove();
selectElement.val(viewValue);
if (viewValue === '') emptyOption.prop('selected', true); // to make IE9 happy
} else {
if (isUndefined(viewValue) && emptyOption) {
selectElement.val('');
} else {
selectCtrl.renderUnknownOption(viewValue);
}
}
...
and it break at this line
if (viewValue === '') emptyOption.prop('selected', true); // to make IE9 happy
because the empty option is provided by REST API at later stage and not available till initial rendering.
Please provide some suggestion for implementation and some alternate approach.
P.S : I can not use ng-option with select as it emit indexed values and I need actual values for some DOM manipulation.
I would first read over how to do data-ng-options (http://docs.angularjs.org/api/ng.directive:select) because your select will cause you a bit of issues. Here's how you could move your code around:
<span data-ng-switch-when="select">
<select id="{{field.name }}" data-ng-model="data[field.name]" data-ng-options="option.value as option.description for option in field.options></select>
</span>
Secondly, the reason that the blank option is selected is because data[field.name] is not set to a value that's present in option.value. You need to set it in your controller so the blank option goes away. You can see a similar question here: Why does AngularJS include an empty option in select?

Resources