序章
AngularJSの最初の製品の最も便利な機能の1つは、テンプレート変数とフィルターのみを使用してページ上のデータをフィルター処理およびソートする機能でした。 双方向のデータバインディングは、AngularJSへの多くの変換に勝ちました。
しかし今日、多くのフロントエンド開発者は一方向のデータバインディングを好みます。 orderBy
と filter
Angularの登場により、フィルターは廃止されました。
注:この記事全体を通して、「AngularJS」は1.xを指すために使用され、「Angular」は2+を指すために使用されます。
この記事では、ngUpgradeを使用して再適用します orderBy
と filter
.
ステップ1—プロジェクトの設定
新しく書き直されたコンポーネントのテンプレートを更新する手順を実行します。 次に、並べ替えとフィルタリングを追加して、AngularJSにあったすべての機能を復元します。 これは、ngUpgradeプロセスのために開発するための重要なスキルです。
開始するには、使用するサンプルプロジェクトのクローンを作成してください。
- git clone https://github.com/upgradingangularjs/ordersystem-project.git
出発点として、このコミットを確認してください。
- git checkout 9daf9ab1e21dc5b20d15330e202f158b4c065bc3
このサンプルプロジェクトは、AngularJS1.6とAngular4の両方を使用するngUpgradeハイブリッドプロジェクトです。 動作するExpressAPIと、開発と本番の両方のWebpackビルドがあります。 自由に探索し、フォークして、独自のプロジェクトでパターンを使用してください。
実行することを忘れないでください npm install
両方で public
フォルダ:
- cd public
- npm install
と server
フォルダ:
- cd server
- npm install
Angular 5を使用するこのプロジェクトのバージョンを確認したい場合は、このリポジトリを確認してください。 このチュートリアルでは、2つのバージョンの違いは重要ではありません。
ステップ2—AngularJS構文を置き換える
アプリケーションのこの段階で、ordersコンポーネントはAngularで書き直され、すべての依存関係が注入されて解決されます。 ただし、アプリケーションを実行しようとすると、テンプレートに問題があることを示すエラーがコンソールに表示されます。 それが最初に修正する必要があるものです。 注文テンプレートのAngularJS構文を置き換えます(orders/orders.html
)これにより、ページに表示されるルートの読み込みと注文を取得できます。 次に、フィルタリングと並べ替えを修正します。
私たちが最初に行う必要があるのは、のすべてのインスタンスを取り除くことです $ctrl
このテンプレートで。 Angularでは不要になりました。 検索と置換を行うだけで、 $ctrl.
(ドットに注意してください)、それを何にも置き換えません。
それでは、 data-ng-click
13行目のボタンで。 Angularでは、代わりに ng-click
、私たちはただ使用します click
イベント。括弧はイベントであることを示します。 括弧は入力を示し、括弧は出力またはイベントを示します。
<button type="button" (click)="goToCreateOrder()" class="btn btn-info">
Create Order
</button>
ここでは、クリックイベントで、 goToCreateOrder
注文コンポーネントで機能します。
続行する前に、コンポーネントが実際にロードされていることを証明するために少し時間を取ってみましょう。 全体をコメントアウトする div
これで注文が読み込まれます(17行目以降)。 アプリケーションを実行するには、ターミナルを開き、次のコマンドを実行します。
- cd server
- npm start
Expressサーバーが起動します。 Webpackを実行するには dev
サーバーで、別のターミナルを開いて実行します。
- cd public
- npm run dev
このチュートリアルの残りの部分では、これらのプロセスを実行し続けることができます。
アプリケーションが再び読み込まれていることを確認する必要があります。 注文ルートに移動すると、注文コンポーネントが正しく表示されていることがわかります。
注文の作成ボタンをクリックすることもできます。これにより、注文の作成ルートとフォームに正しく送信されます。
では、HTMLに戻りましょう。 コメントを外す div
(私たちのアプリは再び壊れます)。
残りのすべてのインスタンスを置き換えましょう data-ng-click
とともに (click)
イベントハンドラ。 「検索と置換」を使用するか、エディターのショートカットを使用してすべてのオカレンスを選択できます(Visual Studio Code for Windowsでは、これは CTRL+SHIFT+L
).
次に、のすべてのオカレンスを置き換えます data-ng-show
と *ngIf
. 実際には、直接同等のものはありません ng-show
Angularで、しかしそれは大丈夫です。 使用することが望ましい *ngIf
これは、要素を非表示にして表示するだけでなく、実際にDOMに要素を追加したり削除したりするためです。 だから、私たちがする必要があるのは私たちの data-ng-show
sそしてそれらを *ngIf
.
最後に、テーブル本体を修正するために2つのことを行う必要があります。 まず、交換してください data-ng-repeat
と *ngFor="let order of orders"
. また、を削除していることに注意してください orderBy
と filter
その行のフィルターは、全体が tr
このように見えます:
<tr *ngFor="let order of orders">
次に、を削除できます data-ng
の前のプレフィックス href
注文詳細ルートへのリンク。 AngularJSは引き続きここでルーティングを処理しますが、これはAngularテンプレートになっているため、このプレフィックスを使用する必要はありません。
アプリケーションをもう一度見ると、注文が画面に正しく読み込まれていることがわかります。
もちろん、それにはいくつかの問題があります。 並べ替えリンクが機能しなくなり、Angularの通貨パイプがAngularJSの対応する通貨パイプとわずかに異なるため、通貨が混乱しました。 それに到達します。 今のところ、これは素晴らしい兆候です。これは、データがコンポーネントに到達し、ページに読み込まれていることを意味します。 したがって、このテンプレートの基本をAngularに変換しました。 これで、並べ替えとフィルタリングに取り組む準備が整いました。
ステップ3—並べ替えを追加する
画面に注文が読み込まれていますが、注文や並べ替えの方法はまだありません。 AngularJSでは、組み込みを使用するのが非常に一般的でした orderBy
フィルタしてページ上のデータを並べ替えます。 Angularにはもうありません orderBy
フィルター。 これは、そのようなビジネスロジックをテンプレートに配置するのではなく、コンポーネントに移動することが強く推奨されているためです。 だから、それが私たちがここでやろうとしていることです。
注:ここでは、リアクティブ形式のアプローチではなく、単純な古い関数とイベントを使用します。 これは、私たちがこのことを理解するために赤ちゃんの一歩を踏み出そうとしているからです。 基本を理解したら、オブザーバブルでさらに進んでください。
コンポーネントでの並べ替え
すでに削除しました orderBy
からのフィルター ng-repeat
に変更したとき *ngFor
. 次に、ordersコンポーネントで並べ替え関数を作成します。 テーブルヘッダーのクリックイベントを使用して、その関数を呼び出し、並べ替えるプロパティを渡すことができます。 また、その関数を昇順と降順の間で前後に切り替えます。
注文コンポーネントを開きましょう(./orders/orders.component.ts
)そして、2つのパブリックプロパティをクラスに追加します。 これらは、テンプレートがすでに参照している2つのプロパティと一致します。 最初のものは sortType
タイプの string
. 2つ目は sortReverse
タイプの boolean
デフォルト値をfalseに設定します。 The sortReverse
プロパティは、順序を反転するかどうかを追跡するだけです。昇順または降順の同義語とは考えないでください。
したがって、クラスでタイトルを宣言した後、これが必要になります。
sortType: string;
sortReverse: boolean = false;
次に、JavaScriptのArray.sortプロトタイプ関数で使用する関数を追加します。 後にこれを追加します goToCreateOrder
関数(ただし、クラス内):
dynamicSort(property) {
return function (a, b) {
let result = (a[property] < b[property]) ? -1 : (a[property] > b[property]) ? 1 : 0;
return result;
}
}
この動的ソート関数は、配列内のオブジェクトのプロパティ値を比較します。 ネストされた三項関数は、一見理解するのが少し難しいかもしれませんが、基本的には、Aのプロパティの値がBより小さい場合、-1を返すと言っているだけです。 それ以外の場合は、それより大きい場合は1を返します。 2つが等しい場合は、0を返します。
さて、これは非常に洗練された、または深い比較ではありません。 ソートするために作成できるより洗練されたヘルパー関数があり、これを壊す方法を自由に試してみてください。 ただし、これは私たちの目的には役立ちます。このロジックを、任意のカスタムソートロジックと交換することができます。
これが私たちのヘルパー関数です。 配列プロトタイプの並べ替え関数には、配列内のアイテムを比較するために使用できる関数を渡すことができます。 という関数を作ってみましょう sortOrders
新しいものでそれを利用する私たちのクラスで dynamicSort
関数:
sortOrders(property) { }
最初に行う必要があるのは、 sortType
渡されたプロパティと等しいクラスのプロパティ。 次に、を切り替えます sortReverse
財産。 これがあります:
sortOrders(property) {
this.sortType = property;
this.sortReverse = !this.sortReverse;
}
今、私たちは呼び出すことができます sort
上の機能 this.orders
、ただし、プロパティを使用して動的ソート関数を渡します。
sortOrders(property) {
this.sortType = property;
this.sortReverse = !this.sortReverse;
this.orders.sort(this.dynamicSort(property));
}
そして、最後にやらなければならないことが1つあります。 変更する必要があります dynamicSort
少しだけ機能して、昇順または降順の配列の順序を逆にすることができます。 これを行うには、結果を結び付けます dynamicSort
に sortReverse
クラスのプロパティ。
最初に行うことは、変数を宣言することです。
let sortOrder = -1;
次に、 sortReverse
クラスのプロパティはtrueまたはfalseです。 trueの場合、ソート順変数を1に設定します。
if (this.sortReverse) {
sortOrder = 1;
}
デモンストレーションのためにsort関数でトグルを実行しているため、このように関数を結合しています。 より徹底的に言うと、別のアプローチは、 sortDescending
それ以外の sortReverse
これは別の機能で制御されます。 このルートに行くと、反対のことをします– sortOrder
でなければ1になります sortDescending
本当だった。
これらの最後の2つを組み合わせて3値式にすることもできますが、わかりやすくするために、もう少し冗長なままにしておきます。 そして、結果を通常とは逆にするために、乗算することができます result
私たちによって sortOrder
. だから私たちの dynamicSort
関数は次のようになります。
dynamicSort(property) {
let sortOrder = -1;
if (this.sortReverse) {
sortOrder = 1;
}
return function(a, b) {
let result = a[property] < b[property] ? -1 : a[property] > b[property] ? 1 : 0;
return result * sortOrder;
};
}
}
繰り返しになりますが、これは並べ替えのデモンストレーション実装であるため、コンポーネントでカスタム並べ替え機能を使用する際の重要な概念を理解できます。
並べ替えの確認
これまでに、 dynamicSort
ヘルパー関数と sortOrders
テンプレートではなくコンポーネントでソートできるように、クラスに関数を追加します。
これらの関数が機能しているかどうかを確認するために、デフォルトの並べ替えを追加してみましょう。 ngOnInit
関数。
私たちの内部 forkJoin
サブスクリプション、後 forEach
ここで、顧客名プロパティを追加します。 this.sortOrders
合計アイテムプロパティを渡します。
this.sortOrders('totalItems');
画面が更新されると、注文が合計アイテムで並べ替えられていることがわかります。
次に、を呼び出して、テンプレートにこの並べ替えを実装する必要があります。 sortOrders
テーブルヘッダーリンクからの関数。
テンプレートに並べ替えを追加する
私たちは sortOrders
関数はordersコンポーネントで正しく機能します。これは、テーブルヘッダーが再びクリックできるように、テンプレートに追加する準備ができたことを意味します。
その前に、デフォルトの並べ替えを変更しましょう ngOnInit
IDになる関数:
this.sortOrders('id');
これは、合計アイテムを使用するよりも少し普通です。
これで、テンプレートで作業できます。 私たちが最初にしたいことは、 sortOrders
すべてのクリックイベントで機能します。 のインスタンスを選択できます sortType =
それらを次のように置き換えます sortOrders(
. 次に、のインスタンスを置き換えることができます ; sortReverse = !sortReverse
と )
.
また、こことで渡す2つのプロパティ名を修正する必要があります。 *ngIf
インスタンス。 の3つのインスタンスを置き換えます orderId
と id
との3つのインスタンス customername
と customerName
.
私がする必要がある最後のことは、それぞれをラップすることです href
Angularが引き継ぎ、これらのリンクが実際にはどこにも行かないように、角かっこで囲まれたヘッダーにタグを付けます。 クリックイベントが発生します。 したがって、ヘッダーは次のパターンに従う必要があります。
<th>
<a [href]="" (click)="sortOrders('id')">
Order Id
<span *ngIf="sortType == 'id' && !sortReverse" class="fa fa-caret-down"></span>
<span *ngIf="sortType == 'id' && sortReverse" class="fa fa-caret-up"></span>
</a>
</th>
ブラウザにアクセスして、すべてのテーブルヘッダーリンクをテストします。 各プロパティが昇順と降順の両方で並べ替えられていることがわかります。 素晴らしい!
これは素晴らしいことですが、1つ失ったことがあります。カーソルはセレクターであり、ポインターではありません。 いくつかのCSSでそれを修正しましょう。
カーソルの修正
注文ページで並べ替えが正しく機能するようになりましたが、カーソルがポインターではなくセレクターになり、煩わしいものになりました。
CSSを使用してこれを修正する方法はいくつかあります。
- メインアプリのSCSSファイルでクラスを作成できます。
- インラインCSSを作成することもできますが、それはほとんど望ましいことではありません。
- コンポーネントデコレータのstylesオプションを使用して、Angularのスコープ付きCSSを利用できます。
この特定のコンポーネントのスタイルに1つのルールを追加するだけなので、最後のオプションを使用します。
注文コンポーネントクラスを再度開きます。 コンポーネントデコレータで、という新しいプロパティを追加できます styles
. Stylesは文字列の配列ですが、文字列はCSSルールです。 カーソルを修正するには、テーブルの行にリンクがある場合は、カーソルのプロパティをポインタに変更するというルールを書き出すだけです。 デコレータは次のようになります。
@Component({
selector: 'orders',
template: template,
styles: ['tr a { cursor: pointer; }']
})
ここで、行ヘッダーにカーソルを合わせると、ポインターカーソルがあることがわかります。 このアプローチの優れている点は、このCSSルールが他のコンポーネントに影響を与えないことです。 それは私たちの注文コンポーネントにのみ適用されます!
それでは、フィルタリングについて何かできるかどうか見てみましょう。 その「フィルターフィルター」はAngularから削除されたため、創造性を発揮して、コンポーネントに実装する方法を考え出す必要があります。
ステップ4—フィルタリングを追加する
AngularJSフィルターを使用して、検索していた文字列に基づいて注文コレクションを検索するために使用していたフィルターボックスを置き換える準備ができました。 AngularJSフィルターはテンプレート上に存在し、コントローラーやコンポーネントにコードを必要としませんでした。 現在、テンプレート内のそのようなロジックは推奨されていません。 コンポーネントクラスでそのような並べ替えとフィルタリングを行うことをお勧めします。
フィルタ機能の追加
コンポーネントに戻って、次のような新しい注文の配列を作成します。 filteredOrders
. それから私達は私達を渡すつもりです orders
配列をフィルター関数に設定します。 filteredOrders
配列。 最後に、 filteredOrders
私たちのテンプレートに *ngFor
元の配列の代わりに。 そうすれば、サーバーから返されるデータを変更することはなく、そのサブセットを使用するだけです。
最初に行うことは、クラスで新しいプロパティを宣言することです。
filteredOrders: Order[];
次に、私たちの forkJoin
元の注文の配列を設定します。の初期状態を設定できます。 filteredOrders
注文配列へ:
this.filteredOrders = this.orders;
これで、実際にフィルタリングを行う関数を追加する準備が整いました。 コンポーネントの下部にある並べ替え関数の直後に、この関数を貼り付けます。
filterOrders(search: string) {
this.filteredOrders = this.orders.filter(o =>
Object.keys(o).some(k => {
if (typeof o[k] === 'string')
return o[k].toLowerCase().includes(search.toLowerCase());
})
);
}
この関数で何が起こっているかについて話しましょう。 まず、関数に次の文字列プロパティを指定します search
. 次に、注文をループして、オブジェクトのすべてのキーを見つけます。 すべてのキーについて、あるかどうかを確認します some
検索語に一致するプロパティの値。 このJavaScriptのビットは、最初は少し混乱しているように見えるかもしれませんが、基本的にはそれが起こっていることです。
注意してください、私たちの if
ステートメント、文字列を明示的にテストしています。 この例では、クエリを文字列に制限します。 ネストされたプロパティ、数値プロパティ、またはそのようなものを処理しようとはしません。 検索語は顧客名プロパティと一致し、住所やその他の文字列プロパティを表示することを選択した場合は、それらも検索します。
もちろん、この関数を変更して数値をテストしたり、ネストされたオブジェクトの別のレイヤーを調べたりすることもできます。それは完全にあなた次第です。 並べ替えと同じように、デモンストレーションの実装から始めて、想像力を使ってより複雑にします。
と言えば sortOrders
関数、次に進む前に、コンポーネントに対して最後の1つのことを行う必要があります。 変更する必要があります sortOrders
使用する filteredOrders
今ではなく、私たちのオリジナル orders
、フィルターがソートよりも優先されるようにするためです。 これに変更するだけです:
sortOrders(property) {
this.sortType = property;
this.sortReverse = !this.sortReverse;
this.filteredOrders.sort(this.dynamicSort(property));
}
これで、このフィルタリングをテンプレートに実装する準備が整いました。
テンプレートへのフィルタリングの追加
テンプレートに戻り、フィルタリングを使用するように修正してみましょう。
最初に行う必要があるのは交換です data-ng-model
. その代わりに、 keyup
イベントなので、「keyup」と書き、括弧で囲みます((keyup)
). これはAngularの組み込みイベントであり、入力のキーアップで関数を実行できます。 関数に名前を付けたので filterOrders
、以前はAngularJSフィルターに渡したプロパティの名前でしたが、その横に括弧を追加する必要があります。 これまでの入力は次のようになります。
<input type="text" class="form-control" placeholder="Filter Orders (keyup)="filterOrders()">
しかし、フィルター次数関数に何を渡しますか? さて、デフォルトでは、イベントはと呼ばれるものを渡します $event
. これには、 target
、入力の値が含まれます。 使用には1つの問題があります $event
. これらのあいまいなタイプを追跡することは非常に困難です。 target.value
本当に何でもかまいません。 これにより、どのタイプの値が期待されるかをデバッグまたは把握することが困難になります。 代わりに、Angularには、この入力にテンプレート変数を割り当てるという非常に優れた機能があります。
幸い、Angularはこれを行うための方法を提供します。 入力タグの後に、ハッシュ記号を追加できます(#
)次に、目的のモデルの名前。 それを呼びましょう #ordersFilter
. タグのどこにこれを配置するか、何と呼ぶかは実際には関係ありませんが、ページを見下ろすだけで、どのモデルがどの入力に関連付けられているかがわかるように、入力の後に配置するのが好きです。
これで、その変数を filterOrders
上の機能 keyup
イベント。 その前にハッシュ記号は必要ありませんが、追加する必要があります .value
. これにより、モデル全体ではなく、モデルの実際の値が渡されます。 完成した入力は次のようになります。
<input #ordersFilter type="text" class="form-control"
placeholder="Filter Orders" (keyup)="filterOrders(ordersFilter.value)">
最後に、 *ngFor
を使用するには filteredOrders
通常の代わりに配列 orders
配列:
<tr *ngFor="let order of filteredOrders">
###製品の検査
コンポーネントにフィルタリングと並べ替えが含まれるようになったため、テンプレートがどれだけクリーンになったかがわかります。
それでは、ブラウザでこれを確認してみましょう。 ボックスにテキストを入力すると、注文が変更され、その上で並べ替えが機能することがわかります。
素晴らしい、別のAngularJS機能を置き換えました!
これで、このコンポーネントで最後に行う必要があることが1つあります。それは、通貨パイプを修正することです。
ステップ5—通貨パイプの修正
最後の仕上げは、以前の通貨フィルターを更新することです。これは、Angularでは通貨pipeと呼ばれています。 AngularJSで指定する必要がなかった、テンプレートのパイプにいくつかのパラメーターを追加する必要があります。 この部分は、Angular4またはAngular5を使用している場合は異なります。
Angular 4では、次のようにします。
<td>{{order.totalSale | currency:'USD':true}}</td>
Angular 5+では、次のようにします。
<td>{{order.totalSale | currency:'USD':'symbol'}}</td>
最初のオプションは通貨コードです(たくさんあります、あなたは米ドルに制限されていません)。 2つ目はシンボル表示です。 Angular 4では、これは通貨記号とコードのどちらを使用するかを示すブール値です。 Angular 5+では、オプションは次のとおりです。 symbol
, code
、 また symbol-narrow
文字列として。
これで、期待される記号が表示されます。
これで完了です。 完成したコードを確認するには、このコミットを確認してください。
結論
あなたはこれを最後までこだわって素晴らしい仕事をしました! このガイドで達成したことは次のとおりです。
- AngularJSテンプレート構文をAngular構文に置き換える
- コンポーネントへの並べ替えの移動
- スコープ付きCSSスタイルの使用
- フィルタリングをコンポーネントに移動する
- AngularJS通貨フィルターをAngular通貨パイプに置き換える
ここからどこへ行けばいいの? あなたができることはたくさんあります:
- 並べ替えをより洗練されたものにします(たとえば、ユーザーが新しいヘッダーをクリックしたときに、順序をリセットするか、同じままにする必要がありますか?)
- フィルタリングをより高度にします(数値またはネストされたプロパティを検索します)
- リアクティブアプローチに変更します。 代わりに、観測可能な値の変化を聞くことができます
keyup
関数を実行し、そこで並べ替えとフィルタリングを行います。 オブザーバブルを使用すると、入力をデバウンスするなど、本当にクールなこともできます。