Angular remove class on location change - angularjs

I want to remove a class when ever the user changes page, mostly because I want to collapse the bootstrap navbar. To do this I have created a directive that will remove a specified class. Whats the best way to trigger my directive on the $routeChangeSuccess event without listening to to the event within the directive as I would like to keep it flexible.
app.directive('removeClass', function() {
return {
scope: {},
restrict: 'A',
link: function(scope, element, attrs) {
//someway to trigger this method e.g. from an event listener outside the directive
scope.removeClass = function() {
element.removeClass(attrs.removeClass);
}
}
};
});

You can change a $rootScope variable with ng-class. Set that variable when there in a controller depending on page.

Related

How to use angular "element" parent or find methods to get access to the parent form?

In a angular js directive which is on the "form input leve" I want a reference to the parent form in order to access the "on submit" event.
Right now, I can query the form by Id and add the submit.
But, I want to achieve it with angula's "element" method.
But it just won't work.
angular.module('app.common').directive('validateField', ['$timeout', function ($timeout) {
return {
restrict: 'A',
require: '^form',
scope: {
error: '=validateField'
},
link: function(scope, el, attrs, form) {
if (document.getElementById('testing')){
var e = angular.element(document.getElementById('testing'))
e.on('submit', function() {
console.log('Works!');
})
};
el.parent('form').on('submit', function() {
console.log('How do i do this?');
})
}
};
}]);
Any ideas how?
UPDATE
I have found a solution, not sure it's an elegant one though:
// Validate field on "form submit".
angular.element(el[0].form).on('submit', function() {
validate();
});
Hmm, this looks 'wrong' to me. Your directive shouldn't need to know about some specific element outside of its own scope in this way. You would normally use some kind of service to synchronise data across components or provide the events/event handling that you need, or have your controller bring the two together.
Without seeing what you're trying to do in the event handler it's hard to know what you're trying to achieve, but it looks like you're just intending to do some validation. Can you not do this using some custom AngularJS validation?
https://docs.angularjs.org/guide/forms#custom-validation
You can use CSS selectors inside angular.element function,
angular.element('#myId'),
angular.element('.myClass')
With that said, you can abstract your directive even further when you are requiring the form controller, by using the $name property of it:
require: '^form',
link: function(scope,el,attrs,form) {
...
if(form.$name) {
var domForm = angular.element("[name=" + form.$name + "]")
domForm.addClass('whee');
}
}

How do I bind to a custom event in an angularjs directive?

I am having trouble catching an event which is sent like this:
document.body.dispatchEvent(event);
The directive below is on a div, inside the body:
.directive('myDirective',function(){
return {
restrict: 'A',
link: function(scope, element, attrs, pageCtrl){
element.bind('myEvent', function(){
console.log('caught my event!');
});
}
};
});
This works if the event is sent with triggerHandler on the div.
event = new Event('myEvent');
elem.triggerHandler(event);
How can I catch a custom event on the document body inside a directive?
Update:
This is for use in a Cordova app, with an existing plugin, which dispatches events on document.body. So is there another way I can catch those events in a directive on a div?
The problem is that events bubble, meaning when you dispatch the event from document.body, it propagates up through its ancestors. Since it doesn't travel down through its descendants, your directive's event listener is never fired.
You can, however, catch the event on body and fire to your div using Angular's internal event system.
Create two directives
One to decorate the body element with the following behavior:
catches the Cordova event, then...
broadcasts an Angular-internal event from rootScope
.directive('bodyDirective',function($rootScope){
return {
restrict: 'A',
link: function(scope, element, attrs){
element.bind('myEvent', function(){
$rootScope.$broadcast('cordovaEvent');
});
}
};
})
... and another to decorate the div with behavior that will catch the Angular event:
.directive('divDirective', function(){
return {
restrict: 'A',
link: function(scope, element, attrs) {
scope.$on('cordovaEvent', function(){
// do something here
});
}
}
})
Plunker (you must view the preview in a separate window and watch console there)

create "nested" ng-click inside of Angular Directive

I have a directive which open up a bootstrap-tours on call of t.start():
app.directive('tourGuide', function ($parse, $state) {
var directiveDefinitionObject = {
restrict: 'E',
replace: false,
link: function (scope, element, attrs) {
var t = new Tour({container: $("#main"),
backdrop: false,
debug:true
});
t.addStep({
element: "#main",
title: "Title123",
content: "Content123"
});
t.init();
t.start();
}};
return directiveDefinitionObject;
});
I want to create a button which on click could call variable t.start(). Is it even possible? I want to achieve this so could be independent of functions inside controllers, because this directive will be on every single view of the application, so it would be nice if it could call a parameter inside itself. Ive tryed to create a template in directive with a button, and add a ng-clikc action with t.start() and ofcourse it failed because variable t is not known to controller where ever my directive is.
EXAMPLE:
Lets say i have 2 views ShowItems and CreateItem they have 2 dirfferent controllers. in those views i have 1 button/link, on click of it i want to show my TourGuide. Thats simple.
Now in my TourGuide i have 2 different Steps, and when i press on a button in CreateItem view i want to see the step in Tour Guide for CreateItem view, and vise versa.
Thats simple if i use functions inside my controller. But is it possible to use directive ONLY, because i could have 20 different controllers?
Based on a few assumptions - I assume what you want here is to dynamically call a routine in scope from a directive. Take the following code as an example
HTML/View Code
<div my-directive="callbackRoutine">Click Here</div>
Controller
function MyController($scope) {
$scope.callbackRoutine = function () {
alert("callback");
};
}
Directive
app.directive("myDirective", function () {
return {
restrict: 'A',
link: function (scope, element, attr){
element.bind('click', function (){
if (typeof scope[attr.myDirective] == "function"){
scope[attr.myDirective]();
}
});
}
};
});
In this, you specify the callback routine as part of the directive. The key to the equation is that the scope for the directive inherits from any parent scope(s) which means you can call the routine even from the scope passed to the directive. To see a working example of this, see the following plunkr: http://plnkr.co/edit/lQ1QlwwWdpNvoYHlWwK8?p=preview. Hope that helps some!

