Angular directive change ngmodel value to undefined - javascript

I'm using this form
<div ng-controller="TestController">
<form ng-submit="saveForm()">
<input type="text" name="test" ng-model="testData" test="{{testValue}}"/>
<input type="submit" value="Save"/>
</form>
</div>
and this angular code:
app.controller('TestController', [ '$scope', function($scope) {
$scope.testData="testValue";
$scope.saveForm = function(){
console.log($scope.testData);
}
}]);
app.directive('test', function($parse) {
return {
require: 'ngModel',
link: function(scope, elm, attrs, ctrl) {
ctrl.$validators.validators = function(modelValue, viewValue) {
};
}
};
});
And after changing value in input and clicking save I'm getting undefined value in $scope.testData. Why and how to avoid that?
I was trying to add:
scope.testData = viewValue;
inside function:
ctrl.$validators.validators
But it doesn't work - only by turns.

Your validator needs to return true for the value to be set, otherwise it will be undefined
If the validity changes to invalid, the model will be set to undefined, unless ngModelOptions.allowInvalid is true. If the validity changes to valid, it will set the model to the last available valid modelValue, i.e. either the last parsed value or the last value set from the scope.
ctrl.$validators.validators = function(modelValue, viewValue) {
return modelValue !== '';
};

Related

AngularJS textbox value undefined

I have created a directive wherein it copies the value of textbox1 to textbox2.
function myCopyText() {
return {
restrict: 'A',
link: function(scope, element, attr) {
$('#textbox2').val($('#textbox1').val())
}
}
}
Then on the textbox field:
<input type="text" id="textbox1" ng-model="vm.textbox1" my-copy-text />
<input type="text" id="textbox2" ng-model="vm.textbox2" />
It works fine until I submitted the form wherein vm.textbox2 is always undefined. But if I manually inputted a value on textbox2, vm.textbox2 is able to display the value.
I find it weird that when the directive do the value assignment, vm.textbox2's value is always undefined not until I manually set a value by typing it in.
That's by design. The angular digest kicks in when you change the value via the
input/change event therefore val() will not trigger the setViewValue, so the model value will not be updated.
This directive will replicate the value from the model/view to the name you pass in the replicate-to attribute:
function replicateTo($parse) {
return {
scope: false,
require: 'ngModel',
restrict: 'A',
link: function(scope, element, attr, ngModelCtrl) {
var target = $parse(attr.replicateTo);
var angularRender = ngModelCtrl.$render;
ngModelCtrl.$render = function(){
angularRender();
target.assign(scope, ngModelCtrl.$viewValue);
};
ngModelCtrl.$viewChangeListeners.push(function(){
target.assign(scope, ngModelCtrl.$viewValue);
});
}
}
}
<input type='text' ng-model="vm.textbox1" data-replicate-to="vm.textbox2"/> <br>

ngModel formatters change the $scope

Having an input with ngModel which has $formatters and $parsers -
link: function(scope, element, attrs, ngModel) {
//format text going to user (model to view)
ngModel.$formatters.push(function(obj) {
return obj.first;
});
}
After I make any change in the input , I miss this model on the scope , mean - {{person.first}} display nothing .
Here is the full code -
var myAppModule = angular.module('myApp', []);
myAppModule.controller('myCtrl',function ($scope) {
$scope.person = {
first: 'Alice',
last: 'Bob'
}
})
.directive('myDrtv',function(){
return {
restrict: 'A',
require: 'ngModel',
link: function(scope, element, attrs, ngModel) {
var _ngModel = ngModel;
//format text going to user (model to view)
ngModel.$formatters.push(function(obj) {
return obj.first;
});
//format text from the user (view to model)
ngModel.$parsers.push(function(value) {
return value;
});
}
}
})
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="myApp">
<div ng-controller="myCtrl">
Name : <input my-drtv ng-model="person" type="text"><br>
{{person.first}}
</div>
</div>
How could I change the input and still see it in {{person.first}} ?
Please don't answer me change it to ng-model="person.first" I looking solution with - ng-model="person"
Change your input to the following:
<input my-drtv ng-model="person.first" type="text">
They way you have it, you are clobbering person and changing it from an object to a string.
Edit: If you want to keep first name and last name separate, then do something like this:
<input my-drtv ng-model="fullname" type="text">
and then in link, watch for changes and update person:
scope.$watch('fullname', function(nv) {
person.first = extractFirstName(nv);
person.last = extractLastName(nv);
});
(I left it to you to supply the extract functions).

Prepopulate AngularJS form with invalid data

My model contains some data that does not passes the form's validation (say an invalid email address that comes from the server). I still want to show this invalid model data to the user so they get a chance to fix it.
Minimal example:
<form ng-init="email='foo'">
<input type="email" ng-model="email"></input>
</form>
How do I get the input to show the initial invalid model value?
JS Fiddle: http://jsfiddle.net/TwzXV/4/
This behaviour is reported as a bug. https://github.com/angular/angular.js/issues/2841
You can go around this behaviour by creating a directive UNTIL this bug is fixed :)
I got this from google mailing list
http://jsfiddle.net/nalberg/XccGJ/
app.directive('displayInvalid', function($parse, $filter) {
return {
restrict: 'A',
require: 'ngModel',
link: function(scope, elm, attrs, model) {
var displayed = false;
scope.$watch(attrs.ngModel, function(newValue, oldValue, scope) {
// only set once... on initial load
if(displayed == false && oldValue != undefined){
displayed = true;
elm.val(model.$modelValue);
}
});
}
}
})

