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

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

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.

Angular directive for multiple elements

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

ngRepeat keep re-creating

I created a fiddle that simulate my issue. I'm using ng-repeat to create some nodes. But these nodes are to be used by another library (Openlayers) which "move" (appendChild) these nodes to another place in the DOM.
So, a fight for these nodes is happening. Is there a way to tell ngRepeat to stop re-sorting, re-creating (not sure about the best term)?
http://jsfiddle.net/jonataswalker/dbxmbxu9/
Markup
<button ng-click="create()">New Record</button>
<div data-label="Created here">
<div
id="hint-{{$index}}"
class="hint--always hint--right"
data-hint="{{row.desc}}"
ng-repeat="row in rows track by row.id"
on-render>{{row.desc}}
</div>
</div>
<div id="wrap" data-label="I'd like all to be here"></div>
JS
app.controller('ctrl', ['$scope', function($scope) {
$scope.rows = [];
$scope.count = 0;
var wrap = document.getElementById('wrap');
$scope.create = function(){
var c = $scope.count++;
$scope.rows.push({
id: c,
desc: 'dummy-desc-' + c
});
};
$scope.move = function(div){
wrap.appendChild(div);
};
}]);
app.directive('onRender', ['$timeout', function ($timeout) {
return {
restrict: 'A',
link: function (scope, element, attr) {
if (scope.$last === true) {
$timeout(function(){
scope.move(element[0]);
});
}
}
};
}]);
At the end, I had to give up using ng-repeat. Thanks for all comments.
The solution I found is to use $compile service and let the DOM manipulation freely.
http://jsfiddle.net/jonataswalker/y4j679jp/
app.controller('ctrl', ['$scope', function($scope) {
$scope.rows = [];
$scope.count = 0;
var wrap = document.getElementById('wrap');
$scope.move = function(div){
wrap.appendChild(div);
};
}]);
app.directive('button', ['$compile', function($compile){
return {
restrict: 'A',
link: function(scope){
var div = document.getElementById('c1');
scope.create = function(){
var c = scope.count++;
var row = { id: 'hint-' + c, desc: 'any desc #' + c };
var index = scope.rows.push(row) - 1;
var html = [
'<div id="'+row.id+'"',
'class="hint--always hint--right"',
'data-hint="{{rows['+index+'].desc}}"',
'data-index="'+index+'"',
'on-render>{{rows['+index+'].desc}}</div>'
].join(' ');
angular.element(div).append($compile(html)(scope));
};
}
};
}]);
app.directive('onRender', ['$timeout', function ($timeout) {
return {
restrict: 'A',
link: function (scope, element, attr) {
$timeout(function(){
scope.move(element[0]);
}, 2000).then(function(){
$timeout(function(){
scope.rows[attr.index].desc = 'changed .... ' + attr.index;
}, 2000);
});
}
};
}]);

How can I use AngularJS directive to apply css style for a string?

