What is the correct way to hide/show elements in custom directive handler?
In my application I have number of elements which should be hidden/shown based on user authentication state. To track the state I created 'Auth' service. At the moment I'm concerned that 'Auth' service is injected almost into each controller. To avoid this I decided that it would be better to create a directive which will show/hide elements based on 'Auth' service state. Something like
<div data-app-auth-only="true">Content is visible only for authenticated users</div>
I've read angular directive tutorial, created directive and set watch on Auth service. I get the handler triggered whenever Auth state is changed but I'm stuck on hiding/showing element. At some tutorials I see people use 'element.hide()' command but for some reason the hide() is not defined in my case.
Here is the link to Plunkr with my example.
I'm also concerned if the directive is the correct way here. What is the best practice for such kind of task? Does it worth to make a directive or maybe inject 'Auth' into root scope would be better?
Solution
In those tutorials, hide()
and show()
most likely comes from jQuery. To use those methods, you need to add jQuery before adding AngularJS. Then, change your directive for something like:
app.directive('appAuthOnly', ['Auth', function(Auth) { function link(scope, element, attrs) { scope.$watch(Auth.isAuth, function(value, oldValue) { if (Auth.isAuth()) { element.show(); } else { element.hide(); } }); } return { link: link }; }]);
If you don't want to add a dependency to jQuery, you can use element.addClass("my-class-name")
and element.removeClass("my-class-name")
. These two methods are included with AngularJS (jqLite). In this case, my-class-name
is a CSS class that sets display to none (display: none
).
You can look at my forked plunker for the solution. I tried to change your code as little as possible.
Editorial
In my projects, I use a directive in this scenario. I found them to be simple and flexible. If you eventually need permissions, you can pass a parameter to your directive to check for a given permission with your Auth factory. (app-auth-only="my-permission"
) At that point, I would also rename my directive to something like required-permissions="view:this, view:that
.
In my opinion, such directives doesn't make sense as a comment, class or element. So I would also restrict it for attributes only.
app.directive('appAuthOnly', ['Auth', function(Auth) { return { restrict: 'A', // Forces the directive to be an attribute. link: function link(scope, element, attrs) { scope.$watch(Auth.isAuth, function(value, oldValue) { if (Auth.isAuth()) { element.show(); } else { element.hide(); } }); } }; }]);
In both examples, I use jQuery for simplicity. In a real project, I wouldn't want to include jQuery for this. I would implement the addClass
and removeClass
solution. I found that including jQuery makes it too easy to go back to my bad jQuery habbits.
What I do is inject the service, AuthService in your case, to every relevant controller, and place on the HTML elements:
<div ng-show="showAuthContent()">Create Ticket</div>
And in the controller i'll have a function on scope:
$scope.showAuthContent = function() { // call to logic, preferably in a auth service so it can be reused // and also no business logic in controller }