为什么在检查$ pristine或$ setDirty()时在ng-include中形成undefined?

时间:2014-05-15 19:15:36

标签: angularjs angularjs-ng-include

以下代码抛出错误" TypeError:无法读取属性' $ pristine'未定义"当我点击"检查"按钮。

app.controller('MainCtrl', function($scope) {
  // other stuff
})

.controller('Ctrl2', function($scope) {
  $scope.product = {description:'pump'};
  $scope.output = 'unknown';
  // uncomment to avoid undefined error, still can't see $pristine
  // $scope.formHolder = {};
  $scope.checkForm = function() {
    $scope.descriptionTest = $scope.product.description;
    if ($scope.formHolder.productForm.$pristine) {
      $scope.output = 'yes';
    }
    if ($scope.formHolder.productForm.$dirty) {
      $scope.output = 'no' 
    }
  }
});

HTML

  <body ng-controller="MainCtrl">
    <div >
      <ng-include ng-controller="Ctrl2" src="'myForm.html'"></ng-include>
    </div>
  </body>

myForm.html

<form name="productForm" novalidate>
  <h2>myForm</h2>
  description: <input type="text" name="description" ng-model="product.description"/>
  <br>
  <button ng-click="checkForm()">Check Form</button>
  <br>
  Form Pristine: {{output}}
  <br><br>
  I can see the description: {{descriptionTest}}
</form>

plunkr

问题是我的Ctrl2无法看到productForm。起初我认为这与ng-include在创建子范围时所做的原型继承有关,所以我尝试在Ctrl2中添加变量:

$scope.productForm = {}; 

这摆脱了错误,但我的控制器仍然没有正确看到$ pristine或$ dirty。

我终于通过在productForm上面添加$ scope.formHolder对象来实现它:

plunkr

.controller('Ctrl2', function($scope) {
  $scope.product = {description:'pump'};
  $scope.output = 'unknown';
  // uncomment to avoid undefined error, still can't see $pristine
  $scope.formHolder = {};
  $scope.checkForm = function() {
    $scope.descriptionTest = $scope.product.description;
    if ($scope.formHolder.productForm.$pristine) {
      $scope.output = 'yes';
    }
    if ($scope.formHolder.productForm.$dirty) {
      $scope.output = 'no' 
    }
  }
});

HTML

<form name="formHolder.productForm" novalidate>

为什么这样做?还有更好的方法吗?

我这样结束是因为我有一个工作形式&amp;我想在其他地方重用的控制器/模板。我应该做一个指令,但一切都运行正常,除了表格的$ pristine和$ dirty特征 - 所有ng-model vars都正确传递。

How can I set a form contained inside a ng-include to be prestine?有一个答案,&#34;打破了所有规则&#34;但似乎更复杂。

当我写什么时,表单Controller将$ pristine添加到范围,以及范围?

编辑/答案:

我的原始问题可以归结为对form指令如何写入范围的混淆。我的印象是它需要

<form name="productForm">...

并为其添加属性,例如

$scope.productForm.$pristine = function() {...}

然而,它直接在productForm上写道:

$scope.productForm = formObject;

因此,表单对象存储在Child中,而不是父项,如所选答案中所述。

帮助我的子范围继承中的关键金点是链条在阅读时被咨询,但不是写作。因此,如果您设置类似于childScope.myThing.property =&#39; 123&#39;,虽然它看起来像写,但它首先必须执行读取以找出myThing是什么。设置childScope.myThing =&#39; 567&#39;是直接写,并不涉及查看父链。在What are the nuances of scope prototypal / prototypical inheritance in AngularJS?

中可以更好地解释这一点

3 个答案:

答案 0 :(得分:14)

要了解为什么使用formHolder工作的解决方案必须首先了解JavaScript prototypes chain。让我们在下面的伪代码中说明没有formHolder的第一种情况:

$parentScope = {
  //I'm a parent scope inside Ctrl2
  productForm:{} //to avoid undefined reference error 
}

$childScope = {
  //I'm a child scope created by by ng-include 
  __protototype__: $parentScope 
}

解析form指令时,会创建FormController,该$scopename属性值下指示的键下的$childScope.productForm = $formCtrl; 属性上设置。这几乎相当于:

$parentScope = {
  //I'm a parent scope inside Ctrl2
  productForm:{} //to avoid undefined reference error 
}

$childScope = {
  //I'm a child scope created by by ng-include 
  productForm: $formCtrl

  __protototype__: $parentScope 
}

之后,两个范围看起来像这样:

$parentScope = {
  //I'm a parent scope inside Ctrl2
  formHolder:{} //to avoid undefined reference error 
}

$childScope = {
  //I'm a child scope created by by ng-include 
  __protototype__: $parentScope 
}

所以你实际上最终得到了持有不同对象的 2个不同范围的属性。 现在在第二种情况下,您有以下情况:

form

FormController指令在$scope上设置$childScope.formHolder.productForm = $formCtrl; 实例时,它会使用不同的属性链

var formHolder = $childScope.formHolder; //since formHolder isn't defined on $childScope
//the JS runtime will look for it in the prototypes chain and find it inside $parentScope
//so here formHolder is the very same object you created and set on $parentScope
formHolder.productForm = $formCtrl;

这相当于写作:

{{1}}

希望有助于理解为什么第二个选项有效。至于你提出问题的第二部分 - 你的解决方案很简单且完全可行 - 但还有其他几种方法可以处理它,这最好取决于实际使用情况:

  • 使用不带子范围的指令来提取常用标记和部分功能
  • 使用带有子范围的指令,该子范围将通过直接父范围属性访问或通过发出的事件来传达状态更改
  • 使用不会创建子范围的custom include directive

答案 1 :(得分:1)

只需在控制器中定义变量(空对象),并在定义表单时使用它。由于角度JS使用范围原型,当表单将尝试访问内部范围(引导变量)时,它将首先通过范围链并尝试在父范围内找到相同的变量。

<!—- The vars should live in the controller. I placed them here for the example. -—>
<div ng-controller=“controllerName” ng-init="form={}; model={}" >
    <div ng-include=“ ‘path-to-the-template’ ”></div>
</div>

<!—- Inside path-to-the-template -—>
<form name="form.createUser">
    <input name="name" ng-model="model.name" />
    <input name="email" ng-model="model.email" />
</form>

参考链接http://blog.152.org/2014/07/angular-form-element-not-attaching-to.html

答案 2 :(得分:0)

我知道这是一个老问题,但我遇到了类似的问题,我更改了html并将我的ng-controller包含在html文件中。

所以而不是

<ng-include ng-controller="Ctrl2" src="'myForm.html'"></ng-include>

也改变它

<ng-include="'myForm.html'"></ng-include>

然后在myForm.html文件中,将代码包装在div中,并添加ng-controller属性,以便myForm.html成为

<div ng-controller="Ctrl2">
    <form name="productForm" novalidate>
      <h2>myForm</h2>
      description: <input type="text" name="description" ng-model="product.description"/>
      <br>
      <button ng-click="checkForm()">Check Form</button>
      <br>
      Form Pristine: {{output}}
      <br><br>
      I can see the description: {{descriptionTest}}
    </form>
</div>

现在您的子控制器在ng-include范围内