【问题标题】:How do I access FormController in parent controller or scope in AngularJS如何在 AngularJS 的父控制器或范围内访问 FormController
【发布时间】:2013-10-09 00:59:36
【问题描述】:

我有一个包含多个表单的页面,我只想一次显示一个。为此,我将每个表单分成一个部分,并使用 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.section1Formundefined

  • $scope.$watch('section1Form, function(){}) 试过,仍然未定义

  • 尝试将表单的名称作为第二个参数添加到open_section,如下所示:open_section('section1', section1Form),但在函数中,第二个参数是undefined

&lt;form&gt;&lt;/form&gt; 标签之间,我可以访问FormController,但在它们之外我没有。由于事件来自&lt;form&gt; 之外(部分的关闭、打开),我无法将 FormController 传递给我的控制器以检查我的表单的有效性。

有没有办法解决这个问题,或者我应该重构我的页面?

我正在使用 Angular 1.1.5 顺便说一句。

另外,检查 AngularJS Batarang Chrome 插件,我可以看到表单作为子范围发布到当前范围。

编辑:这是该应用程序的作用域层次结构

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

这是因为我使用的是ng-include吗?那么有没有办法在控制器中访问这些表单?

【问题讨论】:

  • 你什么时候尝试检查 $scope.section1Form。它不是立即可用的。您可以尝试在某些表单事件上检查它吗,比如说通过将 ng-click 添加到某个元素。
  • 如果我尝试从表单中发送它,例如从带有ng-click="something(nameOfForm)" 的提交事件中发送它,我可以使用它。但如果事件没有在表单内发生,那么我不能。

标签: javascript angularjs forms angularjs-scope


【解决方案1】:

为了处理动态表单以及关联的 FormController 的动态位置,我使用了一个简单的指令来帮助定位包含表单的范围。

解决方案:

创建一个指令,$emit 的范围与表单相关联:

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

【讨论】:

  • 我们可以通过使用角度自然范围遍历来查找对象来实现这种类型的事件发射功能,正如我在下面的答案中所概述的stackoverflow.com/a/24495707/154353
  • 这很优雅。我应该更频繁地考虑 Angular 指令中的事件发射。
  • 这是一个很棒的方法!但是,stackoverflow.com/a/22811273/61604 中提出的答案也完成了同样的事情(将表单放在父范围内),但避免使用事件并且必须创建自己的指令。
  • 感谢高度优雅的解决方案。你成就了我的一天
【解决方案2】:

有一种更简单的方法可以在作用域层次结构中向上进行通信,方法是使用在其作用域层次结构中向上查找对象的角度过程。

这允许我们使用点符号将对象从较低范围附加到较高范围。

在这种情况下,您需要在顶层控制器上创建一个“捕手”空对象。然后可以为该捕手对象分配来自较低范围的表单对象。这是一个演示。

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>

因为我们被分配给“catcher”,所以 Angular 将遍历范围层次结构并在 mainController 中找到它,而不管中间有多少控制器(当然,假设它们也没有 catcher 对象)

【讨论】:

  • 这在没有其他选项时很有帮助,但是这会降低应用程序的可测试性,在大多数情况下,我认为stackoverflow.com/questions/19140386/… 是最好的。
  • 这比公认的解决方案更简单、更优雅(总是试图避免事件)。捕手似乎没有必要。使用 controllerAs/vm 语法你可以做 vm.alpha = alpha.
【解决方案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>

【讨论】:

  • 我看到了同样的事情。但它仍然留下了如何让 formController 自动进入范围的问题(如广告所示)。
  • 这是无法完成的,因为如果您使用的是 ng-view,那么您基本上处于子范围内,而不是控制器正在使用的同一个范围内。相当混乱,特别是因为它没有完整记录。
【解决方案4】:

angular documentation可以阅读:

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

表单的名称。如果指定,表单控制器将被发布 进入相关范围,在此名称下。

但是,有些指令(例如 ngIf)会创建一个新范围:

请注意,当使用 ngIf 删除元素时,其作用域被破坏 并在元素恢复时创建一个新范围。

这可能是你的情况吗?如果是这样,您可以尝试将表单名称设置为“forms.section1Form”之类的名称,然后在范围内相应地访问它。

【讨论】:

  • 表格在每种情况下都被命名,正如我在示例标记中所说的那样。手风琴是否有可能正在创建和破坏范围?
  • forms.section1Form 尝试过。仍然收到undefined
  • 我的用例可能不同,但是在表单中添加一个名称会使 FormController 实例在我的控制器的 $scope 中可用
【解决方案5】:

要从指令、控制器等访问表单...您可以使用 ng-init:

例如:

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

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

您知道可以访问表单数据,或者如果这是一个指令,则可以使用绑定变量返回。 上述模板的控制器示例:

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

这类似于Sinil D's answer 很好的答案,因为它将表单保存到父 $scope,但它不使用事件。它本质上是做同样的事情,但开销要少一些。

【讨论】:

  • 我猜你所说的“myLocalScopeVar=form”实际上是指“myForm”,在这种情况下,你需要写两次表单的名称......不太好。
【解决方案6】:

要获得 实际 FormController 负责某个指令,您只需 require 和带有帽子前缀的 form。这可以在指令的链接函数中存储引用:

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

    var formController;

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

}])

【讨论】:

    【解决方案7】:

    您的控制器可能在您的表单之外,或者您正试图在表单被填充到范围之前获取您的表单。

    我创建了一个PlunkR,它运行良好。

    【讨论】:

    • 谢谢,但这只是创建此问题时的拼写错误。对于那个很抱歉。我的实际代码没有这些错别字。
    • 如果我只在$routeProvider 中而不是在实际模板中设置页面使用的控制器,这有关系吗?
    • 不,它应该与 $routeProvider 一起使用。你在使用ng-view 来渲染你的路线吗?
    • 感谢 Plunkr,当我将 $scope 记录到控制台时,我无法弄清楚为什么我可以看到 $scope 中列出的表单,但每次尝试时都未定义访问 $scope.formName 对象。
    【解决方案8】:

    关于 Sunil D. 将表单的范围提供给父范围的新颖解决方案(请参阅 https://stackoverflow.com/a/22487840/1518575),您实际上可以从 $emit 调用中发送的事件对象中获取表单的范围。

    至少从 Angular 1.0.3 开始,事件对象有一个 targetScope 属性,它引用了 $emit-ed 事件所在的范围。 (有关事件对象属性的更多信息,请参阅http://docs.angularjs.org/api/ng/type/$rootScope.Scope#$on。)

    考虑到这一点,您可以将 Sunil D. 的指令代码简化为:

    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 的情况下。在我的代码库中,我已将此指令重命名为更通用的“present”,因为考虑该指令目的的一种方式是它宣布存在范围。

    【讨论】:

      【解决方案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>
        

        【讨论】:

          猜你喜欢
          • 2014-02-22
          • 2013-06-10
          • 1970-01-01
          • 2014-04-21
          • 2013-11-24
          • 2016-03-28
          • 1970-01-01
          • 2018-08-30
          • 2013-04-02
          相关资源
          最近更新 更多