-->

How to update Directive on State Changes

2019-05-16 15:46发布

问题:

I have a root state that defines the overall structure of the Angular template. In the root state, I have the sidebar included that has dynamic menus via directive that changes based on the state. Like this:

.state(‘root', {
            abstract: true,
            url: ‘/root',
            templateUrl:  ‘views/root.html',
        })

root.html includes the sidebar.html that has dynamic menu called through Directive like this:

sidebar.html

  <ul class="nav" id="side-menu">
        <li class="nav-header">
                <img alt="avatar" ng-src="{{ avatar }}" />
        </li>

        <!—Dynamic Menus Directive -->
        <li sidebar-menus></li>
    </ul>

The directive shows the menu based on $state.includes(). But what happens is, the directive shows fine in the first load but it doesn’t update the directive during state changes. To resolve this, I tried the following methods but nothing worked:

  1. Added the $state to scope in Main controller but it still doesn’t change the directive once it is compiled first.
  2. Tried adding $stateChangeSuccess watcher to trigger recompiling the directive, but it doesn’t recompile again after the first time (or) maybe it is recompiling but the changes are not seen in the template (this is the code I have now which I will give below).
  3. Moving the sidebar inside separate child states instead of having it in root state works, but it beats the purpose since I am trying to load the overall structure in the root state first and only refresh the menu sections in subsequent state changes.

I am not really sure how to approach this. I have a feeling my approach can be out of whack and hoping someone can guide me here. This is my directive code at the moment:

.directive('sidebarMenus', ['$compile', '$state', '$rootScope',
    function($compile, $state, $rootScope) {

    return {
        restrict: 'A',
        replace: true,
        link: function(scope, element, attrs) {

            var state = scope.$state; // Scope from Main Controller

            // HTML Template
            function contructHtml(state) {

                var htmlText = '';

                // First Child State
                if (state.includes('root.child1')) {
                    var htmlText =  '<li>Child 1 Menu</li>';
                }
                // Second Child State
                if (state.includes('root.child2')) {
                    var htmlText =  '<li>Child 2 Menu</li>';
                }
                // Third Child State
                if (state.includes('root.child3')) {
                    var htmlText =  '<li>Child 3 Menu</li>';
                }

                $compile(htmlText)(scope, function( _element, _scope) {
                            element.replaceWith(_element);
                });

            } 

            $rootScope.$on('$stateChangeSuccess', function() {
                    var state = scope.$state; // scope.$state is added in main controller 
                    contructHtml(state);
             });

             // Initial Load 
             contructHtml(state);
        }
    }
}])

回答1:

You can get rid of the compile business by using template. You template could look something like this:

<li ng-if="state.includes('root.child1')">Child 1 Menu</li>
<li ng-if="state.includes('root.child2')">Child 2 Menu</li>
<li ng-if="state.includes('root.child3')">Child 3 Menu</li>

So your directive code should look sth like this

return {
    restrict: 'A',
    replace: true,
    template:'<div> <li ng-if="state.includes('root.child1')">Child 1 Menu</li>
              <li ng-if="state.includes('root.child2')">Child 2 Menu</li>
              <li ng-if="state.includes('root.child3')">Child 3 Menu</li>     
              </div>'
    link: function(scope, element, attrs) {

        $scope.state = scope.$state; // Scope from Main Controller

        $rootScope.$on('$stateChangeSuccess', function() {
            $scope.state = scope.$state; // scope.$state is added in main controller 
        });
    }
}