Kuan
Kuan

Reputation: 11389

How to make directive talk to it parent controller or its sibling directive

All:

I am new to Angular, still trying to figure out all concepts. One thing confused me when I learning design directive:

What I want to create is an image directive( I call it "imgloader" directive), it has attribute imgurl which is passed from parent controller(let it be the "main" controller for simple purpose), and when click this image, it will show a customize style tooltip to show its link. And when this directive works with ng-repeat, all other imgloaders which have same imgurl should highlight their border, so that the user can easily know how many image are same.

The structure may like this:

<body ng-controller="main">
    <imgloader ng-repeat="url in imgurls track by $index" imgurl="url"></imgloader>
</body>

And the imgloader should look like:

<img ng-src="{{imgurl}}" ng-click="showurl_and_hilightother()" />

The imgloader directive I am thinking about is like this( please modify it to make it correct):

app.directive("imgloader", function(){
    return {
        restrict:"E",
        scope:{imgurl:"="},
        controller: function($scope){ 
            $scope.showurl_and_hilightother = function(){
                // I need to know how to do this
            }
        },
        template: "<img ng-src="{{imgurl}}" ng-click="showurl_and_hilightother()" />"
    }
})

I am thinking using $emit to let other directive know this event, but I am not sure if this is the correct way to do this(this seems not that it may need either $rootscope or some reference scope object passed from outside), could anyone give any suggestion(or code demo) how to implement this?

Thanks

Upvotes: 0

Views: 248

Answers (2)

Nicol
Nicol

Reputation: 234

<div ng-app="mayApp" ng-controller="mayController as ctrl">
    <button may-dve1 dve1="ctrl.pipeline1">Directive 1</button>
    <div may-dve2 dve2="ctrl.pipeline2">Directive 2</div>
</div>

angular.module('mayApp',[])
.controller('mayController',[function(){
    var ctrl=this;
    ctrl.pipeline1={
        do:function(){
            if(ctrl.pipeline2.do instanceof Function)
                ctrl.pipeline2.do();
        }
    };
    ctrl.pipeline2={};
}])
.directive('mayDve1',[function(){
    return {
        scope:{dve1:'='},
        link:function(scope,element){
            element.on('click',function(){
                if(scope.dve1.do instanceof Function)
                    scope.dve1.do();
            });
        }
    }
}])
.directive('mayDve2',[function(){

    return {
        scope:{dve2:'='},
        link:function(scope,element){
            scope.dve2.do=function(){
                element.text('Directive 1 Clicked');
            };
        }
    }
}]);

Upvotes: 1

domitall
domitall

Reputation: 655

If you're looking to communicate between a parent and a directive, there are a few options. If you'll have several directives contained within a parent directive, and you want to pass information back and forth... I would recommend inheriting that parent directives controller, and calling the controller form each directive with it's data.

so given you have some markup

<div ng-controller="mainCtrl">

<parent-directive>
    <child-one-directive></child-one-directive>   
    <child-two-directive></child-two-directive>  
    <child-three-directive-rules-them-all></child-three-directive-rules-them-all>
</parent-directive>

You want that parent-directive to have a controller that each of the children can inherit.

.controller('parentDirectiveController',function($scope) {

$scope.messageFromChildDirective = "some message";

this.updateParentMessage = function(value) {
     $scope.messageFromChildDirective = value;   
}

this.updateParentMessageThenPushToChildren = function(value) {
    $scope.messageFromChildDirective = value;
    $scope.$broadcast("messageChanged",value);
}
})

.directive('parentDirective', function() {

return {
    restrict: 'E',
    transclude: true,
    controller: 'parentDirectiveController',
    link: function(scope,iElement,iAttrs, ctrl) {


        scope.msg = function() {
            scope.messageFromChildDirective = "parent message";
        }


    },
    template: "<div class='parent'>" + "<div ng-transclude></div>" + 
              " <button ng-click='msg()'>Message from parent</button>"+
              "<div>{{messageFromChildDirective}}</div>"+
              "</div>"
}
}) 

The important piece in the code above is that the parentDirective has it's controller property set to the 'parentDirectiveController', this is the controller that your other directives will inherit, so anything assigned to the controller itself will be accessible by calling controller.whatever

.directive('childThreeDirectiveRulesThemAll',function() {
return {
    restrict: 'E',
    require: '^parentDirective',
    link: function(scope,iElement,iAttrs, ctrl) {

        scope.directiveThreeName = 'Child Three Directive';
        scope.$on("messageChanged", handleMessageChanged);
        function handleMessageChanged(eventName, eventData) {
            scope.directiveThreeName = eventData;
        }
        scope.msgFromThree = function() { 
            ctrl.updateParentMessageThenPushToChildren("Child Three Directive") 
        }
    },
    template: "<div class='childThree' ng-click='msgFromThree()'>{{directiveThreeName}}</div>"
}
});

notice in this directive, the require attribute it set to the name of the directive it's nested inside of. This will inherit the controller that's assigned to that directive. The controller is passed as the fourth param on the directive linking function, and anything attached to that controller can be called from there.

With this paradigm, as you can see in the directive, you can call the controller function with some value, and then the controller can then broadcast a message. Emitting bubbles out, broadcasting goes down the hierarchy, so that broadcast will only be seen by scopes created within your controllers scope. This broadcast can be listened for on any of the child directives, so that all of them could choose to update when notified of that change.

I've put together a basic jsfiddle that demonstrates a use case for this, where two child directives set their own value and the parent value, but the third child directives propagates a change to the parent as well as all of the other children.

I hope this helps

https://jsfiddle.net/m7urhzbx/

Upvotes: 1

Related Questions