Benny Bottema Benny Bottema - 4 months ago 22
AngularJS Question

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
.

Answer

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