如何在AngularJS中的父控制器或作用域中访问FormController

时间:2013-10-02 15:13:47

标签: javascript angularjs forms angularjs-scope

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

    < / LI>

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

有没有办法绕过这个,或者我应该重构我的页面?

我正在使用Angular 1.1.5 btw。

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

编辑:这是范围层次结构查找此应用的方式

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

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

10 个答案:

答案 0 :(得分:16)

为了处理动态表单,以及相关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;
});

答案 1 :(得分:7)

通过使用在其范围层次结构中查找对象的角度过程,可以更简单地在范围层次结构中进行备份通信。

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

在这种情况下,你需要创建一个&#34;捕手&#34;顶级控制器上的空对象。然后可以为较低范围分配此捕获器对象的表单对象。这是演示的插件。

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

它不是非常优雅,但如果你想到这个&#34;捕手&#34;作为事件监听器的对象,我们仍然遵循标准模式。

在控制器中创建一个空的捕手对象,您想要对嵌套表单的引用

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>

因为我们已经被分配到&#34; catcher。&#34;,angular将遍历范围层次结构并在mainController中找到它,而不管它们之间是否有任何数量的控制器(假设它们也没有&# 39;当然有一个捕手对象)

答案 2 :(得分:5)

因为我使用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>

答案 3 :(得分:4)

angular documentation可以阅读:

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

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

但是,有一些指令,如ngIf,可以创建一个新范围:

  

请注意,使用ngIf删除元素时,其范围将被销毁   并且在恢复元素时创建新范围。

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

答案 4 :(得分:4)

要从指令,控制器等访问表单,您可以使用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,但它不使用事件。它本质上是做同样的事情,但开销要少一些。

答案 5 :(得分:3)

要让实际 FormController对某个指令负责,您只需require form并加上前缀。这样就可以在指令中存储引用&#39;链接功能:

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

    var formController;

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

}])

答案 6 :(得分:2)

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

我创建了一个PlunkR,效果很好。

答案 7 :(得分:2)

关于为父作用域提供表单范围的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的情况下。在我的代码库中,我已将此指令重命名为更通用的&#39; present&#39;,因为考虑该指令的目的的一种方式是它宣布存在范围。

答案 8 :(得分:0)

我知道这会引起很多恐怖......但是我很懒惰并且使用了以下内容(我希望相信它不会因为被添加为手表并且超出每个摘要而产生巨大的开销) :

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

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

</form>

然后我只在控制器中使用controller.referenceToForm等。

答案 9 :(得分:0)

与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>