$error for a field on form undefined

I need to validate an input on a form pre-submitting. I created a directive that validates said input and sets 'valid_amount' to false or true accordingly.
The problem is on the form, I can not seem to evaluate 'formTransfer.amount.$error.valid_amount', $error does not exists. Any idea on what might be the problem?
Part of the form
<form name='formTransfer' ng-submit='prepare( transfer )' >
<input amount name='amount' ng-model='transfer.amount' required='required' type='number'>
<span class='error' ng-show='formTransfer.amount.$error.valid_amount'>This is not valid valid_amount!</span>
And the directive:
var AMOUNT_REGEXP = /^(\d*\.\d{1,2}|\d+)$/;
app.directive("amount", function() {
return {
require: "ngModel",
link: function(scope, element, attrs, ngModel) {
return ngModel.$parsers.unshift(function(viewValue) {
if (AMOUNT_REGEXP.test(viewValue)) {
ngModel.$setValidity("valid_amount", true);
viewValue;
} else {
ngModel.$setValidity("valid_amount", false);
undefined;
}
return console.log(ngModel);
});
}
};
});
I'm working with HAML and Coffeescript, please let me know if some of the code does not make sense to clarify or post the original code.
ngModelController.$parsers array is an array of functions that are being applied to the value entered by user.
ngModelController.$formatters array is an array of functions being applied to the value that is coming from the model (from the scope).
Your error is not being displayed initially because you're not validating your field for the initial (model) value.
So, you need to adjust your directive as such:
var AMOUNT_REGEXP = /^(\d*\.\d{1,2}|\d+)$/;
app.directive("amount", function() {
return {
require: "ngModel",
link: function(scope, element, attrs, ngModel) {
function validate(value){
if (AMOUNT_REGEXP.test(value)) {
ngModel.$setValidity("valid_amount", true);
return value;
} else {
ngModel.$setValidity("valid_amount", false);
return undefined;
}
}
ngModel.$parsers.unshift(validate);
ngModel.$formatters.unshift(validate);
}
};
});
PLUNKER

Angularjs initial form validation with directives

I have a validation directive called valid-number that is used to set the validity of a form using $setValidity - this works fine for any text values that I type into the input box that have the directive applied to as an attribute.
The HTML is
<form name="numberForm">
<input name="amount" type="text" ng-model="amount" required valid-number /></form>
The directive is as follow
angular.module('test',[]).directive('validNumber',function(){
return{
require: "ngModel",
link: function(scope, elm, attrs, ctrl){
var regex=/\d/;
ctrl.$parsers.unshift(function(viewValue){
var floatValue = parseFloat(viewValue);
if(regex.test(viewValue)){
ctrl.$setValidity('validNumber',true);
}
else{
ctrl.$setValidity('validNumber',false);
}
return viewValue;
});
}
};
});
However, I would also like the validation to be triggered and set the css to an invalid clsss if the value the input box is initialised to when the page is first loaded is invalid, eg if I set $scope.amount = 'not a number' I would expect the input box to have had the directive applied to it, but no joy. In order for not a number to be highlighted as invalid I have to make a change to the contents of the input, which triggers the directive.
How can I ensure the directive applies to whatever the <input> is initialised with?
A full code example is here;
http://jsfiddle.net/JW43C/5/
$parsers array contains a list of functions that will be applied to the value that model receives from the view (what user types in), and $formatters array contains the list of functions that are being applied to the model value before it's displayed in the view.
In your directive you correctly used the $parsers array, but you also need to add the $formatters array if you want the initial value to be validated:
angular.module('test',[]).directive('validNumber',function(){
return{
require: "ngModel",
link: function(scope, elm, attrs, ctrl){
var regex = /^\d$/;
var validator = function(value){
ctrl.$setValidity('validNumber', regex.test(value));
return value;
};
ctrl.$parsers.unshift(validator);
ctrl.$formatters.unshift(validator);
}
};
});
Demo plunker
You can simply call your verification function during the linking phase, like in this fiddle :
link: function(scope, elm, attrs, ctrl) {
var regex=/\d/;
var verificationFunction = function(viewValue) {
var floatValue = parseFloat(viewValue);
if(regex.test(viewValue)) {
ctrl.$setValidity('validNumber',true);
return viewValue;
}
else {
ctrl.$setValidity('validNumber',false);
return undefined;
}
};
ctrl.$parsers.unshift(verificationFunction);
verificationFunction();
}
After (>=) angular 1.3.1 version was released you could implement that behaviour with a little bit correct way, following angular validation directives style (e.g. required, maxlength).
In that case you have to append your validator as property of $validators array and there are no need in $parsers or $formatters anymore:
var app = angular.module('test', []);
app
.directive('validNumber', function() {
return {
require: "ngModel",
link: function(scope, elm, attrs, ctrl) {
var regex = /^\d+$/;
ctrl.$validators['validNumber'] = function(modelValue, viewValue) {
return regex.test(viewValue);
};
}
};
});
app.controller('NumberCtrl', NumberCtrl);
function NumberCtrl($scope) {
$scope.amount = '5z';
};
input.ng-invalid {
background-color: #FA787E;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.1/angular.min.js"></script>
<div ng-app="test">
<div ng-controller="NumberCtrl">
<div ng-form name="numberForm">
<input name="amount"
type="text"
ng-model="amount"
required
valid-number />
<span ng-show="numberForm.amount.$error.validNumber">
Doesn't look like an integer
</span>
</div>
</div>
</div>

Categories

Resources