AngularJS:1.2から1.4への移行、パート2

最初の部分では、新しいバージョンへの移行の主な問題をすべて検討しましたが、これで何をしたのかについて触れます。







画像







前述のように、移行の主な理由は、アプリケーションの速度が大幅に向上したことです:Jeff CrossとBrian FordがngEurope会議で述べたように、DOMでの操作が4.3倍、 $digest



サイクルが3.5倍(1.2と比較) 。







ただし、この速度は主に内部の最適化と魔法によるものではなく、コードをより効率的に記述できるツールの提供によるものです。







これらのツールを見てみましょう!







デバッグ情報



アンギュラーがリソースの大部分を、クラスの追加などのデバッグを容易にする情報に費やしていることはニュースではありません

DOM要素( ng-binding



およびng-isolated-scope



クラスなど)またはそれらにさまざまなメソッドをアタッチしてscope



にアクセスしscope



(たとえば.scope()



および.isolateScope()



)。







これらはすべて、分度器やバタランなどのツールの作業に役立ち、必要ですが、このデータは生産に必要ですか?







バージョン1.3以降、デバッグ情報を無効にできます。







 app.config(['$compileProvider', function ($compileProvider) { $compileProvider.debugInfoEnabled(false); }]);
      
      





しかし、製品を販売する必要があり、デバッグが無効になっている場合はどうでしょうか?







ここでは、 angular



オブジェクトの.reloadWithDebugInfo()



メソッドが保存され、 angular



オブジェクトはグローバルなので、コンソールからこのコードを簡単に実行できます。







 angular.reloadWithDebugInfo();
      
      





$ applyAsync



バージョン1.3では、 $applyAsync



サービスが$applyAsync



れました。多くの点で、そのメカニズムは既存の$evalAsync



サービスと似ています。







簡単に言えば、式をキューに追加してから待機し(setTimeout(...、0)を設定し、最新のブラウザーでは約10ミリ秒です)、過去に別の式がキューに追加されていない場合は、開始します

$rootScope.$digest()









これにより、同じ$ダイジェストループで実行され、$ applyを頻繁に呼び出すことを心配せずに、DOMに影響する多くの同時式を実行できます。







$ evalAsyncとの違いは何ですか?







主な違いは、 $applyAsync



自体がダーティチェックの前に$digest



サイクルの開始時に式キュー全体を実行することです。これにより、 $evalAsync



はダーティチェック中に実行さ $evalAsync



(より正確には、 (ダーティチェックサイクルの開始)、およびキューに追加された式($ウォッチの外部)は、$ダイジェストサイクルを再び開始します。これにより、同じ$digest



式が繰り返し実行されます。







[詳細]







ただし、このツールを使用することの本当の利点は、角度の内部サービス、たとえば$httpProvider



ます。







$ http



$http



サービスとXHRリクエストを行う他の方法との主な違いは、完了時に$apply



呼び出すことです。







問題は多くの並列クエリであり、それぞれが完了時に$apply



し、ブレーキにつながります。







この問題は、バージョン1.3の $applyAsync



の到着により解決されました。







 app.config(function ($httpProvider) { $httpProvider.useApplyAsync(true); });
      
      





このコードには、 $applyAsync



内での$httpProvider



$applyAsync



使用が含まれ$applyAsync









これにより、同時要求のすべての約束がresolved



ときに1回だけ$ applyを実行でき、パフォーマンスが大幅に向上します。







一度バインド



AngularJSの主なパフォーマンスの問題の1つは、双方向のバインディングがすべての式に適用されますが、すべてのデータがそれを必要とするわけではないという事実に起因する$watch



の膨大な量です。







静的データの場合、一晩バインドするだけで十分であり、カスタムディレクティブによってこれが決定される前に、バージョン1.3ですべてが変更されました。







バージョン1.3以降、新しい構文は、式の先頭に::



の形式で使用できます。







::



始まる式は一方向のバインディングとして認識され、式のデータが安定して最初の$digest



サイクルが経過するとすぐに追跡(監視解除)されなくなります。







例:







 {{:: foo }} <button ng-bind=":: foo"></button> <ul> <li ng-repeat=":: foo in bar"></li> </ul> <custom-directive two-way-bind-property=":: foo"><custom-directive>
      
      





データの安定性:







データは、 undefined



限り不安定であると見なされます。 NaN



false



''



[]



またはnull



いずれであっても、他のデータは安定していると見なされ、 unwatch



式になります。







これは、最初の$ダイジェストサイクル中に利用できない静的データに必要です。







たとえば、データがサーバーから送信undefined



れた場合、リクエストを待機している間、変数の値をundefined



ままにすることができます。 この間ずっと、この式のウォッチャーは存続し、 undefined



以外のデータをインストールした後にのみ、その結果を返します。







::



を含む式を定数として扱います。一度設定すると、変更できなくなります。







更新不可の更新:







100のうち99のケースで更新されない式があると仮定します(つまり、オーバーウォッチでそれに陥ることはありませんでした)が、場合によっては必要になります。 になる方法 バインドワンス式を強制的に更新できるかどうか疑問に思いました。







いいえ、できません:)ただし、独自の属性ディレクティブを作成して、特定のイベントに応じてディレクティブ全体を再コンパイルするように強制できます。 ここに例があります










