Get value of clicked nested element with element.bind - angularjs

I am using the following code in my directive and it always seems like the click is not able to keep track of my $location.$$path very well. This plug-in I am using is also using the <li></li> as a clickable item with a path which may be causing the issue. So my question would be if there a way I can skit the out elements and just focus down on the <a href="#myValue> which is a child of that <li>?
.directive('treeClick', function ($location) {
return {
restrict: 'A',
link: function (scope, element, attrs) {
element.bind("mousedown", function () {
console.log($location.$$path);
})
}
}
})
<li class="list-group-item node-tree node-selected" data-nodeid="2" style="color:#000;background-color:#eaeaea;"><span class="indent"></span><span class="indent"></span><span class="icon glyphicon"></span><span class="icon node-icon"></span>Example1</li>

I'm thinking that for what you're trying to do, you're using the wrong event. What if you tried mouseup instead of mousedown (you might also need to $timeout your processing if the update to the url is asynchronous, but I'm thinking it's not)?
Couple more things.. Angular exposes an event for when the location changes named $locationChangeSuccess
And.. $$path is an internal property, why not use the documented $location.path()
https://docs.angularjs.org/api/ng/service/$location

event.target.nodeName gave me what the element was: "A" for anchor "LI" for list item. I then extracted the value of the "A" by using event.target.nodeName.getAttribute("href"); so that I do not get the host name in the console.log()

Related

Issue on AngularJS directive link and ngHref

I will try to shorten my problem description as much as possible here.
I have a directive which looks as following
scope: false
restrict: "A"
link: function (scope, element, attributes) {
...
}
and the input parameter element looks as following
<div class="tab-container" my-directive>
<div ng-repeat="tab in $ctrl.tabs" class="tab">
<a ng-href="{{tab.name}}">
</div>
</div>
ng-repeat has been "resolved", unsure if that the correct term, however ng-href has not been resolved, that is, it hasn't turned {{tab.name}} to the value I need.
I wonder how I can tell this directive to run link, after ng-href has been resolved first.
I read something about require property, but it seems to handle controller only.
I wonder how I can tell this directive to run link, after ng-href has
been resolved first.
You can send $broadcast event from your controller to directive.
$rootScope.$broadcast('onHrefReady');
and in your directive:
link : function (scope, element, attributes) {
scope.$on("onHrefReady", function (event, args) {
// do your stuff
});
}
link is called after rendering, so you can call link content after got notification from controller

angularjs: click on a tag to invoke a function and pass the current tag to it

I need to handle a click on a tag that enables the opening of a popover.
I try to figure out the best way to do this with angularjs and naturally used hg-click.
<div ng-repeat="photo in stage.photos"
ng-click="openPopoverImageViewer($(this))"
>
$scope.openPopoverImageViewer = function (source) {
alert("openPopoverImageViewer "+source);
}
The issue is that I cannot manage to pass the $(this) to it.
Q1) How to pass the jQuery element?
Q2) In addition, ng-click sounds
to require the function being part of the controller: is it possible
to invoke a function in the partial instead?
You need to stop "thinking in jQuery" :)
Like #oori says, you can pass in photo.
Or better yet, create a custom directive. Directives is the way to go when you need new functionality in your dom, like an element that you can click to open an overlay. For example:
app.directive('popOver', function() {
return {
restrict: 'AE',
transclude: true,
templateUrl: 'popOverTemplate.html',
link: function (scope) {
scope.openOverlay = function () {
alert("Open overlay image!");
}
}
};
});
You can then use this as a custom elemen <pop-over> or as an attribute on regular HTML elements. Here is a plunker to demonstrate:
http://plnkr.co/edit/P1evI7xSMGb1f7aunh3G?p=preview
Update: Just to explain transclusion: When you say that the directive should allow transclusion (transclude:true), you say that the contents of the tag should be sent on to the directive.
So, say you write <pop-over><span>This will be passed on</span></pop-over>, then the span with "This will be passed on" is sent to the directive, and inserted wherever you put your ng-transclude element in your template. So if your pop-over template looks something like this:
<div>
<ng-transclude/>
</div>
Then your resulting DOM after the template has compiled will look like this:
<div>
<span>This will be passed on</span>
</div>
Pass it "photo"
<div ng-repeat="photo in stage.photos" ng-click="openPopoverImageViewer(photo)">
or the current $index
<div ng-repeat="photo in stage.photos" ng-click="openPopoverImageViewer($index)">

How do I add an ng-click to an element?

I have an HTML element that I cannot directly make changes to.
How would I add an ng-click event to it?
E.g.
<div id="myDiv"><img src="my/img/src/jpg"/></div>
How would I add an ng-click to the div? Jquery or vanilla JS answers, please.
Okay, I worked out that, as the element is being provided by a service, it doesn't exist on page load, so I have to try another way to do this.
The answer, in other circumstances, would be:
$("#myDiv").attr("ng-click", "myFunction()");
$compile($("#myDiv"))($scope);
Just make sure your element exists, when you try to add something to it!
If you're not restricted to using an ID on your element (...if you could use a class instead), then you could write a directive to bind the click handler.
You would write the directive to restrict it to that class:
<div class="myClass"><img src="my/img/src/jpg"/></div>
angular.module('myModule', [])
.directive('myClass', function () {
return {
restrict: 'C', // <-- restrict to classname
link: function (scope, el) {
el.on('click', function () {
// do stuff
})
}
};
})
This would bind a click to any element with myClass added to it.

