web-dev-qa-db-ja.com

AngularJS:ng-repeatからコントローラーを動的に割り当てる

私は次のように含まれているテンプレートにコントローラを動的に割り当てようとしています:

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

しかしAngularは{{panel}}が未定義であると不平を言います。

{{panel}}が定義されていないと思いますまだ(テンプレート内で{{panel}}をエコー出力できるため)。

私は、ng-controllerng-controller="template.ctrlr"のような変数に等しく設定する多くの例を見てきました。しかし、重複した並行ループを作成しないと、{{panel}}が必要なときにng-controllerの値を使用できるようにする方法を理解できません。

追伸また、テンプレートにng-controller="{{panel}}"を設定しようとしましたが(それまでに解決されているはずです)、ダイスはありませんでした。

22
jacob

あなたの問題は、ng-controllerがコントローラの名前の文字列だけでなく、コントローラ自体を指す必要があることです。

そのため、$ scope.sidepanelsを、コントローラへのポインタを持つ配列として定義したい場合があります。たとえば、次のようになります。

$scope.sidepanels = [Alerts, Subscriptions];

これがjsフィドルの実際の例です http://jsfiddle.net/ADukg/1559/

ただし、ngRepeatでコントローラーをセットアップする必要がある場合、私はこのすべての状況を非常に奇妙に感じます。

16
SET

テンプレートでコントローラーを動的に設定するには、コントローラーに関連付けられたconstructor関数への参照を用意すると便利です。コントローラーのコンストラクター関数は、Angularの モジュールAPIcontroller()メソッドに渡す関数です。

ngController ディレクティブに渡された文字列が登録済みコントローラーの名前ではない場合、ngControllerは文字列を評価対象の式として扱うため、これが役立ちます現在のスコープ。このスコープ式は、コントローラーコンストラクターに評価される必要があります。

たとえば、Angularとすると、テンプレートで次のようになります。

_ng-controller="myController"
_

myControllerという名前のコントローラーが登録されていない場合、Angularは現在含まれているコントローラーの_$scope.myController_を調べます。このキーがスコープに存在し、対応するvalueはコントローラーコンストラクターであり、コントローラーが使用されます。

これは、パラメーター値の説明の ngController ドキュメントで言及されています:「グローバルにアクセスできるコンストラクター関数の名前または現在のスコープでコンストラクター関数に評価される式」。 Angularソースコード内のコードコメントは、これをより詳しく説明します ここでは_src/ng/controller.js_ です。

デフォルトでは、Angularでは、コントローラーに関連付けられたコンストラクターに簡単にアクセスできません。これは、Angularのcontroller()メソッドを使用してコントローラーを登録すると、 モジュール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';
    }]);

}]);
_

上記のコードは、testCtrlという名前でコントローラーを登録し、コントローラーのコンストラクターをtestCtrlFactoryというサービスとして公開します。

これで、通常の方法でテンプレートでコントローラーを使用できます-

_ng-controller="testCtrl"
_

または動的に-

_ng-controller="templateController"
_

後者が機能するには、現在のスコープに次のものが必要です。

_$scope.templateController = testCtrlFactory
_
6
cjerdonek

もう1つの方法は、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);
                }
            }
        }
    }]);
5
Brenton

あなたがこのようにコントローラーを定義しているので、あなたはこの問題を抱えていると思います(私が慣れているように):

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/

5
bmleite