Query selector in nested AngularJS Directives - javascript

I'm using two directives in this HTML code:
<div animations>
<h2>Title</h2>
<span class="animateBtn">Animate!</span>
<info-section></info-section>
</div>
The first, is an Attribute Directive:
angular.module('app').directive('animations', ['$window', ($window: any) => {
return {
restrict: "A",
link: function ($scope: any, element: any, attrs: any) {
angular.element(document).ready(() => {
let animateBtns = angular.element(element[0].querySelector('.animateBtn'));
if (animateBtns && animateBtns.length) {
for (let i = 0, animateBtnsLength = animateBtns.length; i < animateBtnsLength; i++) {
let currentBtn = animateBtns[i];
currentBtn.addEventListener("click", function () {
.... other code....
});
}
}
..... other code .....
});
}
};
}])
So, it simply does a querySelector to select all buttons that, at the click, have to start a certain function.
And it works. The problem is that the second directive also contains an "animateBtn":
.directive('infoSection', function() {
return {
replace: true,
restrict: 'E',
template: '<div><span class="animateBtn">Animate with this too</span></div>'
}
});
The problem is that in the first directive (even if I user (document).ready()), the selector returns just one element (the span under the title), and it doesn't include the "animateBtn" of the second directive.
Here you can find the full code: PLNKR

With AngularJS one uses directives to attach code to elements instead of query selectors:
app.directive("animateBtn", function($window) {
return {
restrict: 'C',
link: postLink
};
function postLink (scope, elem, attrs) {
elem.on('click', function() {
.... other code....
});
.... other code....
}
})
The above directive will attach the click handler and associated code to each element with the class animateBtn when the element is added to the DOM by the AngularJS framework.
if a write a function inside "animations" directive, how can I trigger it inside "animatBtn" directive? I mean, in your code, inside the first line of "..... other code...." how can I call a function written in the "animations" directive?
Use the require property of the DDO to access the controller of the animations directive:
app.directive("animateBtn", function($window) {
return {
restrict: 'C',
link: postLink,
require: '^animations'
};
function postLink (scope, elem, attrs, animations) {
elem.on('click', function() {
.... other code....
});
//call animations API
animations.someMethod(args);
}
})
In the animations directive:
app.directive("animations", function() {
return {
controller: ctrl
}
function ctrl($element, $scope) {
this.someMethod = function(args) {
//code here
};
}
})
For more information, see AngularJS Comprehensive Directive API Reference - require

Related

what if I want to create directive that match only for custom element && atribute

let assume that I want to create directive that matched only for element that match amInput[type=dropdown] how can I do that?
I can for example:
.directive('amInput',function () {
return {
restrict: "E",
scope: {
data:'#'
},
compile:function(tElement, tAttrs){
if (tAttrs.type != 'dropdown') return;
return function link ($scope, element, attr, ctrl) {
var parseResult = parse($scope.data);
}
}
}
});
but if I define another directive with isolate scope for am-input[type=checkbox]
.directive('amInput',function () {
return {
restrict: "E",
scope: {
data2:'#'
},
compile:function(tElement, tAttrs){
if (tAttrs.type != 'checkbox') return;
return function link ($scope, element, attr, ctrl) {
var parseResult = parse($scope.data2);
}
}
}
});
angular#$compile throw exception about two directives define isolate scope.
Error: [$compile:multidir] Multiple directives [amInput, amInput] asking for new/isolated scope on: <am-input type="checkbox"></am-input>
directive name should be unique (as long as they match the same restrict)
in your case you should probably merge them into one.
(just as a reference, this might help: Angular directive name: only lower case letters allowed?)
Ok, I end up with that solution that make the differntes between the directives by postlink. when prelink is for everythig is similar between them:
directive('amInput',function () {
var template = {
'dropdown' :require('./dropdown.drv.html'),
'checkbox-group' :require('./checkbox-group.drv.html')
};
var postLinkPerType = {
'dropdown': require('./postLink-OneFromMany'),
'checkbox-group':require('./postLink-ManyFromMany')
};
return {
restrict: "E",
require: 'ngModel',
scope:{
selectAll:'='
//,...
},
transclude:true,
controller: function(scope, element, attr) {
/*for binding stuff that use by post links */
},
controllerAs:'inputCtrl',
bindToController:false,
template: function (element, attr) {
return template[attr.type];
},
compile: function (element, attr, ctrl, transcludeFn) {
return {
pre: function (scope, element, attr, ctrl, transcludeFn) {
/*******************************************/
/* some general/init code for all collection*/
/*******************************************/
/* init options list */
if( attr.data ){
/***********/
/* some code just if there is some attribute*/
/***********/
}
},
/*the little different code that make every directive in collection different*/
/*different by TYPE attribute*/
post: postLinkPerType[attr.type]
}
}
}
})