Directive-to-directive communication in AngularJS?

I already know that you can set up a controller within a directive, and that other directives can call the functions on that controller. Here's what my current directive looks like:
app.directive("foobar", function() {
return {
restrict: "A",
controller: function($scope) {
$scope.trigger = function() {
// do stuff
};
},
link: function(scope, element) {
// do more stuff
}
};
});
I know that I could call it like this:
app.directive("bazqux", function() {
return {
restrict: "A",
require: "foobar",
link: function(scope, element, attrs, fooBarCtrl) {
fooBarCtrl.trigger();
}
};
});
However, I want to be able to call trigger from any directive, not just my own custom ones, like this:
<button ng-click="foobar.trigger()">Click me!</button>
If that doesn't work, is there a way to bring in a third directive to make it happen? Like this?
<button ng-click="trigger()" target-directive="foobar">Click me!</button>
Thanks!
Sounds like you need an angular service. http://docs.angularjs.org/guide/dev_guide.services
This will allow you to share functionality across directives.
Here's a similar question: Sharing data between directives
One simple way of accomplishing application-wide communication between any components would be to use global events (emitted from the $rootScope). For example:
JS:
app.directive('directiveA', function($rootScope)
{
return function(scope, element, attrs)
{
// You can attach event listeners in any place (controllers, too)
$rootScope.$on('someEvent', function()
{
alert('Directive responds to a global event');
});
};
});
HTML:
<button ng-click="$emit('someEvent')">Click me!</button>
Here you're emitting an event from the child scope but it will eventually reach the $rootScope and run the previous listener.
Here's a live example: http://plnkr.co/edit/CpKtR5R357tEP32loJuG?p=preview
When talking on irc it turned out that the communication is unnecessary:
I've got an attribute-restricted directive which performs some DOM manipulation on its parent element when it's "triggered"
A solution is to keep the logic inside the same directive and just to apply the dom changes to the parent.
http://jsfiddle.net/wt2dD/5/
scope.triggerSmthOnParent = function () {
element.parent().toggleClass('whatewer');
}

How to register my own event listeners in AngularJS?

How do I register my own event listeners in an AngularJS app?
To be specific, I am trying to register Drag and Drop (DND) listeners so that when something is dragged and dropped in a new location of my view, AngularJS recalculates the business logic and updates the model and then the view.
Adding an event listener would be done in the linking method of a directive. Below I've written some examples of basic directives. HOWEVER, if you wanted to use jquery-ui's .draggable() and .droppable(), what you can do is know that the elem param in the link function of each directive below is actually a jQuery object. So you could call elem.draggable() and do what you're going to do there.
Here's an example of binding dragstart in Angular with a directive:
app.directive('draggableThing', function(){
return {
restrict: 'A', //attribute only
link: function(scope, elem, attr, ctrl) {
elem.bind('dragstart', function(e) {
//do something here.
});
}
};
});
Here's how you'd use that.
<div draggable-thing>This is draggable.</div>
An example of binding drop to a div or something with Angular.
app.directive('droppableArea', function() {
return {
restrict: 'A',
link: function(scope, elem, attr, ctrl) {
elem.bind('drop', function(e) {
/* do something here */
});
}
};
});
Here's how you'd use that.
<div droppable-area>Drop stuff here</div>
I hope that helps.
Hiding event handling and dom manipulation in a directive is pretty much the the angularjs way. Calling scope.$apply when an event fires tells angular to update the view.
You might consider using jquery-ui like in this sample (see angular wiki of examples
I work with the angular-ui group and there is a simple event wrapper you might find useful.
Nice solution by Ben but keep in mind you will need to access originalEvent and original element. According to Mozilla documentation two conditions must meet https://developer.mozilla.org/en-US/docs/DragDrop/Drag_Operations
draggable is true
Listener for dragstart
So directive might look something like this
app.directive('draggableThing', function () {
return function(scope, element, attr) {
var pureJsElement = element[0];
pureJsElement.draggable = true;
element.bind('dragstart', function(event) {
event.originalEvent.dataTransfer.setData('text/plain',
'This text may be dragged');
//do something here.
});
}
});
A good step by step example is available here http://blog.parkji.co.uk/2013/08/11/native-drag-and-drop-in-angularjs.html

Resources