Question

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.

Was it helpful?

Solution 3

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

OTHER TIPS

There is another approach I used recently with a similiar situation (also I do revert to "rootscope jail break" and "event emit/listen" patterns to solve other problems) that might work for you: lets say we have this directive:

app.directive('infobox', function() {
    return {
        restrict : 'A',
        scope : {
            info : '@info'
        },
        controller: function($rootScope, $scope) {
            //$rootScope.info = $scope.info;
            this.setInfo = function(data){
              $scope.info = data;
            }
            this.getInfo = function(){
                return $scope.info;
            }

        }
    };
})

now I can add another directive for showing {{info}}:

with the html:

app.directive('showinfo', function(){
   return {
    restrict:'A',
    template:'<div>{{info}}</div>',
    require:'^infobox', //this is where the magic happens
    link:function(scope,element,attrs,parentCtrl){ //notice that now er have a "parentctrl, with everything that is on 'this' in the required direcive
//you can set a watch for this etc..
  scope.info = parentCtrl.getInfo();
  }

 }

})

hope this helps!

Write a third directive, which would act either as a marker for the scope which needs to know about the variables, or as an alternative to scope. Either way, you need the third directive to be above the {{info}} and infobox, and you need to the two bottom directives to require it as a parent.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top