16

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

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

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

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

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

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

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

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

4

10 回答 10

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;
});
于 2014-03-18T18:22:26.017 回答
7

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

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

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

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 对象)

于 2014-06-30T17:32:18.320 回答
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>
于 2013-10-02T23:07:42.573 回答
4

角度文档中,可以阅读:

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

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

但是,有一些指令,比如ngIf,会创建一个新的范围:

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

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

于 2013-10-02T15:24:35.363 回答
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 great answer,因为它将表单保存到父 $scope,但它不使用事件。它本质上是在做同样的事情,但开销要少一些。

于 2014-04-02T12:19:09.580 回答
3

要获得对某个指令的实际 负责,您只需使用帽子前缀即可。这可以在指令的链接函数中存储引用:FormControllerrequireform

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

    var formController;

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

}])
于 2015-06-03T15:00:12.597 回答
2

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

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

于 2013-10-02T15:59:03.967 回答
2

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

至少从 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”,因为考虑该指令目的的一种方法是它宣布存在范围。

于 2014-04-17T15:47:41.440 回答
0

我知道这会引起很多恐惧......但我很懒惰并使用了以下内容(我想相信它不会因为被添加为手表并执行每个摘要而产生大量开销):

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

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

</form>

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

于 2015-11-06T14:23:53.730 回答
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>
于 2016-02-10T12:57:05.923 回答