Angular directive for multiple elements - javascript

I created this number format directive, but if I use it on more than one input, it doesn't work for all of them, but with only one it works.
Any ideas?
directive('formatUsNumber', function() {
return {
restrict: 'A',
require: 'ngModel',
priority: 100,
link: function(scope, element, attrs, ngModel) {
scope.formatNumber = function() {
var n = element.val();
var dest = n;
if (n.length >= 10) {
if (/^[A-Za-z\s]+$/.test(n)) {
return;
}
dest = n.replace(/\D/g, '');
if (!isNaN(dest)) {
n = dest;
}
if (n.substr(0, 1) != "1") {
n = "1" + n;
}
}
element.val(n);
ngModel.$setViewValue(n);
};
},
};
});
The template:
<input type="text" ng-change="formatNumber()" format-us-number ng-model="myModel" />
Fiddle: http://jsfiddle.net/Lvc0u55v/7479/

I think it's because scope of directive is not isolated.
And also I've made few changes, hope it workes the same
directive('formatUsNumber', function() {
return {
restrict: 'A',
require: 'ngModel',
priority: 100,
scope: true,
link: function(scope, element, attrs, ngModel) {
scope.formatNumber = function() {
var n = ngModel.$modelValue;
if (n.length >= 10) {
if (/^[A-Za-z\s]+$/.test(n)) {
return;
}
n = n.replace(/\D/g, '');
if (!isNaN(dest)) {
n = dest;
}
if (n.substr(0, 1) != "1") {
n = "1" + n;
}
ngModel.$setViewValue(n, 'change:directive');
}
};
},
};
});
U can test it here

Try adding an isolated scope, something like this:
restrict: 'A',
require: 'ngModel',
priority: 100,
scope:{
ngModel:'='
},...

For this use case I think that fits better implements a custom filter instead a directive.
Building Custom AngularJS Filters
Other alternative could be incude a custom parser and/or formatter.
AngularJS - Formatters and Parsers

Related

Transclusion in AngularJS, repeat multiple elements

I am using Angularjs 1.6, I made this transclusion type of thing.
var app = angular.module('plunker', []);
app.controller("mainCtrl", function ($scope) {
$scope.testmodel1 = {
testmodel1prop: "testmodel1prop"
};
$scope.testmodel2 = {
testmodel2prop: "testmodel2prop"
}
})
app.directive('tabs', function ($compile) {
return {
restrict: 'E',
scope: {},
transclude: true ,
link: function (scope, el, attrs, ctrl, transcludeFn) {
// transcluded content's scope should inherit from parent
transcludeFn(scope.$parent, function (clonedTranscludedContent) {
var tabs = [];
for (var i = 1; i < clonedTranscludedContent.length; i = i + 2) {
tabs.push(clonedTranscludedContent[i]);
}
for (var i = 0; i < tabs.length; i++) {
debugger;
var jqueryTab = $(clonedTranscludedContent[1]);
var stringTab = jqueryTab.prop('outerHTML');
var model = jqueryTab.attr('model');
var pocoModelFromParent = scope.$parent[model];
var newScope = angular.merge(scope.$parent.$new(), pocoModelFromParent)
var linkFn = $compile(stringTab);
var compiledContent = linkFn(newScope);
el.append(compiledContent);
}
});
}
};
});
app.directive('test', function () {
return {
restrict: 'E',
scope: {},
link: function ($scope, $element, attr) {
$scope.var1 = "test";
},
template: '<p>{{ var1 }}</p>'
};
});
<div ng-app="plunker" >
<div ng-controller="mainCtrl">
<tabs>
<tab model="testmodel1" title="tab2">{{ testmodel1prop }}</tab>
<tab model="testmodel2" title="tab1"> {{ testmodel2prop }} </tab>
</tabs>
</div>
</div>
I would like to get some feedback if this is okay or not.
It works perfectly by the way.
The first thing that pops into my mind is why does 'clonedTranscludedContent' has a sort of empty object at position 1 3 5 7 and so on.I have yet to figure out what that is, probably the ::before in chrome?
I am planning to use this on production actually.

How to use tags-input and auto-complete in custom directive?

