简体   繁体   English

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

[英]How do I access FormController in parent controller or scope in AngularJS

I have a page with multiple forms and I only want to show one at a time. 我有一个包含多种表单的页面,我只想一次显示一个。 For this I separated each form into a section and using Bootstrap's accordion plugin I only allow one open section at a time. 为此,我将每个表单分成一个部分并使用Bootstrap的手风琴插件我一次只允许一个打开的部分。

My markup looks something like this: 我的标记看起来像这样:

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

Everything work just fine, I can navigate between the forms etc. 一切正常,我可以在表格等之间导航

Because I don't want the user opening a section if the one they are currently editing contains validation errors, I tried checking in the open_section function if the form associated with it is valid or not. 因为我不希望用户打开一个部分,如果他们当前正在编辑的部分包含验证错误,我尝试检查open_section函数,如果与之关联的表单有效。

I tried, but I could not. 我试过了,但我做不到。 I could not access the FormController associated with the forms in the controller that is responsible for the page. 我无法访问与负责页面的控制器中的表单关联的FormController。 For some reason, they are not getting published on the scope. 出于某种原因,他们没有在范围上发表。

This is what I tried: 这是我试过的:

  • $scope.section1Form is undefined $scope.section1Form undefined

  • tried with $scope.$watch('section1Form, function(){}) , still undefined 尝试使用$scope.$watch('section1Form, function(){}) ,仍未定义

  • tried adding the name of the form as a second parameter to open_section like so: open_section('section1', section1Form) but in the function the second argument is undefined . 尝试将表单的名称添加为open_section的第二个参数, open_section所示: open_section('section1', section1Form)但在函数中第二个参数undefined

Between the <form></form> tags, I have access to the FormController, but outside them I don't. <form></form>标签之间,我可以访问FormController,但在我们之外我没有。 Since the event is coming from outside the <form> (the closing, opening of the sections) I can't pass the FormController to my controller to check the validity of my forms. 由于事件来自<form> (关闭,打开部分),我无法将FormController传递给我的控制器来检查我的表单的有效性。

Is there a way to get around this, or should I refactor my page? 有没有办法解决这个问题,还是应该重构我的页面?

I am using Angular 1.1.5 btw. 我正在使用Angular 1.1.5顺便说一句。

Also, checking with the AngularJS Batarang Chrome plugin, I can see that the forms get published as child scopes to the current scope. 另外,使用AngularJS Batarang Chrome插件进行检查,我可以看到表单作为子范围发布到当前范围。

EDIT : this is how the scope hierarchy looks for this app 编辑 :这是范围层次结构查找此应用程序的方式

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

Is this because I'm using ng-include ? 这是因为我使用的是ng-include吗? Is there no way to access those forms in a controller then? 那么在控制器中无法访问这些表单吗?

To deal with dynamic forms, and thus the dynamic location of the associated FormController, I used a simple directive to help locate the scope that contains the form. 为了处理动态表单,以及相关FormController的动态位置,我使用了一个简单的指令来帮助定位包含表单的范围。

Solution: 解:

Create a directive that $emit's the scope associated w/the form: 创建一个指令$ emit发出与表单关联的范围:

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

Use the directive in the markup: 在标记中使用该指令:

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

Listen for the event broadcast by the directive in your controller: 通过控制器中的指令监听事件广播:

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

There a much simpler way to communicate back up in the scope hierarchy by using angular process of looking for objects up in its scope hierarchy. 通过使用在其范围层次结构中查找对象的角度过程,可以更简单地在范围层次结构中进行备份通信。

This allows us to attach the object from a lower scope into a higher scope by using the dot notation. 这允许我们使用点表示法将对象从较低的范围附加到较高的范围。

In this case you need to create a "catcher" empty object on the top level controller. 在这种情况下,您需要在顶级控制器上创建一个“捕手”空对象。 This catcher object can then be assigned the form objects from the lower scopes. 然后可以为较低范围分配此捕获器对象的表单对象。 Here is a plunk for demo. 这是一个演示的插件。

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

It isn't perfectly elegant but if you think of this "catcher" object as an event listener, we're still following a standard pattern. 它并不完美,但如果你把这个“捕手”对象想象成一个事件听众,我们仍然遵循一个标准模式。

create an empty catcher object in the controller where you'd like a reference to the nested form 在控制器中创建一个空的捕手对象,您希望对嵌套表单进行引用

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

  };
 }

Then in the markup itself whenever the ng-form directive is declared, set catcher.formName = formName like so 然后在声明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>

Because we're assigned to "catcher.", angular will traverse up the scope hierarchy and find thd it in the mainController regardless of any number of controllers in between (assuming they also don't have a catcher object, of course) 因为我们被分配到“catcher。”,angular将遍历范围层次结构并在mainController中找到它,而不管它们之间是否有任何数量的控制器(当然,假设它们也没有捕获器对象)

