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

Sco*_*oll 26 angularjs angularjs-ng-include

当我单击"检查"按钮时,以下代码抛出错误"TypeError:无法读取属性'$ pristine'的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' 
    }
  }
});
Run Code Online (Sandbox Code Playgroud)

HTML

  <body ng-controller="MainCtrl">
    <div >
      <ng-include ng-controller="Ctrl2" src="'myForm.html'"></ng-include>
    </div>
  </body>
Run Code Online (Sandbox Code Playgroud)

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>
Run Code Online (Sandbox Code Playgroud)

plunkr

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

$scope.productForm = {}; 
Run Code Online (Sandbox Code Playgroud)

这摆脱了错误,但我的控制器仍然没有正确看到$ 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' 
    }
  }
});
Run Code Online (Sandbox Code Playgroud)

HTML

<form name="formHolder.productForm" novalidate>
Run Code Online (Sandbox Code Playgroud)

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

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

如何将ng-include中包含的表单设置为prestine?有一个"打破所有规则"的答案,但似乎更复杂.

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

编辑/答案:

我的原始问题可以归结为形式指令如何写入范围的混淆.我的印象是它会把事情搞砸

<form name="productForm">...
Run Code Online (Sandbox Code Playgroud)

并添加属性,如

$scope.productForm.$pristine = function() {...}
Run Code Online (Sandbox Code Playgroud)

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

$scope.productForm = formObject;
Run Code Online (Sandbox Code Playgroud)

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

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

mie*_*sol 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 
}
Run Code Online (Sandbox Code Playgroud)

form解析指令时,会创建在属性值中指示的键下FormController$scope属性上设置的指令name.这几乎相当于:

$childScope.productForm = $formCtrl;
Run Code Online (Sandbox Code Playgroud)

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

$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 
}
Run Code Online (Sandbox Code Playgroud)

所以你实际上最终得到了不同作用域上的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 
}
Run Code Online (Sandbox Code Playgroud)

form指令在此时设置FormController实例$scope时,它使用不同的属性链:

$childScope.formHolder.productForm = $formCtrl;
Run Code Online (Sandbox Code Playgroud)

这相当于写作:

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;
Run Code Online (Sandbox Code Playgroud)

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

  • 使用没有子范围的指令来提取常见标记和部分功能
  • using子指令与子作用域,它通过直接父作用域属性访问或通过发出的事件来传递状态更改
  • 使用不会创建子范围的自定义include指令