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.
Related
I have created an angular directive for auto complete like bellow,
.directive('searchautocomplete',function(){
return function(scope, element, attrs) {
var data = [
{"label":"Aragorn", "actor":"Viggo Mortensen"},
{"label":"Arwen", "actor":"Liv Tyler"},
{"label":"Bilbo Baggins", "actor":"Ian Holm"},
{"label":"Boromir", "actor":"Sean Bean"},
{"label":"Frodo Baggins", "actor":"Elijah Wood"},
{"label":"Gandalf", "actor":"Ian McKellen"},
{"label":"Gimli", "actor":"John Rhys-Davies"},
{"label":"Gollum", "actor":"Andy Serkis"},
{"label":"Legolas", "actor":"Orlando Bloom"},
{"label":"Meriadoc Merry Brandybuck", "actor":"Dominic Monaghan"},
{"label":"Peregrin Pippin Took", "actor":"Billy Boyd"},
{"label":"Samwise Gamgee", "actor":"Sean Astin"}
];
element.autocomplete({
source:data,
}) ;
}
})
and my html like below
<input type="text" ng-model="sfilter.search_text" searchautocomplete >
But it is not working,when the model value is updated.why?
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).
Hi! I have the following input box :
<div ng-repeat="item in items"
<input ng-model="item.cost" />
</div>
I am trying to figure out how to apply "currency" filter to the input box. So that when value is displayed it displays with $ sign and proper currency notations. I can do with span using {{item.cost | currency }} but I need the box to be editable to change the value if needed.
Please let me know how I can apply currency filter to the input box.
Thanks
Try this
.directive('currencyFormatter', ['$filter', function ($filter) {
formatter = function (num) {
return $filter('currency')(num);
};
return {
restrict: 'A',
require: 'ngModel',
link: function (scope, element, attr, ngModel) {
ngModel.$parsers.push(function (str) {
return str ? Number(str) : '';
});
ngModel.$formatters.push(formatter);
element.bind('blur', function() {
element.val(formatter(ngModel.$modelValue))
});
element.bind('focus', function () {
element.val(ngModel.$modelValue);
});
}
};
}]);
And the html
<div ng-repeat="item in items">
<input ng-model="item.cost" currency-formatter/>
</div>
<div ng-repeat="item in items">
{{item.cost}}
</div>
Here is my plunker and the code I can't get to work starts on line 32
http://plnkr.co/edit/pmCjQL39BWWowIAgj9hP?p=preview
I am trying to apply an equivalent to markdown filter onto a directive... I created the filter and tested with manually applying the filter and it works that way,, but I should only use the filter conditionally when the type of content on directive is set to markdown.
I am trying to accomplish this by updating ng-model >>> ngModel.$setViewValue(html) but I am getting an error
ngModel.$setViewValue is not a function.. which makes me thing that the controller is not recognized although it is required by the directive.
Here is a working controller:
var app = angular.module('testOne', ["ngResource", "ngSanitize"]);
app.controller('testOneCtrl', function ($scope) {
$scope.product = {
id:12,
name:'Cotton T-Shirt, 2000',
description:'### markdown\n - list item 1\n - list item 2',
price:29.99
};
});
app.directive("myText", function ($parse) {
return {
restrict: "E",
require: "?ngModel",
scope:{
css: "#class", type: "#type"
},
controller: function ($scope, $element, $attrs) {},
templateUrl: "template.html",
compile: function(elm, attrs, ngModel){
var expFn = $parse(attrs.contentType + '.' + attrs.value);
return function(scope,elm,attrs){
scope.$parent.$watch(expFn, function(val){
scope.exp = { val: val };
if ( attrs.type == 'markdown'){
var converter = new Showdown.converter();
var html = converter.makeHtml(val);
//scope.exp.val = html;
ngModel.$setViewValue(html);
ngModel.$render();
}
})
scope.$watch('exp.val', function(val){
expFn.assign(scope.$parent, val)
})
}
}
}
})
This is a filter for markdown which works when applied.. (I would consider using the filter if I could figure out the way to conditionally apply it to existing directive but I'd rather do it with ng-model)
/*
app.filter('markdown', function ($sce) {
var converter = new Showdown.converter();
return function (value) {
var html = converter.makeHtml(value || '');
return $sce.trustAsHtml(html);
};
});
*/
Here is the directive template
<div ng-class="{{css}}"
ng-click="view = !view"
ng-bind-html="exp.val">
</div>
<div>
<textarea rows="4" cols="30" ng-model="exp.val"></textarea>
</div>
This is the directive in use:
<mb-text ng-cloak
type="markdown"
content-type="product"
value="description"
class="test-one-text-2">
</mb-text>
Why ngModel is empty?
When using require on a directive the controller is passed as the 4th argument to the linking function. In you code you try to reference it as an argument of the compile function. The controller is only instantiated before the linking phase so it could never be passed into the compile function anyway.
The bigger issue is that require can only get a controller of the same element ({ require: 'ngModel' }), or parent elements ({ require: '^ngmodel' } ). But you need to reference a controller from a child element (within the template).
How to get ngModel?
Do not use require at all as you cannot get child element's controller with it.
From angular.element docs:
jQuery/jqLite Extras
controller(name) - retrieves the controller of the current element or its parent. By default retrieves controller associated with the ngController directive. If name is provided as camelCase directive name, then the controller for this directive will be retrieved (e.g. 'ngModel').
Inside the linking function you can get the hold of the controller like so:
var ngModel = elm.find('textarea').controller('ngModel');
I fixed your directive:
here is a plunker: http://plnkr.co/edit/xFpK7yIYZtdgGNU5K2UR?p=preview
template:
<div ng-class="{{css}}" ng-bind-html="exp.preview"> </div>
<div>
<textarea rows="4" cols="30" ng-model="exp.val"></textarea>
</div>
Directive:
app.directive("myText", function($parse) {
return {
restrict: "E",
templateUrl: "template.html",
scope: {
css: "#class",
type: "#type"
},
compile: function(elm, attrs) {
var expFn = $parse(attrs.contentType + '.' + attrs.value);
return function(scope, elm, attrs) {
scope.exp = {
val: '',
preview: null
};
if (attrs.type == 'markdown') {
var converter = new Showdown.converter();
var updatePreview = function(val) {
scope.exp.preview = converter.makeHtml(val);
return val;
};
var ngModel = elm.find('textarea').controller('ngModel');
ngModel.$formatters.push(updatePreview);
ngModel.$parsers.push(updatePreview);
}
scope.$parent.$watch(expFn, function(val) {
scope.exp.val = val;
});
scope.$watch('exp.val', function(val) {
expFn.assign(scope.$parent, val);
});
};
}
};
});
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>