Because I was using partial views with ng-view the forms were registered on a child scope of my controller. 因为我使用ng-view的部分视图,所以表单已在我的控制器的子范围上注册。 It seems that I cannot access the child scopes from a parent one, due to prototypical inheritance. 由于原型继承,我似乎无法从父级访问子范围。

That said, I did manage to get the form controller instances into my controller by passing them through the function that was responsible for opening/closing the accordion. 也就是说,我确实设法通过传递负责打开/关闭手风琴的函数将表单控制器实例放入我的控制器中。

The solution is something like this: 解决方案是这样的:

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

In the angular documentation one can read: 角度文档中,可以阅读:

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

Name of the form. 表格的名称。 If specified, the form controller will be published into related scope, under this name. 如果指定,表单控制器将以此名称发布到相关范围。

However, there are certain directives, like ngIf , that create a new scope: 但是,有一些指令,如ngIf ,可以创建一个新范围:

Note that when an element is removed using ngIf its scope is destroyed and a new scope is created when the element is restored. 请注意,使用ngIf删除元素时,其范围将被销毁,并且在还原元素时会创建新范围。

Can that be your case? 这可能是你的情况吗? If so, you can try setting the form name to something like "forms.section1Form" and then access it accordingly in the scope. 如果是这样,您可以尝试将表单名称设置为“forms.section1Form”之类的内容,然后在范围内相应地访问它。

To access the form from a directive, controller, etc... you can utilize ng-init: 要从指令,控制器等访问表单,您可以使用ng-init:

For example: 例如:

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

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

You know can access the form data, or pass back with a bound variable if this is a directive. 您知道可以访问表单数据,或者如果这是指令,则使用绑定变量传回。 Example in the controller for the above template: 上述模板的控制器示例:

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

This is similar to Sinil D's answer great answer, in that is saves the form onto the parent $scope, but it doesn't use events. 这类似于Sinil D的回答很好的答案,即将表单保存到父$ scope,但它不使用事件。 It is essentially doing the same thing, but a bit less overhead. 它本质上是在做同样的事情,但开销要少一些。

To get the actual FormController responsible for a certain directive, you simply require the form with the hat prefix. 要使实际的 FormController负责某个指令,您只require带有hat前缀的form This enables to store a reference in your directives' link function: 这样可以在指令的link函数中存储引用:

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

    var formController;

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

}])

Your controller may be outside your form OR your are trying to get your form before the form is being populated in scope. 您的控制器可能在您的表单之外,或者您正在尝试在表单填充范围之前获取表单。

I created a PlunkR and it working well. 我创建了一个PlunkR ,它运行良好。

Regarding Sunil D.'s novel solution to providing the form's scope to parent scopes (see https://stackoverflow.com/a/22487840/1518575 ), you can actually get the form's scope from the event object that is sent in an $emit call. 关于Sunil D.为父范围提供表单范围的新颖解决方案(请参阅https://stackoverflow.com/a/22487840/1518575 ),您实际上可以从$中发送的事件对象中获取表单的范围。发出电话。

Since at least Angular 1.0.3, the event object has a targetScope property, which references the scope on which the event was $emit-ed. 至少从Angular 1.0.3开始,事件对象具有targetScope属性,该属性引用事件为$ emit-ed的范围。 (See http://docs.angularjs.org/api/ng/type/ $rootScope.Scope#$on for more information about the properties of the event object.) (有关事件对象属性的更多信息,请参阅http://docs.angularjs.org/api/ng/type/ $ rootScope.Scope#$ on。)

With this in mind, you can simplify Sunil D.'s directive code to be: 考虑到这一点,您可以将Sunil D.的指令代码简化为:

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

The template doesn't change at all: 模板根本不会改变:

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

And subsequently you would change the event handling code to be: 然后您将更改事件处理代码为:

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

This directive would be relevant in any scenario where you would want to provide a scope to one of its ancestors, not just in the case of ngForm. 该指令适用于您希望为其祖先之一提供范围的任何情况,而不仅仅是在ngForm的情况下。 In my codebase, I've renamed this directive to the more generic 'present', since one way to think of the directive's purpose is that it announces the presence of a scope. 在我的代码库中,我已将此指令重命名为更通用的'present',因为考虑该指令的目的的一种方法是它宣布存在范围。

I know this will cause a lot of horror.... but I was quite lazy and used the following (which I'd like to believe does not create massive overhead due to being added as a watch and excecute every digest): 我知道这会引起很多恐怖....但我很懒惰并且使用了以下内容(我希望相信它不会因为被添加为手表并且超出每个摘要而产生巨大的开销):

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

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

</form>

Then I just use controller.referenceToForm within my controller, etc. 然后我只在控制器中使用controller.referenceToForm等。

Similar to Sunil D. solution, I have come accross a similar one, but implicitly setting to the scope variable, the formController in which the tag was used on, as I did not want to hard code the name of the form twice: 与Sunil D.解决方案类似,我遇到了类似的问题,但隐式设置了范围变量,即使用标记的formController,因为我不想硬编码表单的名称两次:

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

And then, the view something like: 然后,观点如下:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM