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

jac*_*cob 22 javascript controller dynamic assign angularjs

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

<section ng-repeat="panel in panels">
    <div ng-include="'path/to/file.html'" ng-controller="{{panel}}"></div>
</section>
Run Code Online (Sandbox Code Playgroud)

但Angular抱怨说这{{panel}}是不确定的.

我猜,{{panel}}是不是定义尚未(因为我可以附和了{{panel}}在模板中).

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

PS我也尝试ng-controller="{{panel}}"在我的模板中设置(认为​​它必须已经解决),但没有骰子.

SET*_*SET 16

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

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

$scope.sidepanels = [Alerts, Subscriptions];
Run Code Online (Sandbox Code Playgroud)

以下是关于js小提琴的工作示例http://jsfiddle.net/ADukg/1559/

但是,当您想要在ngRepeat中设置控制器时,我发现所有这些情况都非常奇怪.

  • 此解决方案要求定义选项卡配置的(控制器)范围,并且可能呈现链接以激活它们,具有对每个控制器的引用.如果它们是全局函数,这很容易,但这不是我认为的最佳实践.AFAIK,不可能将控制器注入其他控制器或TabConfigService,所以我不知道如何实现这一点. (3认同)

cje*_*nek 6

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

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

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

ng-controller="myController"
Run Code Online (Sandbox Code Playgroud)

如果没有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);
        };
    });
}]);
Run Code Online (Sandbox Code Playgroud)

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

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

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

}]);
Run Code Online (Sandbox Code Playgroud)

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

现在你可以按照通常的方式在模板中使用控制器 -

ng-controller="testCtrl"
Run Code Online (Sandbox Code Playgroud)

或动态 -

ng-controller="templateController"
Run Code Online (Sandbox Code Playgroud)

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

$scope.templateController = testCtrlFactory
Run Code Online (Sandbox Code Playgroud)


bml*_*ite 5

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

app.controller('ControllerX', function() {
    // your controller implementation        
});
Run Code Online (Sandbox Code Playgroud)

如果是这种情况,则不能简单地使用引用,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;
});
Run Code Online (Sandbox Code Playgroud)

模板(模拟)

<!-- 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>
Run Code Online (Sandbox Code Playgroud)

视图

<div ng-controller="Ctrl">
    <div ng-repeat="panel in panels">
        <div ng-include src="panel.template"></div>        
    </div>
</div>
Run Code Online (Sandbox Code Playgroud)

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

  • 在这种情况下,您可以从全局范围(`window`)直接访问实现.对我来说这很糟糕,这意味着任何第三方脚本都可以访问(并更改?)它.关于**动态**分配控制器,通过**动态**分配模板,你也"动态"分配控制器,只是采用更结构化的方式(即通过将模板从'alert.html'更改为' subscription.html'你也在将`Controller从`AlertCtrl`更改为`SubscriptionCtrl`) (2认同)

小智 5

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

HTML

<mysections></mysections>
Run Code Online (Sandbox Code Playgroud)

指示

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);
                }
            }
        }
    }]);
Run Code Online (Sandbox Code Playgroud)