4

Lets say I have the following HTML structure:

<div ng-app="testApp">
    <div ng-controller="controller1">
        {{controller1}}
    </div>
    <div ng-controller="controller2">
        {{controller2}}
    </div>
    <div ng-controller="controller3">
        {{controller3}}

        <test-directive></test-directive>
    </div>
</div>

Inside ng-controller="controller3" there is a custom directive.

The directive is as followed:

ang.directive("testDirective", function() {
    return {
        restrict: 'AE',
        link: function (scope, elem, attrs) {
            console.log(scope);
        },
        template: "<h3>I return $scope.controller3</h3>"
    }
});

The $scope now holds the data from ng-controller="controller3". How can i link a custom controller to it? So instead the directive passes its parent controller, I want too pass for example controller1 its data.

I have to be able to pass any controller to it, because the directive is depended on the data inside a given controller.

I cannot replace the <test-directive> inside the required controller

Here a fiddle you can play with

var ang = angular.module('testApp', []);

ang.controller("controller1", controller1);
function controller1($scope) {
 $scope.controller1 = "controller1";
}

ang.controller("controller2", controller2);
function controller2($scope) {
 $scope.controller2 = "controller2";
}

ang.controller("controller3", controller3);
function controller3($scope) {
 $scope.controller3 = "controller3";
}

ang.directive("testDirective", function() {
 return {
  restrict: 'AE',
  link: function (scope, elem, attrs) {
   console.log(scope);
  },
    template: "<h3>I return $scope.controller3</h3>"
 }
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.5/angular.min.js"></script>
<div ng-app="testApp">
 <div ng-controller="controller1">
  {{controller1}}
 </div>
 <div ng-controller="controller2">
  {{controller2}}
 </div>
 <div ng-controller="controller3">
  {{controller3}}
  
  <test-directive></test-directive>
 </div>
</div>

Real case scenario:

I made a pagination directive. You can call the directive by placing <ng-pagination></ng-pagination> anywhere on the page.

The directive creates a pagination from a given dataset. The dataset however can hold anything. It doens't depend on values or whatever.

There is only one requirement for the directive. It looks inside the controller if it has a $scope called $scope.result. The $scope.result gets filled with data from an API call.

The pagination then recreates a object with 10 results per page.

The problem I have is: The ng-controller can be anywhere on the page also can the <ng-pagination></ng-pagination>.

What i want to archieve is something like this:
I assign an data attribute to the [ng-pagination]. Called: data-controller. You can then pass the controller name to it. The directive then has access to all the data inside that controller.

It would then look like this:
<ng-pagination data-controller="controller1" show-next="true" ...></ng-pagination>


UPDATE

I found out that you can assign a controller by doing:

ang.directive("directive", function() {
    return {
        controller: "[CONTROLLER NAME]"
    }
});

I though, perhaps this is possible:

ang.directive("directive", function() {
    return {
        controller: "{{controller}}",
        link: function (scope, elem, attrs) {
            $scope.controller = "[CONTROLLER NAME]"
        }
    }
});

However, it gives me the error:

Error: ng:areq Bad Argument
Argument '{{controller}}' is not a function, got undefined

Red
  • 6,599
  • 9
  • 43
  • 85
  • Take a look at [scope properties](https://docs.angularjs.org/api/ng/type/$rootScope.Scope) - You need to access `$scope.$parent` – Alon Eitan May 18 '17 at 10:22
  • Thanks for the awnser. I cannot use `$parent`. Its not as simple as that. Because the `controller` can be anywhere on the page. Also, if you look the snippet you can see the `$parent` doens't hold any `controller` data. As far as i can see. – Red May 18 '17 at 10:24
  • Oh, I thought they are nested. So you can't pass `controller1` from `controller3` because they have completely different scope, you could use `$rootScope` which I **don't recommend**, but you can use a factory - Let me know if [this question](http://stackoverflow.com/questions/21919962/share-data-between-angularjs-controllers) helps, and I'll mak yours as duplicate – Alon Eitan May 18 '17 at 10:43
  • Thanks for the reply, the link to the question im familair with. But its not about sharing data between controllers or any kind. What i need is some `$scope` variables. For example: `$scope.data`. So what i want is, when you call the directive, you asign a `controller` to it, which has the `$scope.data`. The `directive` is depending on that variable. Is there no way i can do someting like `controller="controller1"`? – Red May 18 '17 at 11:22
  • 1
    See updated question. Explained the real case scenario. – Red May 18 '17 at 11:36
  • Since you need only $scope.data, can't you create a rootScope variable (or even better, a common controller) in which you handle the data you want, and then you create scope.data to pass to the directive itself? – Nick May 18 '17 at 12:00
  • As for `$rootScope`. I perhaps can play around with it. As far as i know, and as @AlonEitan stated, using `$rootScope` is not recommended. Regarding you question about a global controller which handles the data. I can't do that. There are 100+ controllers, to different API calls :( – Red May 18 '17 at 12:08

1 Answers1

1

I was playing with controller as seen in my update, and reading this guide. I came up with below solution

So how can you pass any controller to a directive?

  • You first have to isolate the scope with scope: {}
  • Set the controller property inside the directive with controller: "@". You can now assign values to it with the next step
  • Set the property name: "controller". Ill explain how to use this below.

I have been told isolating the scope is necessary, it does work without however.

If you now place the directive you can now assign any controller to it by doing:

<ng-pagination data-controller="[CONTROLLER NAME]>

The name of the property name doens't matter. So if you want to call it, assign-any, you can pass that attribute by setting the property name as name: assignAny.

var ang = angular.module('testApp', []);

ang.controller("controller1", controller1);
function controller1($scope) {
 $scope.controller1 = "Yay, im now part of the directive!!!";
}

ang.controller("controller2", controller2);
function controller2($scope) {
 $scope.controller2 = "controller2";
}

ang.controller("controller3", controller3);
function controller3($scope) {
 $scope.controller3 = "controller3";
}

ang.directive("testDirective", function() {
 return {
  restrict: 'AE',
  link: function (scope, elem, attrs) {
      scope.controller = attrs.controller;
   console.log(scope);
  },
    template: "<h3>I return {{controller}}</h3>",
    controller: "@",
    name: "controller",
 }
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.5/angular.min.js"></script>
<div ng-app="testApp">
 <div ng-controller="controller1">
  {{controller1}}
 </div>
 <div ng-controller="controller2">
  {{controller2}}
 </div>
 <div ng-controller="controller3">
  {{controller3}}
  
  <test-directive data-controller="controller1"></test-directive>
 </div>
</div>

I recommend to read this guide for a well explained tutorial on how to work with directives.

Part one and Part two

Red
  • 6,599
  • 9
  • 43
  • 85