AngularJS: Longpress directive not working with function in controller's view on nested directives

The onLongPress directive was working before I had to make an adjustment to my directives structures.
Now it's not working and I can't find why, perhaps someone can help me find my error.
The onLongPress should call a method from the controller assigned to the view, it doesn't, but if I add a $scope.someFn = function(); inside the directive, it works, but I need to call a method from the view's controller.
How can I solve this?
The example is the next:
<my-tabs>
<tab-one></tab-one>
<tab-two></tab-two>
...
</my-tabs>
Inside each of this tabs I have lists.
Example inside template:
<ul>
<li ng-click="doSomething()" on-long-press="doOtherthing()">
Some nice item.
</li>
</ul>
Directives are as follow:
tabOne
app.directive('tabOne', function() {
return {
restrict: 'E',
scope: false,
transclude: false,
templateUrl: 'templates/tab-one.html'
};
});
myTabs
app.directive('myTabs', function() {
return {
restrict: 'E',
templateUrl: 'templates/my-tabs.html',
scope: {
myMenues: '=',
selectedTab: '=',
general: '=',
userPoints: '=',
changetab: '&ngClick'
},
controller: function($scope) {
// Some code to add functionality. Nothing fancy.
}
};
});
onLongPress
app.directive('onLongPress', function($timeout) {
return {
restrict: 'A',
link: function($scope, $elm, $attrs) {
$elm.bind('touchstart', function(evt) {
// Locally scoped variable that will keep track of the long press
$scope.longPress = true;
// We'll set a timeout for 600 ms for a long press
$timeout(function() {
if ($scope.longPress) {
// If the touchend event hasn't fired,
// apply the function given in on the element's on-long-press attribute
$scope.$apply(function() {
$scope.$eval($attrs.onLongPress)
});
}
}, 600);
});
$elm.bind('touchend', function(evt) {
// Prevent the onLongPress event from firing
$scope.longPress = false;
// If there is an on-touch-end function attached to this element, apply it
if ($attrs.onTouchEnd) {
$scope.$apply(function() {
$scope.$eval($attrs.onTouchEnd)
});
}
});
}
};
});
UPDATE:
If I add $scope.doOtherthing = function() {}; into myTabs directive it works, but as I said, I need to get that function from the view's controller :/
Thanks.

Rewriting directives to not use isolate scope?

