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).
Related
My Question is similar to this link.
I want '%' symbol for view only, it should not effect to ngmodel.How can I format this.
I have a text box which will have percentage value, there is a calculation based on that value.So I want to display '%' for view only and ngmodel contrains only value. How can I.
you can use custom filter for percentage value
angular.module('percentageValueFilter', [])
.filter( 'titlecase', function() {
return function(input) {
if(isNaN(input)
return input;
else
return input + "%";
}
});
use this filter as shown below example in your html
{{ yourVale | percentageValueFilter }}
{{ 10 | percentageValueFilter }} // 10%
{{ 'test' | percentageValueFilter }} // test
You can try the following code:
var myApp = angular.module('myApp', []);
myApp.controller('myController', ['$scope', function($scope){
$scope.testNumber = {number:30};
}]);
myApp.directive('myDirective', function() {
return {
require: 'ngModel',
link: function(scope, element, attrs, ngModelController) {
ngModelController.$parsers.push(function(data) {
//convert data from view format to model format
return data.replace('%','');
});
ngModelController.$formatters.push(function(data) {
//convert data from model format to view format
return data+'%';
});
}
}
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="myApp" ng-controller="myController">
<input my-directive type="text" data-ng-model="testNumber.number" /> <br/>
ng-model value: {{testNumber.number}}
</div>
You can create a directive that require ngModel and add a formatter.
angular.module('formatter').directive('percentageFormatter', () => {
return {
require: 'ngModel',
link: (scope, el, attrs, ngModel) => {
ngModel.$formatters.push(value => `${value} %`);
}
}
});
When you require the ngModelController you have to add it to the input element. It is important to give it a name too.
I have small portion of codes with $watch but does not fire when a use input with autocomplete ( jQuery plugin). it only fires when manual typing input
app.directive("autoCode", ['elementData', function(elementData) {
var codes = elementData.map(function(ele){
return ele.Code;
});
return {
restrict: 'A',
scope:{
},
link: function(scope, element, attrs) {
$(element).autocomplete({source:[codes]});
}};
}]);
app.controller('transactionCtrl',['$scope','elementData', function($scope, elementData){
var names = elementData.map(function(ele) {
return ele.Name;
}),
codes = elementData.map(function(ele) {
return ele.Code;
});
$scope.$watch('code', function(codeValue){
console.log(codeValue);
});
}]);
below is html:
<form >
Code: <input type="text" name="code" auto-code ng-model="code">
Name: <input type="text" name="name" auto-name ng-model="name">
</form>
How to make it work with manual typing and autocomplete?
Try:
$scope.$watch($("input[name='code']").length, function(codeValue){
console.log(codeValue);
});
O something like that. You have to put the watch over a value that is changed for many times
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 !== '';
};
currently I'm using a custom directive which wraps the ng-minlength and ng-maxlength directives to apply the values of these directives to the model of the input. I need to do this because I'm creating a validation service which uses the angular $error object on a form to return a user friendly message about what's wrong. The problem is, when it comes to min and max lengths, I want to be able to tell the user what the length should be. I've got this working by using the following method
directive('minlength', ['$compile', function ($compile) {
return {
restrict: 'A',
require: 'ngModel',
compile: function compile(tElement) {
tElement.attr('ng-minlength', tElement.attr('minlength'));
tElement.removeAttr('minlength');
return {
post: function postLink(scope, elem, attrs) {
var keys,
form,
field = attrs['ngModel'];
$compile(elem)(scope);
for (keys in scope) {
if (scope.hasOwnProperty(keys)) {
if (keys.substring(0, 2).indexOf('$') < 0) {
if (keys !== 'this') {
form = keys;
break;
}
}
}
}
if (form) {
console.log(attrs);
scope[form][field]['minlength'] = attrs['minlength'];
}
}
}
}
}
}])
But this seems a bit longhanded and possibly difficult to maintain and test. Is there a better way to do this?
If you want just to inform the user about the min and the max:
var app = angular.module('myApp',[]);
app.controller('MyCtrl',function($scope){
$scope.minValue = 5;
$scope.maxValue = 10;
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="myApp">
<div ng-controller="MyCtrl">
<form name="myForm">
Last name: <input type="text" name="lastName" ng-model="user.last"
ng-minlength="{{minValue}}" ng-maxlength="{{maxValue}}">
<span class="error" ng-show="myForm.lastName.$error.minlength">
Should be {{minValue}} charachters long</span>
<span class="error" ng-show="myForm.lastName.$error.maxlength">
No more than {{maxValue}} characters please</span><br>
</form>
</div>
</div>
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>