Por que formar indefinido dentro do ng-include ao marcar $ pristine ou $ setDirty ()?

O código a seguir gera o erro "TypeError: Não é possível ler a propriedade '$ pristine' de indefinido" quando clico no botão "verificar".

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

O problema é que meu Ctrl2 não pode ver o productForm. No começo, pensei que isso tivesse a ver com a herança prototípica que o ng-include faz quando cria um escopo filho, então tentei adicionar uma variável no Ctrl2:

$scope.productForm = {}; 

Isso eliminou o erro, mas meu controlador ainda não estava vendo corretamente $ imaculado ou $ sujo.

Finalmente consegui fazê-lo adicionando um objeto $ scope.formHolder acima do productForm:

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>

Por que isso funciona? E existe uma maneira melhor de fazer isso?

Acabei assim porque tinha um formulário de trabalho e um controlador / modelo que queria reutilizar em outro lugar. Provavelmente eu deveria fazer uma diretiva, mas tudo funcionou bem, exceto os recursos $ pristine e $ dirty do formulário - todos os vars do modelo ng foram aprovados corretamente.

Como posso definir um formulário contido em um ng-include para ser prestine? tem uma resposta que "quebra todas as regras", mas parecia mais complicada.

Quando escrevo, quando o formulário Controller adiciona $ pristine ao escopo e a qual escopo?

Editar / Responder:

Minha pergunta original pode ser resumida em confusão sobre como a diretiva de formulário grava no escopo. Eu tive a impressão de que isso levaria a coisa

<form name="productForm">...

e adicionar propriedades a ele, como

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

no entanto, ele grava diretamente em cima do productForm:

$scope.productForm = formObject;

Portanto, o objeto de formulário é armazenado no Filho e não no pai, conforme explicado na resposta selecionada.

A pepita chave na herança do escopo filho que me ajudou é que a cadeia é consultada na leitura, mas não na escrita. Portanto, se você definir algo como childScope.myThing.property = '123', embora pareça uma gravação, primeiro será necessário fazer uma leitura para descobrir o que é o myThing. Considerando que definir childScope.myThing = '567' é uma gravação direta e não envolve a análise da cadeia pai. Tudo isso é melhor explicado em:Quais são as nuances da herança prototípica / prototípica do escopo no AngularJS?

questionAnswers(3)

yourAnswerToTheQuestion