53

I have a list of outerItems. Inside each outerItem, I have a list of innerItems. They are dynamically sorted.

When mouse cursor points at one of innerItems, I have to show the popup window right above that innerItem element.

Popup div is body's child, because I do not want to have a separate popup for each of innerItems.

The way as I see it — on ng-mouseover I call the function that sets left/top properties to my absolutely positioned popup. So for each of innerItems I'd like to call jQuery .offset() method that gives me left/top values from the top-left corner of page.

So how can I get jQuery object of current scope element? Or, if I've chosen the wrong way

gorpacrate
  • 5,109
  • 3
  • 21
  • 18

2 Answers2

70

In controller:

function innerItem($scope, $element){
    var jQueryInnerItem = $($element); 
}
sudo bangbang
  • 27,127
  • 11
  • 75
  • 77
gorpacrate
  • 5,109
  • 3
  • 21
  • 18
  • 44
    you have chosen the wrong way. Dont EVER access $element in the controller. You need to wrap this as a Popup directive and then put in the pop-up's in your HTML. – ganaraj Dec 18 '12 at 10:15
  • 12
    It goes against angular's no-dom-manipulation-in-the-controller mantra. Keeps code decoupled, allows for easier testing, and keeps all dom manipulation code declarative and in the templates. That being said, sometimes you need the element. Browser autocompletes of fields, for example, fill the inputs but don't trigger ng_model updates. In those cases you sort of have to $(el).val() the input. – hurshagrawal May 13 '13 at 18:56
  • 1
    Bad, bad, bad practice – georgiosd Oct 01 '13 at 22:16
  • 9
    It's not ideal, but sometime unavoidable. Like if you're dealing with inner content delivered from a CMS. – GeekyMonkey Nov 01 '13 at 11:26
  • Or if you need to manipulate the element after it's content has been loaded in Angular. I'm trying to determine if an element is overflowing but can't tell until the directive has rendered, at which point it's too late. – daddywoodland Dec 20 '13 at 08:57
  • 1
    @daddywoodland then $scope data would change and in response to the change angular will update the dom. No need to ever use $element in the controller - if it is thought provoking I suggest that the initial design needs some reworking – Nate-Wilkins Feb 12 '14 at 17:38
  • 106
    Accessing elements in controller is completely OK if you have to deal with integrating Angular with the real world. – Den Mar 18 '14 at 16:09
  • 24
    @hurshagrawal: I hear this mantra a lot, but none of its chanters have shown me how to write a controller for a Google Map, and add custom controls without manipulating the DOM. (Having the controller delegate to a service to do it doesn't count.) angular-google-maps doesn't feature the ability to add custom controls, possibly for this very reason. If you or any of your disciples can show me, post code here. Otherwise, I'll continue to assume your mantra is as applicable to the real world as any other dogma. – Michael Scheper Jun 17 '14 at 07:40
  • 2
    Accessing $element in the controller ≠ manipulating the dom. For instance, what if you want to iterate through all the list items in a ul that is within your controller and store the contents of each in an array? – mmm Jun 19 '14 at 18:48
  • 1
    @hellaFont `ng-repeat` and `ng-model` would accomplish that no? I think there are legit uses for `$element` but not sure it's necessary there. – tobek Jul 19 '14 at 21:44
  • @tobek ng-repeat would require an array to begin with. I'm talking about going the other direction with it. – mmm Jul 20 '14 at 09:13
  • Breaking rules like this one is a little bit like drinking. You can do it, but do it responsibly. There will always be real-world examples that forces solutions that does not adhere to the convention as there aren't unlimited resources for rewriting solutions. People are waiting. Actual work cannot be done until solution X is delployed. In my view, it is in those cases the strength of a particular framework shows. If it is so brittle that *any* step away from convention makes a solution completely unmaintainable, it sucks. Thankfully, AngularJS isn't like that. – Nicklas Börjesson Apr 20 '15 at 13:13
  • Totally agree that using this in Production represents an Angular anti-pattern -- however, it just was _very_ useful for me in solving where multiple superfluous instances of a controller were coming from-- just `console.log($element)` to find that some directive had set the wrong controller. So, bad for production, bug good to know for the occasional debugging purposes. – Alexander Nied Mar 13 '17 at 17:04
7

The better and correct solution is to have a directive. The scope is the same, whether in the controller of the directive or the main controller. Use $element to do DOM operations. The method defined in the directive controller is accessible in the main controller.

Example, finding a child element:

var app = angular.module('myapp', []);
app.directive("testDir", function () {
    function link(scope, element) { 

    }
    return {
        restrict: "AE", 
        link: link, 
        controller:function($scope,$element){
            $scope.name2 = 'this is second name';
            var barGridSection = $element.find('#barGridSection'); //helps to find the child element.
    }
    };
})

app.controller('mainController', function ($scope) {
$scope.name='this is first name'
});
Giacomo1968
  • 25,759
  • 11
  • 71
  • 103
shyam_
  • 2,370
  • 1
  • 27
  • 50
  • 1
    the question is how to get html in dom. So how to get in controller string of html from some dom element by id? – fdrv Mar 30 '16 at 21:54
  • i've voted this answer down, because it's not an answer for posted question. – drdrej Jun 11 '16 at 18:12