Подтвердить что ты не робот

AngularJS: динамически назначает контроллер из ng-repeat

Я пытаюсь динамически назначать контроллер для включенного шаблона следующим образом:

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

Но Angular жалуется, что {{panel}} - undefined.

Я предполагаю, что {{panel}} еще не определен (потому что я могу откликнуть {{panel}} внутри шаблона).

Я видел множество примеров того, как люди устанавливают ng-controller равным такой переменной: ng-controller="template.ctrlr". Но, не создавая повторяющийся контур совпадения, я не могу понять, как получить значение {{panel}}, доступное, когда ng-controller нуждается в нем.

P.S. Я также попытался установить ng-controller="{{panel}}" в моем шаблоне (думая, что он должен был разрешить к тому моменту), но не кубик.

4b9b3361

Ответ 1

Ваша проблема в том, что ng-controller должен указывать на сам контроллер, а не только на строку с именем контроллера.

Итак, вы можете определить $scope.sidepanels как массив с указателями на контроллеры, что-то вроде этого, возможно:

$scope.sidepanels = [Alerts, Subscriptions];

Вот рабочий пример js fiddle http://jsfiddle.net/ADukg/1559/

Тем не менее, я нахожу очень странным всю эту ситуацию, когда вы можете настроить контроллеры в ngRepeat.

Ответ 2

Чтобы динамически установить контроллер в шаблон, он помогает ссылаться на функцию конструктора, связанную с контроллером. Функция-конструктор для контроллера - это функция, которую вы передаете методу 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 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

Ответ 3

Я считаю, что у вас есть эта проблема, потому что вы определяете свои контроллеры как это (как и я привык):

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

Шаблоны (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>

Просмотр

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

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

Ответ 4

Другой способ - не использовать 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

Хорошо. Я думаю, что самым простым решением здесь является определение контроллера явно на шаблоне вашего файла. Пусть say u имеет массив:

$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 с контроллером, установленным вручную.