Подтвердить что ты не робот

Почему форма undefined внутри ng-include при проверке $pristine или $setDirty()?

Следующий код выдает ошибку "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' 
    }
  }
});

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 = {}; 

Это избавило от этой ошибки, но мой контроллер все еще не правильно видел $чистые или грязные.

Я, наконец, получил его работу, добавив объект $scope.formHolder выше 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>

Почему это работает? И есть ли лучший способ сделать это?

Я закончил так, потому что у меня была рабочая форма и контроллер/шаблон, которые я хотел бы использовать в другом месте. Я должен, вероятно, сделать директиву, но все отлично работало, кроме $первозданных и $грязных функций формы - все vg-модели vars прошли правильно.

Как я могу установить форму, содержащуюся внутри ng-include, чтобы быть престижной? имеет ответ, который "нарушает все правила", но казался более сложным.

Когда я пишу, когда форма Controller добавляет $pristine к области видимости и к какой области?

Изменить/Отклик:

Мой первоначальный вопрос можно свести к путанице о том, как директива формы записывает в область. У меня создалось впечатление, что это займет у нас

<form name="productForm">...

и добавить к нему свойства, например

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

однако он пишет непосредственно поверх productForm:

$scope.productForm = formObject;

Итак, объект формы хранится в Ребенке, а не в родительском, как описано в выбранном ответе.

Ключевой самородок в наследовании дочерней области, который помог мне, заключается в том, что цепочку консультируют при чтении, но не пишут. Поэтому, если вы установите что-то вроде childScope.myThing.property = '123', в то время как это похоже на запись, сначала нужно сделать чтение, чтобы узнать, что такое myThing. Принимая во внимание, что установка childScope.myThing = '567' является прямой записью и не предполагает вообще смотреть на родительскую цепочку. Это лучше объясняется в: Каковы нюансы объема прототипа/прототипического наследования в AngularJS?

4b9b3361

Ответ 1

Чтобы понять, почему решение с 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, которая устанавливается в свойстве $scope под ключом, указанным в значении атрибута name. Это в значительной степени эквивалентно:

$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 экземпляр 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;

Надеюсь, что это поможет понять, почему работает второй вариант. Что касается второй части вашего вопроса - ваше решение прост и прекрасно жизнеспособно, но есть несколько других способов обработки, которые лучше всего зависят от контекста использования:

  • с использованием директивы без детальной области для извлечения общей разметки и частей функциональности
  • с помощью директивы с дочерней областью, которая связывает изменения состояния либо через доступ к свойствам прямой родительской области, либо через выпущенные события
  • с помощью настраиваемой директивы include, которая не создавала бы дочернюю область

Ответ 2

Просто определите переменную (пустой объект) в контроллере и используйте ее при определении формы. Поскольку angular 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

Ответ 3

Я знаю, что это старый вопрос, но у меня была аналогичная проблема, и я изменил html и включил мой ng-контроллер в 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