Angular2のテンプレート構文を理解する



Angular2テンプレートの構文を初めて見た人の多くは、彼らがどのような恐怖をしたかを嘆き始めます。実際、少なくともAngular1のようには不可能でした。 さまざまな括弧、アスタリスク、およびその他のナンセンスを導入する必要があった理由! ただし、詳しく調べると、すべてがはるかに簡単になります。主なことは怖がらないようにすることです。



AngularJSのテンプレートはその不可欠な部分であるため、このフレームワークの新しいバージョンを初めて知ったときにそれらに対処することが重要です。 同時に、この構文が角度1.xと比較してどのような利点をもたらすかについて説明します。 そして、小さな例を使用してこれを検討することが最善です。



この記事の大部分は、次の2つの記事の資料に基づいています。







材料の流れを単純化するために、理解しましょう。 AngularJSでは、Angular 1.xブランチ全体を意味し、Angular2では2.xブランチを意味します。



記事の本文に貴重な追加加えてくれたブロンクス氏にも感謝します。



注:週末の夜、タイプミスなどのため PMで通知します。 どうもありがとうございました。





プロパティアイテムのバインド



単にデータを出力するだけなら違いは感じられませんが、状態の一部を要素の属性の値として渡すことを決めた場合、すでに興味深い解決策を観察できます。



AngularJS属性バインダーの動作を更新しましょう。 これを行うには、通常、式を直接テンプレートのコンパイル時に補間して要素の属性に直接挿入するか、多くのディレクティブの1つを使用しました。 たとえば、 ngValue



は、入力のvalueプロパティの値を設定します。



これがAngularJSでどのように機能するかの例を次に示します。



 <input ng-value="expression" />
      
      





また、式の結果を引数の値として直接補間できることを忘れないでください。



 <input value="{{expression}}" />
      
      





興味深い機能に注意してください。 2番目のオプションでは、角度が値を補間する前に中間状態を確認できるため、多くの人はこれを避けます。 ただし、最初のオプションはディレクティブを使用します。 つまり、すべてが私たちにとって良い、美しく、便利であるためには、すべての要素の各プロパティにディレクティブを作成する必要があります。 同意し、あまり便利ではありません。 角度に値をマップするように指示する属性の指定を追加しないのはなぜですか。 そして、構文が有効であればいいと思います。 そして、彼らは、このために、関心のある属性(任意の属性)を角かっこ- []



です。



 <input [value]="valueExpression" [placeholder]="placeholderExpression" />
      
      







本質的に、構文[]



bind-prop



省略表記にすぎません。 砂糖を削除すると、上記の例は次のように記述されます。



 <input bind-value="valueExpression" bind-placeholder="placeholderExpression" />
      
      







ただし、Javascriptで属性を変更する方法を覚えておいてください。



 element[prop] = 'value';
      
      







ここからこれらの角括弧が由来します。 値が要素のプロパティに直接マッピングされることを説明します。 つまり、 ng-bind-html



ディレクティブの代わりに、式の結果を[inner-html]



バインドするだけです。 または、 ng-hide



代わりに、要素自体の[hidden]



プロパティを使用できます。 これにより、知る必要のある角度固有の情報の数が大幅に削減され、DOMからの抽象化を維持したままDOMに近づけることができます。



まあ、AngularJSの場合と同様に、補間された値をマッピングする機能もあることを示すことで、この質問を終了する価値があります。



 <input value="{{ valueExpression }}" placeholder="{{ placeholderExpression }}" />
      
      









イベント



AngularJSでは、特別なディレクティブを使用して要素イベントをサブスクライブできました。 プロパティと同様に考えられるイベント全体を処理する必要があります。 そして、イベントごとに指令を作成しなければなりませんでした。 おそらく、これらのディレクティブの中で最も人気のあるものはngClick



です:



 <button ng-click="doSomething($event)">
      
      





要素のプロパティについてこのような問題をすでに解決していることを考えると、おそらくこの問題も解決する価値があるでしょうか? それはまさに彼らがしたことです! イベントをサブスクライブするには、次の構文を使用して属性を登録するだけで十分です: (eventName)



。 したがって、別のディレクティブを記述する必要なしに、要素によって生成されたイベントをサブスクライブする機会があります。



 <button (click)="doSomething($event)">
      
      







プロパティバインディングと同様に、これも記録のための砂糖です。 つまり、上記の例は次のように書くことができます。



 <button on-click="doSomething($event)">
      
      







