23

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

<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

PS我也尝试ng-controller="{{panel}}"在我的模板中设置(认为到那时它一定已经解决了),但没有骰子。

4

5 回答 5

16

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

因此,您可能希望将 $scope.sidepanels 定义为带有指向控制器的指针的数组,可能是这样的:

$scope.sidepanels = [Alerts, Subscriptions];

这是关于 js fiddle http://jsfiddle.net/ADukg/1559/的工作示例

但是,当您可能想在 ngRepeat 中设置控制器时,我发现所有这些情况都很奇怪。

于 2012-12-19T04:18:38.370 回答
6

要在模板中动态设置控制器,有对与控制器关联的构造函数的引用会有所帮助。控制器的构造函数是传递给controller()Angular模块 API方法的函数。

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

例如,假设 Angular 在模板中遇到以下情况:

ng-controller="myController"

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

这在ngController文档中对参数值的描述中提到:“全局可访问构造函数的名称或在当前范围内计算为构造函数的表达式的名称。” Angular 源代码中的代码注释在此处src/ng/controller.js更详细地说明了这一点。

默认情况下,Angular 无法轻松访问与控制器关联的构造函数。controller()这是因为当您使用Angular 的模块 API方法注册控制器时,它会将您传递给私有变量的构造函数隐藏起来。您可以在$ControllerProvider 源代码中看到这一点。(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';
    }]);

}]);

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

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

ng-controller="testCtrl"

或动态地——

ng-controller="templateController"

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

$scope.templateController = testCtrlFactory
于 2014-07-08T07:52:04.793 回答
5

我相信你有这个问题,因为你定义你的控制器是这样的(就像我习惯做的那样):

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

如果是这种情况,您不能简单地使用对ControllerX的引用,因为控制器实现(或“类”,如果您想这样称呼它)不在全局范围内(而是存储在应用程序上$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/

于 2012-12-19T15:12:03.270 回答
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);
                }
            }
        }
    }]);
于 2014-04-22T10:24:15.000 回答
-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。

于 2014-11-05T11:21:02.230 回答