Проблема с вложенными директивами Angular при использовании ControllerAs

Я создаю огромную форму, которая вызывает различные директивы для создания полной формы. Главная страница, вызывающая Form Builder, передает данные ng-модели следующим образом:

<div form-builder form-data=“formData”></div>

Затем страница Form Builder вызывает различные дочерние директивы для создания различных разделов формы:

FormBuilder.html:

<div form-fields></div>
<div photo-fields></div>
<div video-fields></div>
 .. etc.. etc...

При использовании $scope в контроллере у меня не было проблем с доступом к $scope в дочерних директивах следующим образом:

function formBuilder() {
    return {
         restrict: 'A',
         replace: true,
         scope: {
            formData: '='
         },
         templateUrl: 'FormBuilder.html',
         controller: function($scope) {
            $scope.formSubmit = function() {
            // Submits the formData.formFields and formData.photoFields
            // to the server
            // The data for these objects are created through 
            // the child directives below
         }
     }
   }
}

function formFields() {
    return {
            restrict: 'A',
            replace: true,
            templateUrl: 'FormFields.html',
            controller: function($scope) {
               console.log($scope.formData.formFields);
            }
    }
}

function photoFields() {
    return {
            restrict: 'A',
            replace: true,
            templateUrl: 'PhotoFields.html',
            controller: function($scope) {
               console.log($scope.formData.photoFields);
            }
   }
}
... etc..

Но с тех пор, как я избавился от $scope и начал использовать ControllerAs, у меня возникают всевозможные проблемы с доступом к двухсторонней привязке с родительско-дочерними контроллерами.

function formBuilder() {
    return {
         restrict: 'A',
         replace: true,
         scope: {
           formData: '='
         },
         templateUrl: 'FormBuilder.html',
         controller: function() {
              var vm = this;
             console.log(vm.formData);  // Its fine here

             vm.formSubmit = function() {
               // I cannot change formData.formFields and formData.photoFields 
               // from Child Directive "Controllers"
            }
        },
        controllerAs: ‘fb’,
        bindToController: true
   }
}

function formFields() {
    return {
            restrict: 'A',
            replace: true,
            templateUrl: 'FormFields.html',
            controller: function() {
                var vm = this;
                console.log(vm.formData.formFields); 
                // No way to access 2 way binding with this Object!!!
            }
   }
}

function photoFields() {
    return {
        restrict: 'A',
        replace: true,
        templateUrl: 'PhotoFields.html',
        controller: function() {
            var vm = this;
            console.log(vm.formData.photoFields); 
            // No way to access 2 way binding with this Object!!!
        }
    }
}

Что бы я ни пытался, я достигаю дорожного блока. Вещи, которые я пробовал:

  1. Изолированные области: я попытался передать formData.formFields и formData.photoFields в качестве изолированных областей в дочернюю директиву, но в итоге получил ошибку $compile: MultiDir из-за вложенных изолированных областей, поэтому это невозможно.
  2. Если у меня нет отдельных директив для каждого раздела формы, а все они находятся в одной директиве под директивой formBuilder, то это становится огромной директивой. Вышеприведенный пример — это всего лишь набросок, но каждая дочерняя директива в конце строит одну большую форму. Так что слияние их вместе — действительно последнее средство, так как оно становится сложным в обслуживании и нечитаемым.
  3. Я не думаю, что есть способ получить доступ к родительской директиве ControllerAs из дочерней директивы Controller любым другим способом из того, что я видел до сих пор.
  4. Если я использую родительский ControllerAs в ng-модели шаблона дочерней директивы, такой как <input type=“text” ng-model=“fb.formData.formFields.text" />, это работает нормально, но мне нужно получить доступ к тому же самому из контроллера дочерней директивы для некоторой обработки, которую я не могу сделать.
  5. Если я избавлюсь от controllerAs и снова использую $scope, он будет работать как раньше, но я пытаюсь полностью избавиться от $scope, чтобы подготовиться к будущим изменениям Angular.

Поскольку это расширенная форма, мне нужно иметь отдельную директиву для обработки различных разделов формы, а поскольку вложенные изолированные области не разрешены, начиная с Angular 1.2, это усложняет задачу, особенно при попытке избавиться от $scope с помощью ControllerAs.