I have created custom-directive, I want to use it with 'tags-input'. But When I select any value from auto-complete result, It does not select. I don't know why it not working, Please find
Demo Plunker
Index.html
<my-directive apipoint="customerApi" modeldisplay="customer.selected"
ng-model="customer.selected" searchresults="customeruserprofile" change="customerChanged"></my-directive>
Directive:
app.directive("myDirective", ['$http',function($http){
return {
restrict: "E",
templateUrl: 'auto-complete.html',
require: 'ngModel',
scope : {
modeldisplay: "=",
apipoint: "=",
change: "="
},
link : function(scope, element, attrs, ctrl){
scope.loadTags = function(query) {
return $http.get(scope.apipoint[0]);
};
scope.addCustomerTag = function($tag){
var selectedCustomer = scope.modeldisplay;
if(selectedCustomer === undefined){
return true;
}
return false;
};
scope.tagAdded = function($tag){
scope.selectedmodel = $tag
var selectFun=scope.change;
selectFun();
};
scope.removedCustomerTag = function(){
scope.modeldisplay = undefined;
scope.selectedmodel = undefined;
};
scope.tagAdded = function($tag){
scope.selectedmodel = $tag;
scope.tagid = $tag.customer_id;
scope.change();
};
}
};
}]);

how to pass parameters to ng-event handlers in Angular?

