Search code examples
javascriptangularjsangularjs-directiveangularjs-scopeangularjs-controller

Did I share state correctly outside angular directive? (parent scope / $rootScope issue)


I have two elements on my page which are not nested in some way. With attributes on the first one, I would like to set the content of the second one, like so:

<body>
    <input info="This is some info"></input>
    <div>{{info}}</div>
</body>

I know something similar can be done as described in "AngularJS - Attribute directive input value change", but in my situation the content of the attribute is the modelvalue. Moreover, I'm already using the ng-model to handle the input's value.

Finally I solved the problem like so:

<body>
    <input infobox info="This is some info"></input>
    <div>{{info}}</div>
</body>
app.directive('infobox', function() {
    return {
        restrict : 'A',
        scope : {
            info : '@info'
        },
        controller: function($rootScope, $scope) {
            $rootScope.info = $scope.info;
        }
    };
});

jsfiddle

So I'm transferring the value bypassing any isolated scope by going directly to the $rootScope, but it feels like a cop out. Essentially I don't know what controller or scope might be up the chain, since I need to be able to use this component anywhere.

One other solution is to have a parent controller for the entire page, but that would simply be a pseudo root scope again. Yet another more cumbersome solution would be to have two directives or controllers and communicate using events almost like a bus. It would work, but let's not do that.

Is there a 'clean' way to do this?

/edit

Here's a clean solution that should work (and in jsfiddle it does):

<body>
     <div info="this is some info"></div>
     <div>{{info}}</div>
</body>
app.directive('info', function() {
    return {
        restrict : 'A',
        link: function(scope, element, attrs, ngModel) {
            scope.info = attrs.info;
        }
    };
});

jsfiddle.

In my real life scenario however the value is stuck somewhere in the chain. Is it possible a parent directive with isolated scope is blocking the cascade all the way up?

/edit2

Here's a solution I thought surely would have worked. It involves three directives of which one is the parents to both others. A combination of suggested solutions in the comments:

jsfiddle

In my own code, however, it still doesn't work. As simple as it might seem, infoManager.info remains undefined.


Solution

  • OK, I got it finally. The last edit had it close, but I was reading info from the controller, not the scope the controller was for.

    Solution is as follows:

    <body>
        <div infomanager>
            <div info="'this is some info'"></div>
            <div showinfo></div>
        </div>
    </body>
    
    var app = angular.module('app', []);
    
    app.directive('infomanager', function() {
        return {
            controller: function($scope) {
                this.scope = $scope;
            }
        };
    });
    
    app.directive('info', function() {
        return {
            restrict : 'A',
            require: '^infomanager',
            scope : {
                info : '=info'
            },
            link: function(scope, element, attrs, infoManager) {
                infoManager.scope.info = scope.info;
            }
        };
    })
    
    app.directive('showinfo', function(){
        return {
            template:'{{info}}',
            require:'^infomanager'
        }
    })
    

    jsfiddle

    Here's a better example to demonstrate this with: jsfiddle