また、ブラケットの種類は、javascriptのイベントハンドラーの確立との関連付けから取得されます。



 element.addEventListener('click', ($event) => doSomething($event));
      
      







また、AngularJSのイベントバインディングと比較した場合の動作の重要な違いに注目する価値があります。 現在、Angular2は、存在しないメソッドの呼び出しの場合にエラーをスローします。 javascriptでコードを直接呼び出しているかのように。 この文脈でタイプミスのために愚かなバグを抱えた人の多くは幸せになると思います。 場合によっては、これにより特定の不便さが追加されるため、エルビス演算子も追加されましたが、これについては後で説明します。





両面バインディング



両面バインディングは悪いことであり、これが角の主な罪であると広く信じられています。 これは完全に真実ではありません。 AnugularJSのバイラテラルバインディングの問題は、開発者に代替手段を提供するのではなく、あらゆる場所で使用されることです(おそらく、この状況はすぐに変わるでしょう)。



それでも、特にフォームの場合、両面バインディングによって開発が大幅に簡素化される場合があります。 それでは、Angular2でどのように実装されていますか? プロパティ要素の一方向バインディングとイベントバインディングで双方向バインディングを整理する方法を考えてみましょう。



 <input type="text" [value]="firstName" (input)="firstName=$event.target.value" />
      
      





繰り返しますが、あまり便利ではありません。 したがって、Angular2にはngModel



を使用した構文糖衣もあります。 結果は上記で与えたものと同じになります。



 <input type="text" [(ngModel)]="firstName" />
      
      





ローカル変数



同じテンプレート内の要素間でデータを転送するには、ローカル変数が使用されます。 おそらく、AngularJSで最も近い類推は、ngFormを介した名前によるフォーム要素へのアクセスです。 もちろん、これは完全に正しい比較ではありませんngForm



ディレクティブが原因でのみ機能するためです。 Angular2では、ローカル#



変数を使用して、テンプレート要素内の任意のオブジェクトまたはDOM要素とその子孫への参照を使用できます。



 <video #movieplayer ...> <button (click)="movieplayer.play()"> </video>
      
      





この例では、 movieplayer



変数を使用して、テンプレート内のメディア要素APIに直接アクセスする方法を確認できます。



#



文字に加えて、 var-



プレフィックスを使用して変数を宣言することもできます。 この場合、 #movieplayer



代わりにvar-movieplayer



書くことができます。



ローカル変数のおかげで、一部の要素のアクションが他の要素の何かを変更するたびに新しいディレクティブを作成する必要がなくなりました。 たとえば、上記の例では、ビデオの視聴を開始するボタンをすばやく追加できます。 これは、実際には、Angular2とAngularJSの主な概念上の違い、無駄なマイクロディレクティブの減少、コンポーネントへの注意の集中です。





アスタリスク(記号*)



*



文字はほとんどの質問を発生させます。 なぜ必要なのか見てみましょう。 このシンボルを追加する理由を理解するには、 template



などの要素を思い出してtemplate







template



要素を使用すると、後で初期化できるDOMスライスを宣言できるため、パフォーマンスが向上し、リソースをより効率的に使用できます。 いくつかの点で、これはHTMLのコンテキストでdocumentFragment



に似ています。



おそらくそれがなぜ必要なのかを例で示すほうが簡単でしょう:



 <div style="display:none"> <img src="path/to/your/image.png" /> </div>
      
      





この小さな例では、ブロックが非display:none



ていることがわかります( display:none



)。 ただし、必要ではない場合でも、ブラウザは引き続き画像をダウンロードしようとします。 ページに多くの類似したものがある場合、これはページの全体的なパフォーマンスに悪影響を与える可能性があります。



この問題の解決策は、 template



要素を使用することです。



 <template> <img src="path/to/your/image.png" /> </template>
      
      





この場合、テンプレートを初期化するまでブラウザは画像をロードしません。



しかし、ラムに戻りましょう。 elementディレクティブの前に*



文字を使用すると、Angularはコンパイル中にテンプレート内の要素をラップできます。 例を見るのが簡単です:



 <hero-detail *ngIf="isActive" [hero]="currentHero"></hero-detail>
      
      





このテンプレートは次のように変換されます。



 <template [ngIf]="isActive"> <hero-detail [hero]="currentHero"></hero-detail> </template>
      
      