I have a piece of code like this:
Module.Article.directive('article', function ($compile) {
return {
restrict: 'C',
link: function (scope, element, attrs) {
var el = angular.element('<div></div>');
scope.showNum = function(i){
console.log(i);
};
for (var i = 1; i <= 5; i++) {
el.append('<span ng-mouseover="showNum(i)">' + i + '</span>');
}
$compile(el)(scope);
element.append(el);
}
and I want to get this result:
<div>
<span ng-mouseover="showNum(1)">1</span>
<span ng-mouseover="showNum(2)">2</span>
<span ng-mouseover="showNum(3)">3</span>
<span ng-mouseover="showNum(4)">4</span>
<span ng-mouseover="showNum(5)">5</span>
</div>
but instead I get such result:
<div>
<span ng-mouseover="showNum(i)">1</span>
<span ng-mouseover="showNum(i)">2</span>
<span ng-mouseover="showNum(i)">3</span>
<span ng-mouseover="showNum(i)">4</span>
<span ng-mouseover="showNum(i)">5</span>
</div>
Does anybody know how to pass i to the showNum() handler?
Change it to this, so the actual i variable gets printed instead of the letter i:
Module.Article.directive('article', function ($compile) {
return {
restrict: 'C',
link: function (scope, element, attrs) {
var el = angular.element('<div></div>');
scope.showNum = function(i){
console.log(i);
};
for (var i = 1; i <= 5; i++) {
el.append('<span ng-mouseover="showNum(' + i + ')">' + i + '</span>');
}
$compile(el)(scope);
element.append(el);
}
Edit: I agree that the other answer offers more flexibility, this answer points out a minor mistake in the way the person who asked the question was trying to implement it though.
You could use ng-repeat to render template and pass value, only you need to create a items array inside your directive link function, ng-repeat will take care of rendering part of the html.
Directive
Module.Article.directive('article', function($compile) {
return {
restrict: 'C',
template: '<div>' +
'<span ng-repeat="i in items" ng-mouseover="showNum(i)">{{i}}</span>'+
'</div>',
link: function(scope, element, attrs) {
var el = angular.element('<div></div>');
scope.showNum = function(i) {
console.log(i);
};
scope.items = [];
for (var i = 1; i <= 5; i++) {
scope.items.push(i)
}
}
}
});
Demo Plunkr

whole number pad with zeros

I have a directive that convert to 2 decimal places (input number textbox) and
it is working well but I would like to pad the whole number with zeros.
Anyone knows how I can achieve whole number with padded zeros? Example below.
2 -> 2.00 - not done
10.666 -> 10.67 - done
app.directive('toPrecision',['$filter', function ($filter) {
return {
replace: false,
restrict: 'A',
link: function(scope, element, attr) {
var input = angular.element(element);
var precisionValue = attr.toPrecision;
input.on('keyup', function() {
var parsed = parseFloat(input.val());
if (!isNaN(parsed)) {
if (parseInt(parsed, 10) !== parsed && String(parsed).split('.')[1].length > 2) {
var result = parsed.toFixed(precisionValue);
input.val(result);
}
}
});
}
}
}]);
HTML
<input type="number" class="form-control" ng-model="rate" to-precision="2" min="0" step="1">
Ended rewriting the directives for what I want.
app.directive('toPrecision', function() {
return {
replace: false,
restrict: 'EA',
require: 'ngModel',
link: function(scope, element, attr, ngModelCtrl) {
var input = angular.element(element);
var precisionValue = attr.toPrecision;
ngModelCtrl.$parsers.push(function(value) {
var clean = value.replace(/[^-0-9\.]/g, '');
if (value !== clean) {
ngModelCtrl.$setViewValue(clean);
ngModelCtrl.$render();
}
return clean;
});
ngModelCtrl.$formatters.push(function(value) {
if (angular.isUndefined(value)) {
return "";
}
var parsed = parseFloat(value);
return parsed.toFixed(precisionValue);
});
input.on('blur', function() {
var parsed = parseFloat(input.val());
if (!isNaN(parsed)) {
var result = parsed.toFixed(precisionValue);
input.val(result);
ngModelCtrl.$setViewValue(result);
}
});
}
}});
Here's code to do it taken from the answer I voted to close as duplicate:
var s = number.toString();
if (s.indexOf('.') == -1) s += '.';
while (s.length < s.indexOf('.') + 4) s += '0';
Let math do it, Try this.
if (!isNaN(parsed)) {
parsed += Math.pow(10, -1 * (precisionValue + 2)); // 2 + 0.0001
var result = parsed.toFixed(precisionValue); // 2.00
input.val(result);
}
See fiddle

Set angular directive options as child tags

I'm learning AngularJs now, and trying to write my first directives.
So i have a question: is there any way to pass complex options to directive. For example i want to write directive wrapper for slick grid. It has a lot of options, columns for example, and it's imposible to configure it using attributes. Can i do simething like this?
<s-grid>
<s-grid-columns>
<s-grid-column id="title" title="Title"/>
<s-grid-column id="duration" title="Duration"/>
</s-grid-columns>
...
</s-grid>
And get all this properties as json object in s-grid directive?
So i could do it. Is it here any mistakes?
module
.directive('sGrid', [function () {
return {
restrict: 'E',
controller: function($scope) {
$scope.columns = [];
this.setColumns = function(columns) {
$scope.columns = columns;
};
},
link: function (scope, element, attrs, controller, transclude) {
// for clearer present I initialize data right in directive
// start init data
var columns = scope.columns;
var options = {
enableCellNavigation: true,
enableColumnReorder: true
};
var data = [];
for (var i = 0; i < 50000; i++) {
var d = (data[i] = {});
d["id"] = "id_" + i;
d["num"] = i;
d["title"] = "Task " + i;
d["duration"] = "5 days";
d["percentComplete"] = Math.round(Math.random() * 100);
d["start"] = "01/01/2009";
d["finish"] = "01/05/2009";
d["effortDriven"] = (i % 5 == 0);
}
// end init data
// finally render layout
scope.grid = new Slick.Grid(element, data, columns, options);
$(window).resize(function () {
scope.grid.resizeCanvas();
})
}
}
}])
.directive("sGridColumns", function(){
return {
require: '^sGrid',
restrict: 'E',
controller: function($scope) {
var columns = $scope.columns = [];
this.addColumn = function(pane) {
columns.push(pane);
};
},
link: function (scope, element, attrs, gridCtrl){
gridCtrl.setColumns(scope.columns);
}
}
})
.directive('sGridColumn', function() {
return {
require: '^sGridColumns',
restrict: 'E',
transclude: 'element',
link: function (scope, element, attrs, gridCtrl) {
scope.field = scope.field || scope.id;
scope.title = scope.title || scope.field;
gridCtrl.addColumn({
id: attrs.id,
field: attrs.field || attrs.id,
name: attrs.name || attrs.field || attrs.id
});
}
};
});
And declaration:
<s-grid>
<s-grid-columns>
<s-grid-column id="title" name="Title"></s-grid-column>
<s-grid-column id="duration" name="Duration"></s-grid-column>
</s-grid-columns>
</s-grid>

Categories