3

In my directive, I'm instantiating an object.

I'd like to pass this object to the scope of the controller I associate with the directive. How do I do that?

Please keep in mind this is an isolated code for you to understand the issue. In the actual issue it won't help to instantiate that object inside of the controller.

I know that the scope object in the directive is for passing values that are specified in the HTML, I wrote it that way to help you understand what I'm trying to do.

angular.module('test', [])

.controller('test', ['$scope', function($scope) {
    alert($scope.obj); //Needs to contain {value: 'bla'}

}])

.directive('pTest', ['$compile', function($compile) {
    var object = {value: 'bla'};

    return {
        scope: {
            obj: object //how can I do that?
        },
        controller: 'test'
    };
}]);

2 Answers2

3

You can have two solution

Solution 1: use '=' in isolated scope, it binds a local/directive scope property to a parent scope property.

 .directive('ptest', ['$compile', function($compile) {
        var object = {value: 'changed value'};

        return {

          scope: {
                iobj:"="
            },
          template : "<div>{{iobj.value}}<div>",

             link: function(scope,elem,attr){
             scope.iobj=object ;
          }
        };
    }]);

in html

 <div ng-controller="testCtrl">
  <div ptest iobj="object"></div>
</div>

Solution 2: use $controller service and make testCtrl as parent and copy its all scope to controllers scope

.directive('ptest', ['$compile', function($compile,$controller) {
                var object = {value: 'changed value'};

                return {


                     controller:function($scope,$controller){

                    $controller('testCtrl', {$scope: $scope});
                       console.log($scope.object.value);
                       $scope.object = object;
                     }
                };
            }]);

working example for '=' solution 1 :

angular.module('test',[])
.controller('testCtrl',function($scope){


  $scope.object = {value:'intial value'};



})



.directive('ptest', ['$compile', function($compile) {
        var object = {value: 'changed value'};

        return {
          //replace:true,
          scope: {
                iobj:"="
            },
          template : "<div>{{iobj.value}}<div>",
            
             link: function(scope,elem,attr){
             scope.iobj=object ;
          }
        };
    }]);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="test"  ng-controller="testCtrl">
  {{object.value}}
   <div ptest iobj="object"></div>
</div>

Working example for solution 2 with $controller

  angular.module('test',[])
    .controller('testCtrl',function($scope){


      $scope.object = {value:'intial value'};



    })



    .directive('ptest', ['$compile', function($compile,$controller) {
            var object = {value: 'changed value'};

            return {
              
                
                 controller:function($scope,$controller){
                 
                $controller('testCtrl', {$scope: $scope});
                   console.log($scope.object.value);
                   $scope.object = object;
                 }
            };
        }]);
    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
    <div ng-app="test"  ng-controller="testCtrl">
      {{object.value}}
       <div ptest ></div>
    </div>
A.B
  • 20,110
  • 3
  • 37
  • 71
  • Nice solution, however, is there a way to do it without touching the HTML? –  Dec 29 '14 at 19:45
  • @RoyiBernthal let me see that too – A.B Dec 29 '14 at 20:00
  • @RoyiBernthal do you have same outer controller(parent of directive) and directive controller? – A.B Dec 29 '14 at 20:01
  • No, the parent controller is different. (and is empty for the sake of the test) –  Dec 29 '14 at 20:08
  • @RoyiBernthal so you want to mainatin controller logic sparate but still want to chnage contoller properties in directive? – A.B Dec 29 '14 at 20:10
  • @RoyiBernthal see if it is suitable for you now :) – A.B Dec 29 '14 at 20:19
  • Thanks, better suited for my needs. I could work with it, but can you think of way for me to have that property during controller initialization? (have object set to 'changed value' during initialization) –  Dec 29 '14 at 20:27
  • The issue can be seen solved since I can call an initialize function in the scope after setting the parameter. But I'm curious if there's a way to do what I mentioned in my previous comment. –  Dec 29 '14 at 21:08
  • actually directive gets initialized quickly too and changes the value to "chnaged value". you can chnaged the value on click etc not on initialization? – A.B Dec 29 '14 at 21:11
  • sice you have accepted an answer i thing problems is closed :) – A.B Dec 29 '14 at 21:11
  • I mean have it set in the original controller initialize function, since initialization naturally happens before the initialized property is passed. Have the controller initialized with the the parameter already passed, same way it happens with attributes you pass through the scope object in the directive. It is sort of closed, but out of curiosity, further answers would be much appreciated :) –  Dec 29 '14 at 21:56
3

You can do this in the link function of the direction. Since you want to set the value on the scope, you can use the scope parameter of the link function. You can also set the object on the controller, since The fourth argument (optional) argument to the link function is the controller for the directive.

.directive('pTest', ['$compile', function($compile) {
    var object = {value: 'bla'};

    return {
        controller: 'test',
        link: function(scope, elements, attrs, controller) {
           scope.obj = object;
           // or
           controller.obj = object;
        }

    };
}]);

Now that assume you don't want to isolate your scope by using a "scope" member in the return of your directive. From your example I don't think you actually want an isolated scope. (Regardless, the link function would work there too.)

Geoff Genz
  • 2,006
  • 17
  • 20
  • It looks like a very nice solution, but I can't get it to work, I assume I don't understand it properly. In both ways obj alerts null in the controller's scope. I do btw want an isolated scope. To make it clear - the controller is solely initialized by the directive, nowhere else. –  Dec 29 '14 at 19:56
  • Okay I understand, I failed to follow the logic before. The controller is naturally first initialized before being passed as an argument to the link function (missed that), and so during initialization that property doesn't exist and alerts null. I could work with it, but can you think of way for me to have that property during controller initialization? –  Dec 29 '14 at 20:20
  • The issue can be seen solved since I can call an initialize function in the scope after setting the parameter. But I'm curious if there's a way to do what I mentioned in my previous comment. –  Dec 29 '14 at 21:07
  • It depends what data you want to pass in. Almost anything you can put in the directive you can make available in the controller. For example, if you want to pass in a value from the attributes of the html, you can use the $attrs service. Why does the directive have data that's not available to the controller? – Geoff Genz Dec 29 '14 at 21:51
  • What you're saying is true, but I'm trying to understand if it's possible to have it available during the initialize phase of the controller, and not aterwards. Why? - I'm initializing an external non-angular class and embedding an element it returns via directive. Trying to find the cleanest way to do it. –  Dec 29 '14 at 23:13