I have two simple directives that I want to apply to an element.
This directive gives focus to a particular element when a condition changes to be true:
.directive('focusOnCondition', function ($timeout) {
return {
restrict: 'A',
scope: {
condition: '=focusOnCondition',
},
link: function (scope, el, attr) {
scope.$watch('condition', function (value) {
if (value) {
$timeout(function () {
el[0].focus();
});
}
});
}
};
});
This directive calls a function when the user presses 'enter':
.directive('triggerOnEnter', function () {
return {
restrict: "A",
scope: {
func: '&triggerOnEnter'
},
link: function (scope, el, attr) {
$(el).keypress(function (event) {
if (event.which == 13) scope.func();
});
}
};
})
These two directives work separately, but they don't work if I try to apply them to the same element. For example:
<form ng-show="pastingResults">
<textarea placeholder="Paste results and press 'enter'"
ng-model="pastedResultData"
trigger-on-enter="pasteResults()"
focus-on-condition="pastingResults"></textarea>
<button type="submit" ng-click="pasteResults()">Submit</button>
</form>
Result in an error, [$compile:multidir] Multiple directives (...) on (...).
How can I resolve this? I am new to Angular JS, and 'put it in an isolated scope' is the only thing I know how to do when making directives. I assume it must be possible to implement these without using an isolated scope, but I don't know how.
Can anyone give an example?
If you remove the isolated scopes, you can access the options by looking at the attributes on the element directly using the attrs object passed to the linking function, together with $eval to evaluate it if needs be:
app.directive('focusOnCondition', function ($timeout) {
return {
restrict: 'A',
link: function (scope, el, attr) {
scope.$watch(attr.focusOnCondition, function (value) {
if (value) {
$timeout(function () {
el[0].focus();
});
}
});
}
};
});
and
app.directive('triggerOnEnter', function () {
return {
restrict: "A",
link: function (scope, el, attr) {
$(el).keypress(function (event) {
if (event.which == 13) {
scope.$eval(attr.triggerOnEnter);
}
});
}
};
});
These can be seen working at http://plnkr.co/edit/1iOBNVlmRxm8HGRrObBd?p=preview

angular directive to call scope method on click event

I have a grid with button that has k-grid-cancel-changes class. I would like to create a directive that will attach a click event to that button and call method on the page scope
.directive('kGridCancelChanges', function () {
return {
restrict: 'C',
scope: {
onCancelChanges: "&"
},
controller: function ($scope, $element, $attrs, $location) {
$element.click(function () {
$scope.onCancelChanges();
});
}
}
});
When I press button I can see $scope.onCancelChanges() fired from my directive but it never reaches function on the page scope.
$scope.onCancelChanges = function () {
alert('test');
}
I would appreciate any suggestions
If you want to call a function in the scope it has to be provided like this:
<button class="k-grid-cancel-changes" on-cancel-changes="onCancelChanges()">test</button>
Demo: http://plnkr.co/edit/8vQ1wmdriGrDFGZwhqW2?p=preview
If for some reason you can't modify HTML code (say, it's rendered dynamically by Kendo) and can't add attribute, then you can only access the function to call via $parent scope reference:
$scope.$parent.onCancelChanges();
Demo: http://plnkr.co/edit/tpEEZs9VQunKXABud9yN?p=preview
And finally, if it's not principal to have isolated scope for your directive then you can simply call the function as it's the same scope:
.directive('kGridCancelChanges', function() {
return {
restrict: 'C',
controller: function($scope, $element, $attrs, $location) {
$element.click(function() {
$scope.onCancelChanges();
});
}
}
});
Demo: http://plnkr.co/edit/0OmlCJ6SgYU2GQRyBgYj?p=preview
You can create you directive like this:
app.directive('myDir', function () {
return {
restrict: 'C',
scope: {
foo: '&'
},
link: function(scope,elem){
elem.on('click',function(){
scope.foo();
});
}};
});
or use controller function instead of link if you need:
controller: function($scope,$element){
$element.on('click',function(){
$scope.foo();
});
}};
Note that angular's jqLite has no element.click function.
Here is fiddle:
http://jsfiddle.net/cxo77xb4/2/

Updating the element in the link function propagates the change across all instances of the directive

This is the skeleton of my directive:
define(function () {
return [
'$deps',
function ($deps) {
return {
restrict: 'A',
link: function ($scope, $el, attrs) {
attrs.$observe('attr-name', function (attrValue) {
// process the attrvalue, and come up with the href
$el.attr('href', processedResult);
});
}
};
}
]
})
Problem being, when I use the $el.attr(), it is updating all the instances of the directive (all the tags get the href); why might this be happening?

Categories