2

I've run into a problem in a tab/panel directive I'm trying to make. I need to be able to supply a template and a controller to each panel, either as a string in the HTML or as an expression to be bound. E.g. I need to be able to call it like this...

<tab-panel tab-id="tab1" 
    template="myTemplate.html" 
    controller="AdminController as adminController"
></tab-panel>

or like this

<tab-panel tab-id="tab1" 
    template="{{model.tabTemplate}}" 
    controller="{{model.tabController}}"
></tab-panel>

I'm using an ng-if inside each panel to switch the content on and off and an ng-include and ng-controller to load the content in. Here is a simplified test case of my directive.

// Tab Panel Directive Controller
.controller('TabPanelCtrl', function(){
    // removed for brevity
})

// Tab Panel Directive
.directive('s4pTabPanel', function($interpolate) {

    return {
        restrict: 'E',
        scope: {
            id: '@?tabId',
            template: '@?',
            controller: '@?',
        },
        controller: 'TabPanelCtrl as tabPanelCtrl',
        template: getTemplate
    };


    function getTemplate(element, attr) {


        // removed for brevity

        // Panel loads template and controller
        if(attr.template && attr.controller){
            return  '<tab-panel-inner ng-if="loadContent" ng-include="template" onload="onPanelLoaded()" ng-controller="controller"></tab-panel-inner>';
        }

        // removed for brevity

    }

});

So, the template attribute seems to be fine, the expression is evaluated and the resulting string is inserted into the ng-include attribute. However the ng-controller attribute doesn't like it for some reason and I get the following console error...

Error: ng:areq
Bad Argument
Argument 'controller' is not a function, got string

Anybody help fixing this would really be appreciated.

EDIT:

Just to be clear, the controller on the directive is fine, it's the ng-controller="controller" bit in the directive's template which is causing a problem as it's not getting the evaluated result of the '@' binding where the controller name is passed in.

EDIT 2:

I'm pretty sure it has something to do with the first answer here.

AngularJS: dynamically assign controller from ng-repeat

Specifically this bit:

"Your problem is that ng-controller should point to controller itself, not just string with controller's name."

The name of the controller is contained in a scope variable inside the directive but it's a string, not sure how it can be any other way if I want to keep this dynamic.

Community
  • 1
  • 1
jonhobbs
  • 26,684
  • 35
  • 115
  • 170
  • 1
    In your directive definition try leaving the controller as `controller: 'tabPanelCtrl'` and if you want to use controller as then add `controllerAs: 'tabPanelCtrl'` – stevenelberger Aug 22 '16 at 17:36
  • That's not the bit that's broken :) Please take a look at the ng-controller in the directive's template which you have to scroll right to see. – jonhobbs Aug 22 '16 at 19:49
  • maybe try controller: '=?' – gyc Aug 22 '16 at 19:52
  • That may work but then I would have to remove the {{}} and always bind it to something on the parent controller, I wouldn't have the option of just putting a string in there. – jonhobbs Aug 22 '16 at 19:59

1 Answers1

0

It seems like you want to dynamically set the controller based on the controller that is bound.

In your code here, ng-controller is bound to a string "controller". This is not the same as the controller in your scope binding.

// Panel loads template and controller
        if(attr.template && attr.controller){
            return  '<tab-panel-inner ng-if="loadContent" ng-include="template" onload="onPanelLoaded()" ng-controller="controller"></tab-panel-inner>';
        }

I think you need to do this:

...ng-controller="' + controller + '" ...

or this

...ng-controller="' + attr.controller + '" ...

so that you are accessing the controller object instead of a string.

Jinw
  • 428
  • 5
  • 10
  • Not quite. The string 'controller' is in the directive template so is actually a value on the directive's scope. That doesn't matter though as it's evaluated to a string anyway which ng-controller doesn't like. The last way works if you pass in something which doesn't need evaluating (contains no curly braces) but is no good if you do. Basically the problem is that scope.controller needs to be evaluated but not to a string. To an actually controller, which is impossible. – jonhobbs Aug 23 '16 at 10:24
  • I believe I have found a hack solution though which I will post when I get into work. – jonhobbs Aug 23 '16 at 10:25