web-dev-qa-db-ja.com

$ pristineまたは$ setDirty()をチェックするときにng-include内でundefinedを形成するのはなぜですか?

次のコードは、「チェック」ボタンをクリックすると、エラー「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を正しく認識していませんでした。

$ form.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>

なぜこれが機能するのですか?そして、これを行うより良い方法はありますか?

他の場所で再利用したい作業フォームとコントローラー/テンプレートがあったので、私はこのようになりました。おそらくディレクティブを作成する必要がありますが、フォームの$ pristineおよび$ dirty機能を除くすべてが正常に機能しました。すべてのng-model変数が正しく渡されました。

ng-include内に含まれるフォームをどのようにきれいにすることができますか? 「すべてのルールに違反する」という答えがありますが、より複雑に見えました。

フォームコントローラーがスコープに$ pristineを追加するのはいつですか、またどのスコープに追加しますか?

編集/回答:

私の元の質問は、フォームディレクティブがスコープに書き込む方法についての混乱に要約できます。中に入ると思った

<form name="productForm">...

そしてそれにプロパティを追加します

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

ただし、productFormの上に直接書き込みます。

$scope.productForm = formObject;

そのため、選択した回答で説明されているように、フォームオブジェクトは親ではなく子に保存されます。

子スコープの継承における重要なナゲットは、チェーンが読み取りでは参照され、書き込みでは参照されないことです。したがって、childScope.myThing.property = '123'のように設定すると、書き込みのように見えますが、最初に読み取りを実行して、myThingが何かを確認する必要があります。一方、childScope.myThing = '567'の設定は直接書き込みであり、親チェーンを確認する必要はありません。これはすべて、よりよく説明されています: AngularJSのスコーププロトタイプ/プロトタイプ継承のニュアンスは何ですか?

26
Scott Driscoll

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つの異なるスコープのプロパティさまざまなオブジェクトを保持することになります。 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ディレクティブが$scopeFormControllerインスタンスを設定する場合、今回は異なるプロパティチェーンを使用します

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

2番目のオプションが機能する理由を理解するのに役立つことを願っています。質問の2番目の部分について-ソリューションはシンプルで完全に実行可能です-しかし、実際の使用状況に応じて最適な方法で対処する方法が他にもいくつかあります。

  • 子スコープなしのディレクティブを使用して、共通のマークアップと機能の一部を抽出する
  • 直接の親スコーププロパティアクセスまたは発行されたイベントのいずれかを介して状態の変化を伝える子スコープでディレクティブを使用する
  • 子スコープを作成しない カスタムインクルードディレクティブ を使用する
14
miensol

コントローラーで変数(空のオブジェクト)を定義し、フォームを定義するときにそれを使用するだけです。 angular JSは内部でスコーププロトタイプを使用するため、フォームが内部スコープにアクセスしようとすると(bootstrap変数に))、最初にスコープを経由しますチェーンして、親のスコープで同じ変数を見つけようとします。

<!—- 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

1
kirill.buga

これは古い質問であることはわかっていますが、同様の問題があり、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スコープ内にあります

0
Gillardo