Angular.js: Wrapping elements in a nested transclude

This seems like such a simple thing, but I am just not able to wrap my head around how to do it.
Here is what I want:
<my-buttons>
<my-button ng-click="doOneThing()">abc</my-button>
<my-button ng-click="doAnotherThing()">def</my-button>
</my-buttons>
That turns into something like this:
<ul class="u">
<li class="l"><button ng-click="doOneThing()">abc</button></li>
<li class="l"><button ng-click="doAnotherThing()">def</button></li>
</ul>
Notice how the ng-click is on the button, inside a wrapping li. However, the normal transclusion will place the ng-click on the li.
My best try is on this fiddle: http://jsfiddle.net/WTv7k/1/ There I have replaced the ng-click with a class, so it is easy to see when it works and not.
Any ideas of how to get this done? If it is really easy, maybe the tabs/pane example on the frontpage could be expanded to include a wrapper around the panes, while still keeping the attributes.
With replace:true the replacement process migrates all of the attributes / classes from the old element (<my-button ...>) to the new one (the root element in the template, <li ...> ). Transclude moves the content of the old element to the specified (ng-transclude) element. I'm not sure if there's a simple way to change which element in the template that will receive the migrated attributes.
To achieve what you want you could probably do some dom manipulation in a custom compile function in the my-button directive. However, I think it'd be a better idea to create a new isolate scope in the my-button directive:
<my-buttons>
<my-button click-fn="doOneThing()">abc</my-button>
<my-button click-fn="doAnotherThing()">def</my-button>
</my-buttons>
(notice I've changed ng-click to click-fn)
module.directive('myButtons', function () {
return {
restrict:'E',
transclude:true,
replace:true,
template:'<ul class="u" ng-transclude></ul>'
}
});
module.directive('myButton', function () {
return {
scope:{clickFn:'&'},
restrict:'E',
transclude:true,
replace:true,
template:'<li class="l"><button ng-click="clickFn()" ng-transclude></button></li>'
}
});
I've also made a working version of your fiddle.
To understand how the isolate scope works (scope:{clickFn:'&'}) I recommend you read the angular guide on directives.

Fire an event immediately after $scope.$digest

In my AngularJS app, there's several points at which I want to wait for a $scope to be processed into the DOM, and then run some code on it, like a jQuery fadeIn, for example.
Is there a way to listen for a "digestComplete" message of some sort?
My current method is: immediately after setting whatever $scope variables I want rendered, use setTimeout with a delay of 0 ms, so that it will let the scope finish digesting, and then run the code, which works perfectly. Only problem is, I very occasionally see the DOM render before that setTimeout returns. I'd like a method that is guaranteed to fire after digest, and before render.
In this jQuery fade-in-and-out fiddle (which I found it on the JSFiddles Examples wiki page), the author defines a "fadey" directive and performs the jQuery fadeIn (or fadeOut) in the directive's link function"
<li ng-repeat="item in items" fadey="500">
...
myApp.directive('fadey', function() {
return {
restrict: 'A',
link: function(scope, elm, attrs) {
var duration = parseInt(attrs.fadey);
if (isNaN(duration)) {
duration = 500;
}
elm = jQuery(elm); // this line is not needed if jQuery is loaded before Angular
elm.hide();
elm.fadeIn(duration)
Another possible solution is to use $evalAsync: see this comment by Miško, in which he states:
The asyncEval is after the DOM construction but before the browser renders.
I believe that is the time you want to attach the jquery plugins. otherwise
you will have flicker. if you really want to do after the browser render
you can do $defer(fn, 0);
($defer was renamed $timeout).
However, I think using a directive (since you are manipulating the DOM) is the better approach.
Here's a SO post where the OP tried listening for $viewContentLoaded events on the scope (which is yet another alternative), in order to apply some jQuery functions. The suggestion/answer was again to use a directive.
Alternatively, this example will work the same way as an AngularJS built-in ng-show directive, except it will fade-in or fade-out based on AngularJS condition:
<li ng-repeat="item in items" ng-ds-fade="{condition}">
<!-- E.g.: ng-ds-fade="items.length > 0" -->
...
myApp.directive('ngDsFade', function () {
return function(scope, element, attrs) {
element.css('display', 'none');
scope.$watch(attrs.ngDsFade, function(value) {
if (value) {
element.fadeIn(200);
} else {
element.fadeOut(100);
}
});
};
});
Source:
http://www.codeproject.com/Articles/464939/Angular-JS-Using-Directives-to-Create-Custom-Attri
If all you want is to run some jQuery stuff why not try the Angular-UI jQuery Passthrough?

Resources