I have struggle with this problem for days, not knowing how to solve it.
The scenario is:
There is a string from database as QUESTION, and another string as ANSWER.
$scope.exam = {};
$timeout(function () {
$scope.exam.QUESTION = 'Find the perimeter of a rhombus whose diagonals measure 12 and 16. <br />A.10<br />B.20 <br />C.40 <br />D.80 <br />';
$scope.exam.ANSWER = 'D';
}, 500);
I use regular expression to split the choices of that QUESTION and apply css style button to the choices.
var myArray = /(^.*?)(?:<br ?\/?>)(A\s*?\.[\s\S]*?)(?:<br ?\/?>)(B\s*?\.[\s\S]*?)(?:<br ?\/?>)(C\s*?\.[\s\S]*?)(?:<br ?\/?>)(D\s*?\.[\s\S]*?)(?:<br ?\/?>)?<.*$/mg.exec(question);
If that choice is a correct answer, then apply hvr-bounce-in hvr-sweep-to-right. Otherwise, apply hvr-buzz-out style.
ans = "<br /><a ng-class=\"(scope.choiceanswer === v.substring(0, 1)) ? \'hvr-bounce-in hvr-sweep-to-right\':\'hvr-buzz-out\'\">" +v +"</a>";
But all my choices are applied by the same style hvr-bounce-in hvr-sweep-to-right.
How can I solve it?
'use strict';
angular.module('myApp', [])
.controller('View2Ctrl', function ($scope, $timeout) {
$scope.exam = {};
$timeout(function () {
$scope.exam.QUESTION = 'Find the perimeter of a rhombus whose diagonals measure 12 and 16. <br />A.10<br />B.20 <br />C.40 <br />D.80 <br />';
$scope.exam.ANSWER = 'D';
}, 500);
})
.directive('choicebutton', function ($compile, $interpolate, $timeout) {
return {
restrict: 'A',
scope: {
choiceanswer: '&',
question: '&',
choiceClass: '&'
},
replace: true,
link: function (scope, ele, attrs) {
scope.$watch(scope.question, function (question) {
if (question) {
var result = "";
var ans ='';
console.log(question);
console.log(scope.choiceanswer());
var myArray = /(^.*?)(?:<br ?\/?>)(A\s*?\.[\s\S]*?)(?:<br ?\/?>)(B\s*?\.[\s\S]*?)(?:<br ?\/?>)(C\s*?\.[\s\S]*?)(?:<br ?\/?>)(D\s*?\.[\s\S]*?)(?:<br ?\/?>)?<.*$/mg.exec(question);
console.log(myArray.length);
angular.forEach(myArray, function (v, l) {
console.log(v, l);
if (l > 0) {
console.log(v.substring(0,1));
console.log(scope.choiceanswer() == v.substring(0,1));
ans = "<br /><a ng-class=\"(scope.choiceanswer === v.substring(0, 1)) ? \'hvr-bounce-in hvr-sweep-to-right\':\'hvr-buzz-out\'\">" +v +"</a>";
console.log(ans);
result += ans;
}
});
console.log(result);
$timeout(function () {
ele.html(result);
$compile(ele.contents())(scope);
}, 0);
}
});
}
};
});
<link href="http://ianlunn.github.io/Hover/css/hover.css" rel="stylesheet"/>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<body ng-app="myApp" ng-controller='View2Ctrl'>
<div choicebutton="exam.QUESTION" class="text-info selectable" choiceanswer="exam.ANSWER" question="exam.QUESTION" choiceClass="choiceClass"></div>
</body>
Possibly in snippet i too many simplified your code, but your variant seems very complicated.
first, if question always have this format, you not need in regex and simple split work.
second, you generate string and compile it instead just use built-in template for directive.
third, in template string you use wrong scope and v, because you add it as text, so angular try get it from scope and always get undefined
'use strict';
angular.module('myApp', [])
.controller('View2Ctrl', function($scope, $timeout) {
$scope.exam = {};
$timeout(function() {
$scope.exam.QUESTION = 'Find the perimeter of a rhombus whose diagonals measure 12 and 16. <br />A.10<br />B.20 <br />C.40 <br />D.80 <br />';
$scope.exam.ANSWER = 'D';
}, 500);
})
.directive('choicebutton', function($compile, $interpolate, $timeout) {
return {
template: '<div><span>{{qText}}</span>' +
'<div ng-repeat="answer in answers">' +
' <a ng-class="{\'hvr-bounce-in hvr-sweep-to-right\':answer.selected, \'hvr-buzz-out\':!answer.selected}">{{answer.text}}</a>' +
'</div></div>',
restrict: 'A',
scope: {
choiceanswer: '=',
question: '&',
choiceClass: '&'
},
replace: true,
link: function(scope, ele, attrs) {
scope.$watch(scope.question, function(newQuestion, oldQuestion) {
if (newQuestion === undefined || newQuestion === oldQuestion) return;
var q = newQuestion.split('<br />');
scope.qText = q.shift();
scope.answers = q.map(function(item) {
return {
selected: item[0] === scope.choiceanswer,
text: item
};
});
});
}
};
});
<link href="http://ianlunn.github.io/Hover/css/hover.css" rel="stylesheet" />
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="myApp" ng-controller='View2Ctrl'>
<div choicebutton="exam.QUESTION" class="text-info selectable" choiceanswer="exam.ANSWER" question="exam.QUESTION" choiceClass="choiceClass"></div>
</div>
'use strict';
angular.module('myApp', [])
.controller('View2Ctrl', function ($scope, $timeout) {
$scope.exam = {};
$timeout(function () {
$scope.exam.QUESTION = 'Find the perimeter of a rhombus whose diagonals measure 12 and 16. <br />A.10<br />B.20 <br />C.40 <br />D.80 <br />';
$scope.exam.ANSWER = 'D';
}, 500);
})
.directive('choicebutton', function ($compile, $interpolate, $timeout) {
return {
restrict: 'A',
scope: {
choiceanswer: '&',
question: '&',
choiceClass: '&'
},
replace: true,
link: function (scope, ele, attrs) {
scope.$watch(scope.question, function (question) {
if (question) {
var result = "";
var ans ='';
console.log(question);
console.log(scope.choiceanswer());
var myArray = /(^.*?)(?:<br ?\/?>)(A\s*?\.[\s\S]*?)(?:<br ?\/?>)(B\s*?\.[\s\S]*?)(?:<br ?\/?>)(C\s*?\.[\s\S]*?)(?:<br ?\/?>)(D\s*?\.[\s\S]*?)(?:<br ?\/?>)?<.*$/mg.exec(question);
console.log(myArray.length);
angular.forEach(myArray, function (v, l) {
console.log(v, l);
if (l > 0) {
console.log(v.substring(0,1));
console.log(scope.choiceanswer() == v.substring(0,1));
ans = "<br /><a class=\"" + (scope.choiceanswer() === v.substring(0, 1) ? 'hvr-bounce-in hvr-sweep-to-right' : 'hvr-buzz-out') + "\">" +v +"</a>";
console.log(ans);
result += ans;
}
});
console.log(result);
$timeout(function () {
ele.html(result);
$compile(ele.contents())(scope);
}, 0);
}
});
}
};
});
<link href="http://ianlunn.github.io/Hover/css/hover.css" rel="stylesheet"/>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<body ng-app="myApp" ng-controller='View2Ctrl'>
<div choicebutton="exam.QUESTION" class="text-info selectable" choiceanswer="exam.ANSWER" question="exam.QUESTION" choiceClass="choiceClass"></div>
</body>
Your issue is the formatting of your ng-class attribute.
I believe you were trying to use ternary syntax however ng-class does not support such syntax. You provide it with an object hash or map {'some class to apply': scopeProperty} where scopeProperty is a value that will be evaluated and if truthy will cause the class to be applied.
To use the syntax you are you would <a class="{{angular expression}}">. This would also provide you a class that is data bound
Some example in fiddle: http://jsfiddle.net/7u9z2247/
However all of this is defunct in your implementation as your manually compiling it instead of using the template and not implementing any data binding from what i can see. So you can conditionally build your anchor string and apply the class that way, a very JQuery way of doing it.
ans = "<br /><a class=\"" + (scope.choiceanswer() === v.substring(0, 1) ? 'hvr-bounce-in hvr-sweep-to-right' : 'hvr-buzz-out') + "\">" +v +"</a>"

