Accessing clicked element in angularjs
Asked Answered
A

1

172

I'm relatively new to AngularJS and suspect I'm not grasping a concept. I'm also using Twitter Bootstrap and I've got jQuery loaded.

Workflow: User clicks a link from a list, "master" section is updated and link user clicked on gains active class.

Basic HTML Markup:

<ul class="list-holder" ng-controller="adminController">
   <li><a ng-click="setMaster('client')">Clients</li>
   <li><a ng-click="setMaster('employees')">Employees</li>
   <li><a ng-click="setMaster('etc')>Etc...</li>
</ul>

Doing this in jQuery:

jQuery(".list-holder").on('click', 'a', function(event){
    event.preventDefault();
jQuery(".list-holder li").removeClass('active');
jQuery(this).parent('li').addClass('active');
});

But I can't figure out how to integrate Angular and jQuery to get this done, because I'm using Angular to fetch the master list (in JSON form) from the server and update a list on the page.

How do I integrate this? I can't seem to find the element I've clicked on once I'm inside the angular controller function

Controller:

function adminController($scope)
    {    
        $scope.setMaster = function(obj)
        {
            // How do I get clicked element's parent li?
            console.log(obj);
        }
    }
Aplomb answered 14/9, 2012 at 19:28 Comment(0)
D
283

While AngularJS allows you to get a hand on a click event (and thus a target of it) with the following syntax (note the $event argument to the setMaster function; documentation here: http://docs.angularjs.org/api/ng.directive:ngClick):

function AdminController($scope) {    
  $scope.setMaster = function(obj, $event){
    console.log($event.target);
  }
}

this is not very angular-way of solving this problem. With AngularJS the focus is on the model manipulation. One would mutate a model and let AngularJS figure out rendering.

The AngularJS-way of solving this problem (without using jQuery and without the need to pass the $event argument) would be:

<div ng-controller="AdminController">
    <ul class="list-holder">
        <li ng-repeat="section in sections" ng-class="{active : isSelected(section)}">
            <a ng-click="setMaster(section)">{{section.name}}</a>
        </li>
    </ul>
    <hr>
    {{selected | json}}
</div>

where methods in the controller would look like this:

$scope.setMaster = function(section) {
    $scope.selected = section;
}

$scope.isSelected = function(section) {
    return $scope.selected === section;
}

Here is the complete jsFiddle: http://jsfiddle.net/pkozlowski_opensource/WXJ3p/15/

Dactyl answered 14/9, 2012 at 20:1 Comment(12)
FYI: $event doesn't work unless it's passed in the markup: ng-click="setMaster(section, $event)" Just a heads up.Swoop
Is there a link to any AngularJs doc about this?Cachou
@blesh Is there another way to get $event object?Bourgeois
Not really, it has to be passed in to the function. In reality, though, it's probably not the best idea to reference dom-specific stuff like this in your controller. Generally when $event is used, it's in the context of stopping propagation or the like: <a ng-click="doSomething(); $event.stopPropagation()">Click Just Me</a>Swoop
I had a problem using the $event.target because I had a icon inside my button. So, sometimes the target result is the button and sometimes is the icon (depending where I clicked). I used $event.currentTarget instead of target and it worked like a charm.Bouldin
Please note that you only need to pass the $event argument if you plan to do low-level DOM manipulation. This argument is definitively not needed if you approach the problem AngularJS-way.Dactyl
You can access DOM element in your function without passing $event from view, check my solution below.Ornithomancy
Another use case for needing $event.target is for manipulating focus for accessibility. Unfortunately, there is no "angular way" when it comes to accessibility as the authors have largely ignored it.Synergy
thank you! i didnt know before that ng-directive function can get the param $eventAfar
Though using $event.target like this may not be the "angular way" I feel like having a large ng-repeat list with every item needing a listener to evaluate a change is not efficient? Clicking one element would mean every item in the entire list must be re-evaluated right? -- why not just target that one item with $event.target to toggle a CSS class for example. What do you think? I'm working on a Phonegap App and need to squeeze every performance tweak I can.Holst
I would also like to recommend using $event.currentTarget instead of $event.target. If the element with ng-click has child elements, if the child element is clicked $event.target becomes the child element. $event.currentTarget will always target the element with the designated ng-click.Cool
@BradleyFlood is correct. I won't disagree that $event.target isn't the angular way, but the angular way showcased here is terribly inefficient. isSelected gets fired a multiple of times for every element ng-repeat iterates through. After app init it's 3*n and for every onclick it's 2*n. So in the example above with only 3 elements, when it loads and you've clicked once, isSelected has fired a total of 18 times. Now scale that up to a real production app with a real list. Sometimes the angular way isn't always the best way.Foregoing

© 2022 - 2024 — McMap. All rights reserved.