26

当我单击“检查”按钮时,以下代码会引发错误“TypeError: Cannot read property '$pristine' of undefined”。

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>

为什么这行得通?有没有更好的方法来做到这一点?

我以这种方式结束了,因为我有一个工作表单和控制器/模板,我想在其他地方重用。我可能应该做一个指令,但是除了表单的 $pristine 和 $dirty 特性之外,一切都很好——所有的 ng-model 变量都被正确传递了。

如何将包含在 ng-include 中的表单设置为 prestine?有一个“违反所有规则”的答案,但似乎更复杂。

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

编辑/回答:

我最初的问题可以归结为对表单指令如何写入范围的混淆。我的印象是它会把东西放进去

<form name="productForm">...

并为其添加属性,例如

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

但是,它直接写在 productForm 之上:

$scope.productForm = formObject;

因此,表单对象存储在子项中,而不是所选答案中解释的父项。

对我有帮助的子范围继承中的关键金块是在阅读时咨询了链条,而不是写作。因此,如果您设置了 childScope.myThing.property = '123' 之类的内容,虽然它看起来像写入,但它首先必须进行读取以找出 myThing 是什么。而设置 childScope.myThing = '567' 是直接写入,根本不涉及查看父链。这在下面得到了更好的解释:AngularJS 中范围原型/原型继承的细微差别是什么?

4

3 回答 3

14

要了解为什么解决方案有效,formHolder您必须首先了解JavaScript 原型链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;

之后 2 个作用域如下所示:

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

因此,您实际上最终在不同的范围内获得了 2 个属性,其中包含不同的对象。现在在第二种情况下,您有以下情况:

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

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;

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

  • 使用没有子范围的指令来提取通用标记和部分功能
  • 使用带有子范围的指令,该指令将通过直接父范围属性访问或通过发出的事件来传达状态更改
  • 使用不会创建子范围的自定义包含指令
于 2014-05-15T20:23:40.697 回答
1

我知道这是一个老问题,但我遇到了类似的问题,我更改了 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 范围内

于 2015-05-21T10:08:22.737 回答
1

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

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

<!—- Inside path-to-the-template -—&gt;
<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

于 2015-10-29T21:23:25.297 回答