Может ли кто-нибудь помочь мне, какие у меня есть варианты, пожалуйста? Я благодарю вас за чтение моего длинного поста.




Ответы (1)


В основном вам нужно использовать параметр require директивы (опция require используется для связи директивы с директивой). Что даст доступ к его родительскому контроллеру, просто упомянув параметр require в дочерней директиве. Также вам нужно использовать bindToController: true, который в основном добавит изолированные данные области в контроллер директив.

Код

function formBuilder() {
    return {
         restrict: 'A',
         replace: true,
         bindToController: true, 
         scope: {
            formData: '='
         },
         templateUrl: 'FormBuilder.html',
         controller: function($scope) {
            $scope.formSubmit = function() {
            // Submits the formData.formFields and formData.photoFields
            // to the server
            // The data for these objects are created through 
            // the child directives below
         }
     }
   }
}

Затем вам нужно добавить параметр require в дочерние директивы. По сути, опция require будет иметь директиву formBuilder с ^ (указывает, что formBuilder будет в родительском элементе), например require: '^formBuilder',.

Написав параметры require, вы можете получить контроллер этой директивы в 4-м параметре функции ссылки.

Код

function formFields() {
    return {
        restrict: 'A',
        replace: true,
        require: '^formBuilder',
        templateUrl: 'FormFields.html',
        //4th parameter is formBuilder controller
        link: function(scope, element, attrs, formBuilderCtrl){
            scope.formBuilderCtrl = formBuilderCtrl;
        },
        controller: function($scope, $timeout) {
            var vm = this;
            //getting the `formData` from `formBuilderCtrl` object
            //added timeout here to run code after link function, means after next digest
            $timeout(function(){
                console.log($scope.formBuilderCtrl.formData.formFields);
            })
        }
    }
}

function photoFields() {
    return {
        restrict: 'A',
        replace: true,
        require: '^formBuilder',
        templateUrl: 'PhotoFields.html',
        //4th parameter is formBuilder controller
        link: function(scope, element, attrs, formBuilderCtrl){ 
            scope.formBuilderCtrl = formBuilderCtrl;
        },
        controller: function($scope, $timeout) {
            var vm = this;
            console.log(vm.formData.photoFields);
            //to run the code in next digest cycle, after link function gets called.
            $timeout(function(){
                console.log($scope.formBuilderCtrl.formData.formFields);
            })
        }
    }
}

Изменить

Одна проблема с вышеуказанным решением заключается в том, что для того, чтобы получить доступ к контроллеру родительской директивы в самом контроллере директивы, я сделал несколько хитростей. 1-й включите formBuilderCtrl в переменную области действия из 4-го параметра функции ссылки. Только тогда вы сможете получить доступ к этому контроллеру, используя $scope (что вам там не нужно). Что касается той же проблемы, зарегистрированной в Github с открытым статусом, Вы можете проверить это здесь.

person Pankaj Parkar    schedule 06.12.2015
comment
Это фантастическое решение @pankajParkar. Я попробую это прямо сейчас. Большое спасибо, так как я застрял на этом в течение 3 дней, пытаясь найти решение! Однако у меня есть вопрос по scope в функции link. Из того, что я читал, Angular 2, вероятно, избавится от $scope, поэтому вышеизложенное не сломается в Angular 2, поскольку scope используется в link()? Я понимаю, наверное, слишком рано готовиться к Angular 2, но что вы думаете об этом сегодня? - person Neel; 06.12.2015
comment
@Neel, да, вы правы. Я понял, что вы используете шаблон controllerAs, близкий к миграции angular2.. но с моей стороны нет решения по этому поводу. Нам нужна переменная области видимости в функциях controller и link. в списке проблем AngularJS все еще есть открытая ошибка github.com/angular/angular.js/issues /5893 - person Pankaj Parkar; 06.12.2015
comment
Спасибо за ваш вклад @PankajParkar и +1 за ссылку. Я надеюсь, что Angular 2 предложит элегантный способ сделать это. Но пока ваше решение мне поможет. Спасибо еще раз. :) - person Neel; 06.12.2015
comment
У @Neel angular2 есть лучшая реализация для этого с использованием DI .. если вы прочитаете ссылку, которую я вам предоставил .. Вы узнаете много вещей .. - person Pankaj Parkar; 06.12.2015