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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/13944207/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-24 15:12:06  来源:igfitidea点击:

AngularJS: dynamically assign controller from ng-repeat

javascriptdynamiccontrollerangularjsassign

提问by jacob

I'm trying to dynamically assign a controller for included template like so:

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

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

But Angular complains that {{panel}}is undefined.

但是 Angular 抱怨说这{{panel}}是未定义的。

I'm guessing that {{panel}}isn't defined yet(because I can echo out {{panel}}inside the template).

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

I've seen plenty of examples of people setting ng-controllerequal to a variable like so: ng-controller="template.ctrlr". But, without creating a duplicate concurrant loop, I can't figure out how to have the value of {{panel}}available when ng-controllerneeds it.

我见过很多人树立榜样ng-controller等于一个变量,像这样:ng-controller="template.ctrlr"。但是,如果不创建重复的并发循环,我无法弄清楚如何{{panel}}ng-controller需要时获得可用的值。

P.S. I also tried setting ng-controller="{{panel}}"in my template (thinking it must have resolved by then), but no dice.

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

采纳答案by SET

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

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

So you might want to define $scope.sidepanels as array with pointers to controllers, something like this, maybe:

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

$scope.sidepanels = [Alerts, Subscriptions];

Here is the working example on js fiddle http://jsfiddle.net/ADukg/1559/

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

However, i find very weird all this situation when you might want to set up controllers in ngRepeat.

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

回答by cjerdonek

To dynamically set a controller in a template, it helps to have a reference to the constructorfunction associated to a controller. The constructor function for a controller is the function you pass in to the controller()method of Angular's module API.

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

Having this helps because if the string passed to the ngControllerdirective is not the name of a registered controller, then ngControllertreats the string as an expression to be evaluated on the current scope. This scope expression needs to evaluate to a controller constructor.

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

For example, say Angular encounters the following in a template:

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

ng-controller="myController"

If no controller with the name myControlleris registered, then Angular will look at $scope.myControllerin the current containing controller. If this key exists in the scope and the corresponding value is a controller constructor, then the controller will be used.

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

This is mentioned in the ngControllerdocumentation in its description of the parameter value: "Name of a globally accessible constructor function or an expression that on the current scope evaluates to a constructor function." Code comments in the Angular source code spell this out in more detail here in src/ng/controller.js.

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

By default, Angular does not make it easy to access the constructor associated to a controller. This is because when you register a controller using the controller()method of Angular's module API, it hides the constructor you pass it in a private variable. You can see this here in the $ControllerProvider source code. (The controllersvariable in this code is a variable private to $ControllerProvider.)

默认情况下,Angular 无法轻松访问与控制器关联的构造函数。这是因为当您使用controller()Angular 的模块 API的方法注册控制器时,它隐藏了您在私有变量中传递它的构造函数。您可以在$ControllerProvider 源代码中看到这一点。(controllers此代码中的变量是 私有的变量$ControllerProvider。)

My solution to this issue is to create a generic helper service called registerControllerfor registering controllers. This service exposes both the controller andthe controller constructor when registering a controller. This allows the controller to be used both in the normal fashion and dynamically.

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

Here is code I wrote for a registerControllerservice that does this:

这是我为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);
        };
    });
}]);

Here is an example of using the service to register a controller:

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

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

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

}]);

The code above registers the controller under the name testCtrl, and it also exposes the controller's constructor as a service called testCtrlFactory.

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

Now you can use the controller in a template either in the usual fashion--

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

ng-controller="testCtrl"

or dynamically--

或动态--

ng-controller="templateController"

For the latter to work, you must have the following in your current scope:

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

$scope.templateController = testCtrlFactory

回答by bmleite

I believe you're having this problem because you're defining your controllers like this (just like I'm used to do):

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

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

If that's the case, you cannot simply use references to ControllerXbecause the controller implementation (or 'Class', if you want to call it that) is not on the global scope (instead it is stored on the application $controllerProvider).

如果是这种情况,您不能简单地使用对的引用,ControllerX因为控制器实现(或“类”,如果您想调用它)不在全局范围内(而是存储在应用程序中$controllerProvider)。

I would suggest you to use templates instead of dynamically assign controller references (or even manually create them).

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

Controllers

控制器

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;
});

Templates (mocks)

模板(模拟)

<!-- 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>

View

看法

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

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

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

回答by Brenton

Another way is to not use ng-repeat, but a directive to compile them into existence.

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

HTML

HTML

<mysections></mysections>

Directive

指示

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);
                }
            }
        }
    }]);

回答by Alex Arvanitidis

Ok I think the simplest solution here is to define the controller explicitly on the template of your file. Let's say u have an array:

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

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

Then on your html file:

然后在你的 html 文件中:

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

And the solution aWidget.html:

和解决方案aWidget.html:

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

bWidget.html:

bWidget.html:

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

Simple as that! You just define the controller name in your template. Since you define the controllers as bmleite said:

就那么简单!您只需在模板中定义控制器名称。由于您将控制器定义为 bmleite 说:

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

then this is the best workaround I could come up with. The only issue here is if u have like 50 controllers, u'll have to define them explicitly on each template, but I guess u had to do this anyway since you have an ng-repeat with controller set by hand.

那么这是我能想到的最好的解决方法。这里唯一的问题是,如果你有 50 个控制器,你必须在每个模板上明确定义它们,但我想你无论如何都必须这样做,因为你有一个手动设置控制器的 ng-repeat。