[AngularJS] Reusable directive, require from parent controller
Glorious Directives for Our Navigation
NoteWrangler navigation has now been broken into two parts: the children — nw-nav-item
— and the parent — nw-nav
.
Help the children and parent communicate by using what we have learned about $scope
and link
. They'll need to function as a nav should when clicked.
Create a default activeNav
variable on nwNav
's $scope
and make it default to'Notes'
.
angular.module('NoteWrangler') .directive('nwNav', function() { return { restrict: 'E', controller: function($scope) { $scope.activeNav = 'Notes'; } }; });
Create a function called getActiveNav
in the controller of the nw-nav
directive that returns the value of $scope.activeNav
variable.
angular.module('NoteWrangler') .directive('nwNav', function() { return { restrict: 'E', controller: function($scope) { $scope.activeNav = 'Notes'; this.getActiveNav= function(){ return $scope.activeNav; }; } }; });
Create a function called setActiveNav
on the controller of the nw-nav
directive that sets the value of $scope.activeNav
variable.
angular.module('NoteWrangler') .directive('nwNav', function() { return { restrict: 'E', controller: function($scope) { $scope.activeNav = 'Notes'; this.getActiveNav= function(){ return $scope.activeNav; }; this.setActiveNav = function(note){ $scope.activeNav = note; }; } }; });
return this to the controller:
angular.module('NoteWrangler') .directive('nwNav', function() { return { restrict: 'E', controller: function($scope) { $scope.activeNav = 'Notes'; this.getActiveNav= function(){ return $scope.activeNav; }; this.setActiveNav = function(note){ $scope.activeNav = note; }; return this; } }; });
The Parent Is Required
The nwNavItem
directive needs to be able to communicate with the parentnwNav
directive in order to tell when a nav item should be active. Let's go ahead and set that up.
Within the nwNavItem
directive, use the require
option to gain access to the controller from the parent nwNav
directive.
angular.module('NoteWrangler') .directive('nwNavItem', function() { return { restrict: 'E', templateUrl: './templates/directives/nw-nav-item.html', require: '^nwNav' }; });
Give the nwNavItem
directive a link
function. Make sure to fill in all the arguments so that we have access to the controller required from the previous task.
angular.module('NoteWrangler') .directive('nwNavItem', function() { return { restrict: 'E', templateUrl: './templates/directives/nw-nav-item.html', require: '^nwNav', link: function(scope, element, attrs, nwNavCtrl){ } }; });
Using Parent Functionality
We've created the isActive()
and activate()
functions on the scope of the nwNavItem
directive. Within these functions, we'll need to access the controller of the nwNav
directive to set and get which nav item is active.
First, we need a name for the nav item to work. Create a new isolate scope on thenwNavItem
directive and allow it to accept an attribute (@) value called name
.
angular.module('NoteWrangler') .directive('nwNavItem', function() { return { restrict: 'E', templateUrl: './templates/directives/nw-nav-item.html', require: '^nwNav', link: function(scope, element, attrs, nwNavCtrl) { scope.isActive = function() { }; scope.activate = function() { }; }, scope: { name: '@' } }; });
Within the isActive()
function, call the getActiveNav()
function from the required
controller to get the current active nav value. Compare the return value from the controller with the scope.name
value and return the result from the isActive
function.
angular.module('NoteWrangler') .directive('nwNavItem', function() { return { restrict: 'E', templateUrl: './templates/directives/nw-nav-item.html', require: '^nwNav', link: function(scope, element, attrs, nwNavCtrl) { scope.isActive = function() { return nwNavCtrl.getActiveNav()==scope.name; }; scope.activate = function() { }; }, scope: { name: '@' } }; });
We need a way to set the active nav value when a nav item is clicked. In ouractivate()
function, call the setActiveNav()
function on the require
'd controller and pass the scope.name
as an argument.
angular.module('NoteWrangler') .directive('nwNavItem', function() { return { restrict: 'E', templateUrl: './templates/directives/nw-nav-item.html', require: '^nwNav', link: function(scope, element, attrs, nwNavCtrl) { scope.isActive = function() { return nwNavCtrl.getActiveNav()==scope.name; }; scope.activate = function() { nwNavCtrl.setActiveNav(scope.name); }; }, scope: { name: '@' } }; });
The Magic Revealed
Now that we have our nav directives working, we need to hook up the templates.
We can see in the index.html
that we're already passing Notes
and Users
to thename
attribute of our nav item directive. Use data binding to display the value ofscope.name
as the content of our a
tag.
<a class="list-item" href="#/">{{name}}</a>
Give each nwNavItem
a class of active
if it isActive()
.
<a class="list-item" ng-class="{'active': isActive()}" href="#/">{{name}}</a>
On click, call the activate()
method.
<a class="list-item" ng-class="{'active': isActive()}" ng-click="activate()" href="#/">{{name}}</a>
Create an href
for each nav item with a dynamic path using the name
variable. The route should look like: #/value_of_scope_dot_name
and use data binding.
<a class="list-item" ng-class="{'active': isActive()}" ng-click="activate()" href="#/{{name}}">{{name}}</a>
Notice that routes are case sensitive. When we click on Users, it finds no route for/Users
, and therefore redirects to /notes
. Solve this issue by using a lowercase
filter on the name
binding within the route.
<a class="list-item" ng-class="{'active': isActive()}" ng-click="activate()" href="#/{{name | lowercase}}">{{name}}</a>
Since we've added an expression to the href
attribute of our a
tag, we need to change it to ng-href
attribute. Check out the docs to see why this is important.
<a class="list-item" ng-class="{'active': isActive()}" ng-click="activate()" ng-href="#/{{name | lowercase}}">{{name}}</a>