パフォーマンスのこの部分は終了し、素敵な利点について話すことができます。







ngModelオプション



バージョン1.3では、 ng-model



に加えて、モデルが更新されるタイミングを担当ng-model



補助ディレクティブng-model-options



導入されました。







更新時間は2つの要因に依存します。







1) updateOn



モデルが更新される特別なイベント(イベント)。たとえば、 blur



click



またはある種のカスタムイベントです。 デフォルトは常にdefault



。つまり、各コントロールは独自のイベントを使用します。 {event: "default customEvent"}



イベントを追加して標準イベントを拡張するdefault



は、リストにdefault



を追加することを忘れないでください(例: {event: "default customEvent"}









2) debounce



-新しいデータを見越してモデルを更新するときの遅延(デフォルトは0、つまり即時)。 入力{debounce: 300}



を指定し、300ミリ秒未満の間隔で3文字を入力すると、モデル(およびさまざまなモディファイヤ/バリデータ)が一度だけ更新されます。 さらに、 debounce



をイベントと組み合わせて、各イベントの遅延を示すことができます。例: {event: "default customEvent", debounce: {default: 0, customEvent: 400}}









これにより、多くの自転車をなくすことができ(さよならsetTimeout / clearTimeout)、パフォーマンスが大幅に向上し( $digest



$watchers



すべての$watchers



の無駄な再起動が$watchers



)、また非同期検証の誤検知の数が減ります(ただし、 $http



このサービスは、リクエストをスパムしないように十分にスマートですが、安定したデータを待ちます。







しかし、さらに3つの便利なオプションがあります。



allowInvalidフラグを使用すると、値が無効な場合でも$modelValue



を設定できます (デフォルトでは、値は無効ですが、 undefined



モデルに書き込まれます。これにより、たとえば中間値を見つけることができません)







setterGetterフラグを使用すると、独自の関数をngModel



として設定できます 。これはngModel.viewValue



ngModel.modelValue



ngModel.viewValue



間の一種であり、セッターおよびゲッターとして機能します。 plunkerのライブ例







タイムゾーンを使用すると、時間( date



またはtime



)に関連するコントロールのタイムゾーンを設定できます。たとえば、 '+0430'



は「4時間30分GTM」を意味します。 デフォルトでは、ブラウザのタイムゾーンが使用されます。







updateOnとデバウンスを無視する



モデルを手動で記録する場合、イベントで設定された遅延を無視して、すぐに更新を実行する必要がある場合があります。 これにはngModelCtrl.$commitViewValue()



メソッドがあります。







変更をキャンセル



debounce



プロセス中にハングしているすべての変更を元に戻したい場合、ビューをモデルの現在の状態に調整するメソッド$rollbackViewValue()



(以前の$cancelUpdate()



)があります。







この機能の使用例として、公式ドキュメントに入力があり、ESCを押すと変更をロールバックできます。







詳細なドキュメント







検証



ユーザビリティの面での主要な改善点の1つは、フォームの検証です。







以前は、 ndModel.$formatters



およびndModel.$parsers



を介して検証メカニズムを実装する必要があり、 ndModel.$setValidity()



を介して検証結果に直接影響し、非同期チェックの実装は別の喜びでした。







イノベーションはパフォーマンスにも影響しました。







実際、DOM( $parsers



)またはモデル( $formatters



)で更新されるたびに、以前に検証されていた関数が起動され、しばしば互いの値に影響を与え、検証サイクルを再開しました。







新しいバージョンでは、モデルが変更され、このモデルにエラーがない場合( {parse: true



)にのみ検証が開始され{parse: true



。 モデル自体またはバリデーター内のコントロールの表現への影響も排除され、アプリケーションの速度にプラスの影響を与えます。







$フォーマッターおよび$パーサーとは何ですか、なぜ作成されるのですか? それらは削除されますか?

いいえ、削除されませんでした。これらは他の目的のための他のツールであり、まだ必要です。







$formatters



$parsers



はどちらも、値を受け取り、チェーンに沿って次のハンドラー関数に渡すハンドラー関数を含む配列です。 チェーン内の各リンクは、値を変更してから渡すことができます。







$フォーマッター

モデルが変更されるたびに、 $formatters



は配列内のハンドラーを逆の順序(末尾から先頭)で$formatters



ます。 最後に渡された値は、DOMでモデルがどのように表現されるかを決定します。 言い換えると、 $formatters



ngModelCtrl.$modelValue



ngModelCtrl.$modelValue



に変換される方法を担当します。







$パーサー

コントロールがDOMから値を読み取るたびに、 $parsers



はハンドラーの配列を最初から最後まで$parsers



、チェーンに沿って値を渡します。 最後に渡された値は、モデルで値がどのように表現されるかを決定します。 言い換えると、 $parsers



ngModelCtrl.$viewValue



ngModelCtrl.$viewValue



に変換される方法を担当します。







どこで使用されていますか?

まず第一に、アングル自身が彼の作品でそれらを使用しています。 たとえば、最小長の検証を使用してコントロールを作成し、 $formatters



チェックすると、空ではないが、DOMから文字列に値を変換するラッパー関数が既に含まれていることがわかります。







上記の例は、厳密に定義された形式でのみモデルに値が入っていることを確認したい場合、ハンドラーを使用して値を前処理(サニタイズ)します。







他の2つの一般的な使用方法は、値の双方向フィルタリング(たとえば、ユーザーが入力に「10,000」を入力し、「10000」がモデルに保存され、その逆の場合)とマスクの作成(ユーザーが電話マスク「+7(000 )000-00-00 "、およびモデルに保存する" 70000000000 ")。







例からわかるように-不可欠なツール。







検証は以前どのように機能しましたか?

古い検証方法に関する包括的な情報は、Habréに関する次の記事から入手できます。









将来、古い検証方法を使用しないのはなぜですか?

事実は、 ndModel.$parsers



からundefined



を返すことですndModel.$parsers



は、 ngModelCtrl.$modelValue



を公​​開し、 ngModelCtrl.$modelValue



undefined



に公開し、 ngModelCtrl.$modelValue



{parse: false}



ngModelCtrl.$modelValue



障害フィールド。







この場合、バリデーター( $validators



$asyncValidators



)は作業を開始しません。







allowInvalid



フラグをtrue



設定することにより、 ngModelOptions



この動作を無効にできtrue









バージョン1.3から 、便利な同期および非同期検証のためのツールが用意されました。







また、新しいバリデーターの作業はモデルの更新に関係しているため、上記のngModelOptions



にも依存していることにも言及する価値があります。







同期



同期検証の場合、新しいバージョンはndModel.$validators



コレクションを提供し、検証関数で拡張します。







バリデータ関数は、有効な値と無効な値に対してそれぞれtrue



またはfalse



を返す必要があります。







例:







  ngModel.$validators.integer = function(modelValue, viewValue) { // ,      if (ctrl.$isEmpty(modelValue)) { return true; } if (INTEGER_REGEXP.test(viewValue)) { return true; //   } return false; //    };
      
      





非同期



非同期検証の場合は、 ngModelCtrl.$asyncValidators



コレクションが使用され、同じロジックが検証関数でそれを拡張します。







非同期バージョンの主な違い:









AngularJSの約束は、特別なサービス$q



、基本的に$q



使用する$q



$timeout



$http



などのサービスによって生成されます。







このコレクションには、1つのバリデーターによって返される複数のプロミスが含まれていません。 つまり、検証を数回呼び出すと、以前のプロミスの結果に関係なく、バリデーターの最後の呼び出しからのプロミスのみが考慮されます。







バリデーターがプロミスを渡してから解決するまで( resolve()



またはreject()



)、 ngModelCtrl.$pending



フィールドにはバリデーター名が格納され、 ngModelCtrl.$valid



ngModelCtrl.$invalid



およびngModelCtrl.$invalid



undefined









この機能には注意してください:検証がFormCtrl.$errors



ている限りFormCtrl.$errors



フォームは無効になりますが、 FormCtrl.$errors



Errorsにはエラーが表示されませんが、 FormCtrl.$pending



は「保留中」のバリデータがFormCtrl.$pending









例:
  ngModelCtrl.$asyncValidators.username = function(modelValue, viewValue) { // ,      if (ctrl.$isEmpty(modelValue)) { return $q.when(); } var def = $q.defer(); //    $timeout(function() { if (usernames.indexOf(modelValue) === -1) { def.resolve(); //  ,   } else { def.reject(); //  ,    } }, 2000); return def.promise; }; ngModelCtrl.$asyncValidators.uniqueUsername = function(modelValue, viewValue) { var value = modelValue || viewValue; // ,     return $http.get('/api/users/' + value). then(function resolved() { //  ,   ,     return $q.reject('exists'); }, function rejected() { //   ,  ,    return true; }); };
      
      





値修飾子を使用するフィールドで( $formatters



および$parsers



を介して)非同期検証を慎重に使用することは価値があります。これにより、複数の操作、およびフィールドの誤った検証または無効化が発生する可能性があります。







そのような場合の検証例
 var pendingPromise; ngModelCtrl.$asyncValidators.checkPhoneUnique = function (modelValue) { if (pendingPromise) { return pendingPromise; } var deferred = $q.defer(); if (modelValue) { pendingPromise = deferred.promise; $http.post('/', {value: modelValue}) .success(function (response) { if (response.Result === '   ') { deferred.resolve(); } else { deferred.reject(); } }).error(function () { deferred.reject(); }).finally(function () { pendingPromise = null; }); } else { deferred.resolve(); } return deferred.promise; };
      
      





ngMessages



ngMessages



モジュールngMessages



、ページ上のメッセージの表示を容易にするように設計されています。







このモジュールはページ上のあらゆるメッセージに便利に使用できるという事実にもかかわらず、通常はフォームのエラーを表示するために使用されます。 このツールが解決する問題を理解するために、エラーを表示する古い方法の苦痛を見て、それを新しいものと比較しましょう。







比較の例として、このフォームを使用してコメントを追加します。







 <form name="commentForm"> <ul class="warnings" ng-if="commentForm.$error && commentForm.$dirty"> ... </ul> <label>:</label> <input type="text" name="username" ng-model="comment.username" required> <label>:</label> <textarea name="message" ng-model="comment.message" minlength="5" maxlength="500"></textarea> </form>
      
      





検証の実装はそのままにして、必要な条件に関するメッセージの表示のみを考慮します。









古いエラー表示方法



この例は長く出てきたので、私はそれをネタバレの下に隠しました。

次に、これらのフィールドのエラーを表示する方法を想像してください。







 <form name="commentForm"> <ul class="warnings" ng-if="commentForm.$error && commentForm.$dirty"> <span ng-if="commentForm.message.$error.minlength">      5  </span> <span ng-if="commentForm.username.$error.maxlength">      500  </span> <span ng-if="commentForm.username.$error.required">    </span> </ul> <label>:</label> ... <label>:</label> ... </form>
      
      





まあ、物事がそれほど悪くない限り、そうですか?

しかし、なぜニックネームのメッセージとコメントがすべて一度に表示されるのですか? 順次表示を追加してこれを修正しましょう。







 <form name="commentForm"> <ul class="warnings" ng-if="commentForm.$error && commentForm.$dirty"> <span ng-if="commentForm.message.$error.minlength && commentForm.username.$valid">      5  </span> ... </ul> <label>:</label> ... <label>:</label> ... </form>
      
      





あなたはまだ「これはそれほど悪くない」と思いますか? ページのフィールドが2ではなく20であり、それぞれに少なくとも5つの投稿があると想像してください。 同様のスタイルで、私たちのページはすぐに状況が変わるとゴミ箱に変わります。







もちろん、このタスクを実装するためのベストプラクティス、 FormController



の動作を拡張する特別なディレクティブおよび松葉杖がありFormController



(たとえば、プロジェクトでは、現在のエラーを格納するすべてのコントロールがshowError



プロパティで拡張されました) 。







新しいエラー表示方法



接続

ngMesssages



別のモジュールとして提供されており、作業を開始する前に接続する必要があります。 パッケージマネージャーを使用してモジュールをダウンロードまたはインストールし、プロジェクトに接続します。







 <script src="path/to/angular-messages.js"></script>
      
      





依存する追加:







 angular.module('myApp', ['ngMessages']);
      
      





このモジュールの基本的な作業は、2つのディレクティブを使用することです。







  1. ng-messages



    を含むコンテナ
  2. ng-message



    直接ng-message





ng-messages



は、引数として、 ng-messages



がチェックおよびng-messages



されるキーのコレクションを受け取ります。これらのキーは、比較のための引数として文字列または式を受け取ります( 1.4から開始)。







コメントを追加するための同じフォームの例を繰り返しますが、 ngMessages



ます。







 <div ng-messages="commentForm.message.$error" class="warnings"> <p ng-message="minlength">      5  </p> <p ng-message="maxlength">      500  </p> <p ng-message="required">    </p> </div>
      
      





ngMessages



は要素としてngMessages



使用できます:







 <ng-messages for="commentForm.message.$error" class="warnings"> <ng-message when="minlength">      5  </ng> <ng-message when="maxlength">      500  </ng> <ng-message when="required">    </ng> </ng-messages>
      
      





したがって、 ngMessages



はすぐに2つの問題を解決しました。









さらに、メッセージ出力の優先順位付けの問題にも対処しています。 ここではすべてが簡単です。メッセージは、DOM内の場所に従って表示されます。







複数のメッセージ出力を有効にするには、 ng-messages-multiple



属性をng-messages-multiple



ディレクティブに追加しng-messages-multiple









 <ng-messages ng-messages-multiple for="commentForm.message.$error"> ... </ng-messages>
      
      





ngMessages



, , , :







, 1.4, 1.3

1.3 ng-messages-include



ng-messages



, ng-messages-include



ng-message



:







1.3







 <ng-messages ng-messages-include="length-message" for="commentForm.message.$error"> </ng-messages>
      
      





1.4+







 <ng-messages for="commentForm.message.$error"> <ng-messages-include="length-message"></ng-messages-include> </ng-messages>
      
      





(, ) , :







 <script type="script/ng-template" id="length-message"> <ng-message when="minlength">     </ng-message> </script> ... <ng-messages for="commentForm.message.$error"> <ng-messages-include="length-message"></ng-messages-include> </ng-messages> <ng-messages for="anotherForm.someField.$error"> <ng-messages-include="length-message"></ng-messages-include> </ng-messages>
      
      





, , . .







, 1.4 , ng-message-exp



:







 // error = {type: required, message: '    '}; <ng-messages for="commentForm.message.$error"> <ng-message-exp="error.type"> {{ error.message }} </ng-message-exp> </ng-messages>
      
      





ng-message-exp



, ng-message



, ( expression



). , , , AJAX .







– . ng-messages



!







:









コントローラー



bindToController



[]







, controller as



, scope



.







, this.something



. .







:







 app.directive('someDirective', function () { return { scope: { name: '=' }, controller: function () { this.name = 'Foo' }, controllerAs: 'ctrl' ... }; });
      
      





name



.







:







  $scope.$watch('name', function (newValue) { this.name = newValue; }.bind(this));
      
      





, , , , ?







1.3 :







bindToController



.







 app.directive('someDirective', function () { return { scope: { name: '=' }, controller: function () { this.name = 'Foo' }, bindToController: true, ... }; });
      
      





ctrl.name



$scope.name



.







1.4 :







 app.directive('someDirective', function () { return { scope: true, bindToController: { name: '=' }, controller: function () { this.name = 'Foo' }, ... }; });
      
      





scope



bindToController



.







, bindToController



, , , scope



, scope



.







- scope



, true



. bindToController



scope



.







フィルター



 {{ expression | filter }}
      
      







, , «» ( expression



), ( filter



). , , , .







1.3 : , . , , $digest



, , . , , .







plunker







: , , - ? , , «» , ?







このため、静的( stateless



)および動的( stateful



)フィルターの概念は1.3で導入されました。 デフォルトでは、フィルターはstateless



ように動作します。 , $stateful



true



.







例:







 angular.module('myApp', []) .filter('customFilter', ['someService', function (someService) { function customFilter(input) { //     someService input += someService.getData(); return input; } customFilter.$stateful = true; return customFilter; }]);
      
      





重大な変更:

注意深い読者は、そのような変更が実際に古い動的フィルターの動作を壊す可能性があることに気付いているかもしれません。 残念ながら、最初の部分でこの機能を説明するのを忘れていましたが、そのような機会は検討する価値があります。







dateFilter





weeks









おわりに



それだけです - , .







, .







, markdown. - html.







, , :)







画像








All Articles