2

Within a directive definition, there's an API for accessing $element and $attrs, so you can refer back to the element from which the directive was called. How would I access $element and $attrs when calling a custom function using a standard directive like ng-class?


EDIT: I understand that this is not an idiomatic approach. This question is applicable to rapid prototypes, which is a great use for many of Angular's features. Just not the ones that are all about sustainability, separation of concerns, etc. My primary concern is velocity. Thus, being able to bang something out quickly with the built-in directives and a quick controller method can, in fact, be a virtue, and can win the opportunity to do a fuller and more proper implementation down the road...


In this case, I'm just adding a contextual .active class to a nav element, based on the value of $location.path(), as per this post and this one. However, in those examples you need to explicitly and redundantly pass a copy of the contents of the href attribute as an argument to the getClass() function you're calling from ng-class. But isn't there a way to programmatically access the href attribute from within getClass() and avoid redundantly passing identical content as an arg?

For example, a controller with a getClass() function the way I'd imagine it could work:

function navCtrl($scope, $routeParams) {
    $scope.getClass = function($element, $attrs) {
        if ($location.path().substr(0, path.length) == $attrs.href) {
            return "active"
        } else {
            return ""
        }
    }
}

Which you could then call simply and elegantly with:

<a ng-class="getClass()" href="/tasks">Tasks</a>

rather than:

<a ng-class="getClass('/tasks')" href="/tasks">Tasks</a>

(I recognize another option is to create a custom directive that does this, but for now I'd just like to figure out if it's possible to access $attrs and/or $element from within a controller function that's been called by a directive. Thanks!)

Community
  • 1
  • 1
XML
  • 19,206
  • 9
  • 64
  • 65

3 Answers3

5

You actually can do this... BUT YOU SHOULD NOT DO THIS. lol...

Here's how to do what you're asking...

In your markup, pass $event into your function.

<a ng-click="test($event)" href="whatever">Click Me</a>

Then in your controller, get the target element of the event:

$scope.test = function($event) {
    alert($event.target.href);
};

Now here is why you probably shouldn't do this:

If you reference the DOM, or manipulate the DOM in your controller, you're endangering dependency injection and also the separation of concerns from within the Angular structure. Sure you could still inject $event as a dependency if you were testing your function, but depending on what you're doing inside of that function, you might still have ruined your DI, and you're trying to make a controller do a directive's work (which is to try to keep your controller from being tightly-coupled to your markup)

That said, if ALL your doing is just getting a value, it's probably fine, but you're still coupling your controller to your markup to some degree. If you're doing anything else with that DOM element, you're off the reservation.

Ben Lesh
  • 107,825
  • 47
  • 247
  • 232
  • 2
    Yes, this works if there is an event, but there is no event in the case of ng-class. – DaveJ Oct 26 '12 at 09:12
  • One of the great strengths of Angular is its suitability as a *rapid* development framework. I'm in the rapid prototyping business, and will crank out an app in as little as 3 days, or as long as 3 weeks. While I'm very much aware of the desirability of separation of concerns, and dependency injection, and all the rest, it's generally simply not worth the investment on such a short-fuse, one-off, limited-use project to refactor something as a custom directive if it can be done using the default Angular directives and a quick controller method. Hence, my question. – XML Dec 04 '12 at 04:39
  • It's probably faster/just as fast to do things the "right" way. It just takes longer to learn the right way to do it. It's just a few lines of code to create a directive to wire anything up. In my 15 years of experience, I've inherited way too many "rapid prototypes", "one-offs" and "limited-use" projects, and I've seen the original authors get thrown under a bus and lose business as a result. Everything in Angular is quick. Everything. If you can do it the "wrong" way, you can do it the "right" way in two more lines of code, and it's *always* worth it. Even if you're not writing unit tests. – Ben Lesh Dec 04 '12 at 05:56
  • .. in fact you've probably wasted more time trying to get just the answer you wanted out of this question than you would have just implementing it in any of the suggested ways. – Ben Lesh Dec 04 '12 at 06:00
  • 3
    A year later and... I agree with @blesh. A custom Angular directive is really the way to go. It's easier than you think, and you can bang them out in moments after you take the time to learn. Thanks for the good advice, @blesh! – XML Nov 20 '13 at 23:10
  • I'm glad someone agrees with me! :P – Ben Lesh Nov 21 '13 at 02:37
2

I don't think you can do that. The angular philosophy is to avoid accessing the DOM directly from your controller. You have already identified your two options for doing this in Angular:

  • pass in the href as a param i.e.. getClass('/tasks')
  • or, write a custom directive

Alternatively, if the class is purely presentational and doesn't affect how your application runs then you could ignore angular and use a quick and dirty jQuery function to do the job for you.

The lack of direct interaction with the DOM can be a bit strange at first but it's a godsend in the long term as your codebase grows, gets more complicated and needs more tests.

DaveJ
  • 2,357
  • 6
  • 28
  • 35
  • **The angular philosophy is to avoid accessing the DOM directly from your controller.** <-- That needs to be bolded. – Ben Lesh Oct 26 '12 at 00:52
0

Here's a completely different answer: you don't need a Angular to change styling based on href. You can use CSS selectors for that.

In your styles:

a[href="/something"] {
    background-color: red;
}

That in combination with anything you might be doing with ng-class should be everything you need.

Ben Lesh
  • 107,825
  • 47
  • 247
  • 232
  • 1
    The question isn't about how to change styling, it's about how to change styling *contingent on the Angular path*... – XML Oct 26 '12 at 11:42