AngularJS textbox value undefined - javascript

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>

Related

Angular directive for range validation

I am trying to make a directive for an input to limit the value between 1-99. On the same input I also have another directive that converts the value to a percentage and am not sure if that is what is getting in the way.
The directive is simple (taken basically from the Angular website):
(function() {
'use strict';
angular
.module('app.model')
.directive('inputRange', inputRange);
function inputRange() {
return {
require: 'ngModel',
restrict: 'A',
link: function(scope, elm, attrs, ctrl) {
var INTEGER_REGEXP = /^-?\d+$/;
ctrl.$validators.inputRange = function(modelValue, viewValue) {
if (ctrl.$isEmpty(modelValue)) {
// consider empty models to be valid
return true;
}
if (INTEGER_REGEXP.test(viewValue)) {
// it is valid
return true;
}
// it is invalid
return false;
};
}
}
}
});
And the section of html with the input field (which is paired with a slider):
<form name="form">
<div sc-slider
ng-model="vm.baseline"
min="0.01"
max="0.99"
initial="{{vm.baseline}}"
step="0.01"
uib-tooltip="The initial estimate of the KIQ's likelihood - prior to any indicator observations."
tooltip-popup-delay="200"
tooltip-popup-close-delay="200"
tooltip-placement="bottom"></div>
<input to-percent
input-range
name="baseline"
style="text-align:center;"
type="text"
min="1"
max="99"
class="form-control"
ng-model="vm.baseline"></input>
<span ng-show="form.baseline.$error.inputRange">The value is not a valid integer!</span>
<span ng-show="form.baseline.$error.min || form.baseline.$error.max">
The value must be in range 1 to 99!</span>
</form>
I have read on SO about priority for directives that share an input but I don't think that is necessarily an issue here. But when I enter a value greater than 99 I'd expect one of the spans below to show up, but nothing is appearing. And my other directive works fine all of the time. Any help is appreciated.
Make your life easier and use ng-max and ng-min attributes on the input element.

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

Angular directive change ngmodel value to undefined

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 !== '';
};

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);
}
});
}
}
})

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