AngularJS:从ng-repeat动态分配控制器

时间:2012-12-19 01:21:04

标签: javascript dynamic controller angularjs assign

我正在尝试为包含的模板动态分配控制器,如下所示:

<section ng-repeat="panel in panels">
    <div ng-include="'path/to/file.html'" ng-controller="{{panel}}"></div>
</section>

但Angular抱怨{{panel}}未定义。

我猜测{{panel}}未定义尚未(因为我可以在模板中回显{{panel}})。

我见过很多人设置ng-controller等于变量的例子:ng-controller="template.ctrlr"。但是,如果没有创建重复的循环循环,我无法弄清楚当{{panel}}需要时如何使ng-controller的值可用。

P.S。我也尝试在我的模板中设置ng-controller="{{panel}}"(认为它必须在那时解决),但没有骰子。

5 个答案:

答案 0 :(得分:16)

你的问题是ng-controller应该指向控制器本身,而不仅仅是指向控制器名称的字符串。

所以你可能想把$ scope.sidepanels定义为带有控制器指针的数组,像这样,也许:

$scope.sidepanels = [Alerts, Subscriptions];

以下是js fiddle http://jsfiddle.net/ADukg/1559/

的工作示例

但是,当您想要在ngRepeat中设置控制器时,我发现这种情况非常奇怪。

答案 1 :(得分:6)

要在模板中动态设置控制器,有助于引用与控制器关联的构造函数函数。控制器的构造函数是您传入Angular module APIcontroller()方法的函数。

这有帮助,因为如果传递给ngController指令的字符串不是已注册控制器的名称,则ngController会将字符串视为要在当前范围上计算的表达式。此范围表达式需要求值为控制器构造函数

例如,假设Angular在模板中遇到以下内容:

ng-controller="myController"

如果没有注册名称为myController的控制器,则Angular将查看当前包含控制器中的$scope.myController。如果此键存在于作用域中,并且相应的值是控制器构造函数,则将使用控制器。

ngController文档中对参数值的描述中提到了这一点:“全局可访问的构造函数的名称或当前作用域上的表达式求值为构造函数。” Angular源代码中的代码注释更详细地说明了这一点here in src/ng/controller.js

默认情况下,Angular不容易访问与控制器关联的构造函数。这是因为当您使用Angular module APIcontroller()方法注册控制器时,它会隐藏您在私有变量中传递它的构造函数。您可以在$ControllerProvider source code中看到此处。 (此代码中的controllers变量是$ControllerProvider私有的变量。)

我对此问题的解决方案是创建一个名为registerController的通用帮助程序服务,用于注册控制器。注册控制器时,此服务会向控制器公开控制器构造函数。这允许控制器以正常方式和动态使用。

以下是我为registerController服务编写的代码:

var appServices = angular.module('app.services', []);

// Define a registerController service that creates a new controller
// in the usual way.  In addition, the service registers the
// controller's constructor as a service.  This allows the controller
// to be set dynamically within a template.
appServices.config(['$controllerProvider', '$injector', '$provide',
  function ($controllerProvider, $injector, $provide) {
    $provide.factory('registerController',
      function registerControllerFactory() {
        // Params:
        //   constructor: controller constructor function, optionally
        //     in the annotated array form.
        return function registerController(name, constructor) {
            // Register the controller constructor as a service.
            $provide.factory(name + 'Factory', function () {
                return constructor;
            });
            // Register the controller itself.
            $controllerProvider.register(name, constructor);
        };
    });
}]);

以下是使用该服务注册控制器的示例:

appServices.run(['registerController',
  function (registerController) {

    registerController('testCtrl', ['$scope',
      function testCtrl($scope) {
        $scope.foo = 'bar';
    }]);

}]);

上面的代码在名称testCtrl下注册控制器,它还将控制器的构造函数公开为名为testCtrlFactory的服务。

现在,您可以以通常的方式在模板中使用控制器 -

ng-controller="testCtrl"

或动态 -

ng-controller="templateController"

要使后者工作,您必须在当前范围内拥有以下内容:

$scope.templateController = testCtrlFactory

答案 2 :(得分:5)

我相信你遇到了这个问题,因为你正在定义这样的控制器(就像我以前那样):

app.controller('ControllerX', function() {
    // your controller implementation        
});

如果是这种情况,你不能简单地使用对ControllerX的引用,因为控制器实现(或'Class',如果你想称之为)不在全局范围内(而是存储在申请$controllerProvider)。

我建议您使用模板而不是动态分配控制器引用(甚至手动创建它们)。

<强>控制器

var app = angular.module('app', []);    
app.controller('Ctrl', function($scope, $controller) {
    $scope.panels = [{template: 'panel1.html'}, {template: 'panel2.html'}];        
});

app.controller("Panel1Ctrl", function($scope) {
    $scope.id = 1;
});
app.controller("Panel2Ctrl", function($scope) {
    $scope.id = 2;
});

模板(模拟)

<!-- panel1.html -->
<script type="text/ng-template" id="panel1.html">
  <div ng-controller="Panel1Ctrl">
    Content of panel {{id}}
  </div>
</script>

<!-- panel2.html -->
<script type="text/ng-template" id="panel2.html">
  <div ng-controller="Panel2Ctrl">
    Content of panel {{id}}
  </div>
</script>

查看

<div ng-controller="Ctrl">
    <div ng-repeat="panel in panels">
        <div ng-include src="panel.template"></div>        
    </div>
</div>

jsFiddle:http://jsfiddle.net/Xn4H8/

答案 3 :(得分:5)

另一种方法是不使用ng-repeat,而是使用指令将它们编译成存在。

<强> HTML

<mysections></mysections>

<强>指令

angular.module('app.directives', [])
    .directive('mysections', ['$compile', function(compile){
        return {
            restrict: 'E',
            link: function(scope, element, attrs) {
                for(var i=0; i<panels.length; i++) {
                    var template = '<section><div ng-include="path/to/file.html" ng-controller="'+panels[i]+'"></div></section>';
                    var cTemplate = compile(template)(scope);

                    element.append(cTemplate);
                }
            }
        }
    }]);

答案 4 :(得分:-1)

好的我认为这里最简单的解决方案是在文件模板上明确定义控制器。假设你有一个数组:

$scope.widgets = [
      {templateUrl: 'templates/widgets/aWidget.html'},
      {templateUrl: 'templates/widgets/bWidget.html'},
];

然后在您的html文件中:

<div ng-repeat="widget in widgets">
    <div ng-include="widget.templateUrl"></div>
</div>

解决方案aWidget.html:

<div ng-controller="aWidgetCtrl">
   aWidget
</div>

bWidget.html:

<div ng-controller="bWidgetCtrl">
   bWidget
</div>

简单就是这样!您只需在模板中定义控制器名称即可。由于你将控制器定义为bmleite说:

app.controller('ControllerX', function() {
// your controller implementation        
});
那么这是我能提出的最好的解决方法。这里唯一的问题是如果你有50个控制器,你必须在每个模板上明确定义它们,但我想你无论如何都必须这样做,因为你手动设置了控制器的ng-repeat。