ngFor



ngIf



ngSwitch



ような条件付きディレクティブを使用する場合、このシンボルが構文シュガーを提供してパフォーマンスを向上させることは明らかです。 isActive



がtrueになるまで、 hero-detail



コンポーネントをインスタンス化する必要がないことは論理的です。



パイプ



パイプは、AngularJSのフィルターの直接的な類似物です。 一般に、アプリケーションの構文はあまり変更されていません。



 <p>My birthday is {{ birthday | date:"MM/dd/yy" }} </p>
      
      





すでにおなじみのフィルターから新しいパイプに名前を変更する必要があったのはなぜですか? これは、フィルターの新しいメカニズムを強調するために行われました。 現在、これらは同期フィルターではなく、非同期パイプです(UNIXパイプに似ています)。



AngularJSでは、$ダイジェストサイクルごとにフィルターが同期的に実行されます。 これは、AngularJsの変更追跡メカニズムに必要です。 Angular2では、変更の追跡ではデータの依存関係が考慮されるため、多くの概念を最適化できます。 ステートフルパイプとステートレスパイプの分離も現れました(AngularJSフィルムは明らかにステートフルと見なされていました)。



名前が示すように、ステートレスパイプには独自の状態がありません。 これらは純粋な関数です。 それらは一度だけ実行されます(または入力データが変更された場合)。 Angular2のほとんどのパイプはステートレスパイプです。 これにより、生産性が大幅に向上します。



それどころか、ステートフルパイプには独自の状態があり、内部状態が変化する可能性があるために実行されることがよくあります。 そのようなパイプの例はAsync



です。 彼は入力で約束を受け取り、変更をサブスクライブし、解決された値を返します。



 //   TypeScript,  babel  stage-1,  ,   @Component({ selector: 'my-hero', template: 'Message: {{delayedMessage | async}}', }) class MyHeroAsyncMessageComponent { delayedMessage = new Promise((resolve, reject) => { setTimeout(() => resolve('You are my Hero!'), 500); }); } // ? ,   TypeScript   .
      
      





この例ではmy-hero



コンポーネントはMessage: You are my Hero!



を出力しますMessage: You are my Hero!



delayedMessage promiseがdelayedMessage



後にのみ。



ステートフルパイプを作成するには、そのメタデータで明示的に宣言する必要があります。 それ以外の場合、Angular2はそれをステートレスと見なします。



エルビス演算子



AngularJSでは、完全に痛みのないものを呼び出すことができました。これにより、非常に陰湿なバグが発生し、デバッグが困難になりました。 おそらくngClick



タイプミスを処理する必要がありました。この場合、コードは必要なアクションを実行せず、フレームワークは何が間違っているかについてのヒントを提供しませんでした。 Angular2では、最終的にエラーが発生します! ただし、このような解決策は、追加の砂糖なしでは誰にもアピールしない場合があります。



Javascriptでは、多くの場合、プロパティをチェックする必要があります。 私たちは皆このようなことを書いたと思う:



 if (cordova && cordova.plugins && cordova.plugins.notification){ // use cordova.plugins.notification }
      
      





そのようなチェックを行うことで、これを確実に回避したいと思います。



 TypeError: Cannot read property 'notification' of undefined.
      
      





この問題を解決するために、 エルビス演算子は三項演算子の短縮バージョンとして導入されました。 Coffeescriptでも同様のものを見ることができます。 Angular2は同じ演算子を使用して、テンプレートレベルでこの問題を解決しました。



 <button (click)="employer?.goToWork()">Go To Work</button>
      
      





このエントリは、 employer



プロパティがオプションであり、値が空の場合、残りの式は無視されることを意味します。 砂糖を削除すると、このエントリは次のようになります。



 <button (click)="employer === undefined ? : employer.goToWork()">Go To Work</button>
      
      





この演算子を使用せず、同時に同様のチェックを行わなかった場合、この状況ではTypeError



ます。



coffescriptと同様に、この演算子は、次のように、1つの式のフレームワーク内で必要に応じて何度でも使用できます: a?.b?.c?.d







結論



Angular2開発者は、テンプレートの構文をより柔軟かつ強力にするために素晴らしい仕事をしました。 はい、適応するのに少し時間がかかるものもありますが、一般に、時間の経過とともに、これはすべて自然に思えます。 そして最も重要なことは、すべてが一見したほど怖いわけではありません。



All Articles