Как получить доступ к FormController в родительском контроллере или области действия в AngularJS

У меня есть страница с несколькими формами, и я хочу только показать ее за раз. Для этого я разделил каждую форму на раздел и используя плагин аккордеона Bootstrap, я разрешаю только один открытый раздел за раз.

Моя разметка выглядит примерно так:

<a ng-click="open_section('section1')">Section 1</a>

<div collapse="section1">
  <form name="section1Form">
  </form>
</div>

<a ng-click="open_section('section2')">Section 2</a>

<div collapse="section2">
  <form name="section2Form">
  </form>
</div>

Все работает нормально, я могу перемещаться между формами и т.д.

Поскольку я не хочу, чтобы пользователь открывал раздел, если тот, который они в настоящее время редактируют, содержит ошибки проверки, я попытался проверить функцию open_section, если связанная с ним форма действительна или нет.

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

Это то, что я пробовал:

  • $scope.section1Form undefined

  • попробовал с $scope.$watch('section1Form, function(){}), еще undefined

  • попытался добавить имя формы в качестве второго параметра в open_section следующим образом: open_section('section1', section1Form), но в функции второй аргумент undefined.

Между тегами <form></form> у меня есть доступ к FormController, но вне их я этого не делаю. Поскольку событие происходит извне <form> (закрытие, открытие разделов), я не могу передать FormController моему контроллеру, чтобы проверить правильность моих форм.

Есть ли способ обойти это, или мне нужно реорганизовать мою страницу?

Я использую Angular 1.1.5 битв.

Кроме того, проверяя с помощью модуля AngularJS Batarang Chrome, я вижу, что формы публикуются как дочерние области для текущей области.

EDIT: так выглядит иерархия областей для этого приложения.

 - root
 |
 ---current controller\ scope
 |
 ----scope that contains the forms

Это потому, что я использую ng-include? Нет ли способа доступа к этим формам в контроллере?

Ответы

Ответ 1

Чтобы справиться с динамическими формами и, таким образом, динамическим расположением связанного с ним FormController, я использовал простую директиву, чтобы помочь найти область, содержащую форму.

Решение:

Создайте директиву, что $испускает область, связанную с формой:

  module.directive('formLocator', function() {
    return {
      link: function(scope) {
        scope.$emit('formLocator');
      }
    }

Используйте директиву в разметке:

<form name="myForm" novalidate form-locator>

Слушайте трансляцию событий по директиве в контроллере:

$scope.$on('formLocator', function(event) {
  $scope.myDeeplyNestedForm = event.targetScope.myForm;
});

Ответ 2

Существует гораздо более простой способ связи с резервной копией в иерархии областей с помощью процесса angular поиска объектов в иерархии областей видимости.

Это позволяет нам присоединить объект из более низкой области видимости к более широкому пространству с помощью точечной нотации.

В этом случае вам нужно создать пустой объект "улавливатель" на контроллере верхнего уровня. Затем этому узловому объекту могут быть назначены объекты формы из нижних областей. Вот демон для демонстрации.

http://plnkr.co/edit/xwsu48bjM3LofAoaRDsN?p=preview

Это не очень элегантно, но если вы думаете об этом объекте "зрелище" в качестве прослушивателя событий, мы по-прежнему следуем стандартным шаблонам.

создать пустой объект-ловушку в контроллере, где вам нужна ссылка на вложенную форму

function mainController($scope){
  $scope.catcher = {

  };
 }

Затем в самой разметке всякий раз, когда объявляется директива ng-form, задайте catcher.formName = formName так

<ng-form name="alpha">
          <span ng-init="catcher.alpha = alpha"></span>
          <input type="text" required="" ng-model="alphaValue" />
        </ng-form>

Поскольку мы назначены "ловушка". angular будет проходить по иерархии областей и найти ее в mainController независимо от любого количества контроллеров между ними (при условии, что у них также нет объекта-уловителя, конечно)

Ответ 3

Поскольку я использовал частичные представления с ng-view, формы были зарегистрированы в дочерней области моего контроллера. Кажется, что я не могу получить доступ к дочерним областям из родительского, из-за прототипического наследования.

Тем не менее, мне удалось получить экземпляры контроллера формы в моем контроллере, передав их через функцию, которая отвечала за открытие/закрытие аккордеона.

Решение выглядит примерно так:

<a ng-click="open_section('section1', section1Form)">Section 1</a>

<div collapse="section1">
  <form name="section1Form">
  </form>
</div>

<a ng-click="open_section('section2', section2Form)">Section 2</a>

<div collapse="section2">
  <form name="section2Form">
  </form>
</div>

Ответ 4

В angular документации можно прочитать:

<form
       [name="{string}"]>
</form>

Название формы. Если указано, контроллер формы будет опубликован в соответствующий раздел, под этим именем.

Однако существуют определенные директивы, такие как ngIf, которые создают новую область:

Обратите внимание, что когда элемент удаляется с помощью ngIf его область действия уничтожается и новая область создается при восстановлении элемента.

Это может быть ваш случай? Если это так, вы можете попробовать установить имя формы на что-то вроде "forms.section1Form", а затем соответствующим образом получить доступ к ней в области.

Ответ 5

Чтобы получить доступ к форме из директивы, контроллера и т.д., вы можете использовать ng-init:

Например:

  <form name="myForm">
     <div ng-init="myLocalScopeVar=form"></div

    <input name="myField" ....>
  </form>

Вы знаете, что можете получить доступ к данным формы или передать обратно связанную переменную, если это директива.    Пример в контроллере для указанного шаблона:

     if ($scope.myLocalScopeVar.myField.$valid ) ...

Это похоже на ответ Sinil D отличный ответ, в котором сохраняется форма в родительской области $scope, но она не использует события. Это по сути делает то же самое, но немного меньше накладных расходов.

Ответ 6

Чтобы получить фактическую FormController, ответственную за определенную директиву, вы просто require form с префиксом шляпы. Это позволяет сохранить ссылку в функции ссылок ваших директив:

module.directive('awesomeFormChild', [function () {

    var formController;

    return {
        require: '^form',
        link: function(scope, elm, attr, ctrl) {
            formController = ctrl;
        }
    };

}])

Ответ 7

Ваш контроллер может находиться вне вашей формы, или вы пытаетесь получить форму до того, как форма будет заполнена в области.

Я создал PlunkR, и он хорошо работает.

Ответ 8

Что касается решения Sunil D. new для предоставления области формы родительским областям (см. fooobar.com/questions/329779/...), вы можете фактически получить область формы из объекта события, которая отправляется при вызове $emit.

Так как по крайней мере Angular 1.0.3, объект события имеет свойство targetScope, которое ссылается на область, в которой событие было испущено. (См. http://docs.angularjs.org/api/ng/type/ $rootScope.Scope # $для получения дополнительной информации о свойствах объекта события.)

Имея это в виду, вы можете упростить директивный код Сунил Д.:

module.directive('formLocator', function() {
  return {
    link: function(scope) {
      scope.$emit('formLocator');
    }
  };
});

Шаблон не изменяется вообще:

<form name="myForm" novalidate="" form-locator>

И впоследствии вы измените код обработки событий:

$scope.$on('formLocator', function(event) {
  $scope.myDeeplyNestedForm = event.targetScope.myForm;
});

Эта директива была бы релевантна в любом сценарии, где вы хотели бы предоставить область охвата одному из своих предков, а не только в случае ngForm. В моей кодовой базе я переименовал эту директиву в более общий "подарок", поскольку одним из способов думать о директивной цели является то, что она объявляет о наличии области.

Ответ 9

Я знаю, что это вызовет много ужасов.... но я был довольно ленив и использовал следующее (что я хотел бы верить, не создает больших накладных расходов из-за того, что их добавили в качестве часов и превзошли каждый дайджест)

<form name = "formName" nf-if="someCondition">

{{(controller.referenceToForm==undefined && (controller.referenceToForm=formName) != undefined)?'':''}}

</form>

Затем я просто использую controller.referenceToForm внутри своего контроллера и т.д.

Ответ 10

Подобно решению Sunil D., я прошел через аналогичный, но неявно устанавливая переменную области видимости, formController, в которой использовался тег, поскольку я не хотел жестко кодировать имя формы дважды

renderFormModule.directive('formLocator', function() {
    return {
        link: function(scope, element) {
            scope.formAssociated = scope[element[0].name];
        }
    }
});

И затем, вид что-то вроде:

    <form name="testForm" ng-controller='formController as formCtrl' form-locator novalidate>