Custom star rating Angularjs

I am trying to do the custom star rating with angular.js, where I will have different set of images. I need to change it dynamically on hover the image. I am having 5 images
X X X X X
if I move the mouse pointer to 4th X I should be able to dynamically change
X
I used directive to achieve it.
.directive('fundooRating', function () {
return {
restrict: 'A',
template: '<ul class="rating">' +
'<li ng-repeat="star in stars" ng-class="star"
ng-click="toggle($index)"><img ng-mouseenter="hoveringOver($index)"
ng-src="{{con}}" />' +
'',
scope: {
ratingValue: '=',
max: '=',
readonly: '#',
onRatingSelected: '&'
},
link: function (scope, elem, attrs) {
var updateStars = function() {
scope.stars = [];
for (var i = 0; i < scope.max; i++) {
scope.stars.push({filled: i < scope.ratingValue});
}
};
scope.con = "Images/Rating/empty.png";
scope.hoveringOver = function(index){
var countIndex = index+1;
scope.Condition = "Good.png"
console.log("Hover " + countIndex);
};
scope.toggle = function(index) {
if (scope.readonly && scope.readonly === 'true') {
return;
}
scope.ratingValue = index + 1;
scope.onRatingSelected({rating: index + 1});
};
scope.$watch('ratingValue', function(oldVal, newVal) {
if (newVal) {
updateStars();
}
});
}
} });
How can I able to find which image my mouse pointer is and how to change the rest of Images. I want to do the custom rating option.
Angular UI gives you premade directives for the same purpose, did you try it?
http://angular-ui.github.io/bootstrap/
Go down to the Rating Title in the same page, i think it might solve your purpose.
You'll need a condition for each star in your updateStars function, either as a property for each, or a separate array. Then, you can do something like this:
scope.hoveringOver = function(index){
for (var i = 0; i <= index; i++) {
scope.stars[i].Condition = "Good.png";
}
};
Or the separate array way (assuming the array is scope.conditions):
scope.hoveringOver = function(index){
for (var i = 0; i <= index; i++) {
scope.conditions[i] = "Good.png";
}
};
You also need a function opposite of hoveringOver to remove the states to default/previous versions.

Categories