NgRx を使用して Angular 状態管理をマスターする
Angular の
状態管理 により、アプリケーションのすべての部分でデータが一貫して効率的に共有されることが保証されます。各コンポーネントが独自のデータを管理する代わりに、中央ストアが状態を保持します。
この一元化により、データが変更されると、すべてのコンポーネントが更新された状態を自動的に反映し、一貫した動作とよりシンプルなコードが実現されます。また、データ フローが単一の信頼できる情報源から管理されるため、アプリの保守と拡張が容易になります。
この記事では、簡単なショッピング カート アプリケーションを構築することで、NgRx を使用して Angular で状態管理を実装する方法を検討します。 ストア、アクション、リデューサー、セレクター、、エフェクト などの NgRx の中心的な概念を取り上げ、これらの部分がどのように連携してアプリケーションの状態を管理するかを示します。効果的に。
Angular のState は、ショッピング カートの内容など、アプリが管理および表示する必要があるデータを指します。
状態管理が必要な理由
1.一貫性: すべてのコンポーネントにわたってデータが均一であることを保証します。 1 か所のデータが変更されると、中央ストアが関連するすべてのコンポーネントを自動的に更新し、不整合を防ぎます。
2.簡素化されたデータ フロー: コンポーネント間でデータを手動で渡す代わりに、状態管理により、すべてのコンポーネントが中央ストアから直接データにアクセスしたり更新したりできるため、アプリのデータ フローの管理と理解が容易になります。
3.より簡単なメンテナンスとスケーラビリティ: データ管理を一元化することで、状態管理によりコードの重複と複雑さが軽減されます。これにより、アプリの保守、デバッグ、拡張が容易になります。
4.パフォーマンスの最適化: 状態管理ソリューションには、アプリケーション全体を再レンダリングするのではなく、状態の変化に対応する必要があるコンポーネントのみを選択的に更新するなど、パフォーマンスを最適化するためのツールが付属していることがよくあります。
NgRx の仕組み
NgRx は、アプリケーションの状態を予測可能な方法で管理および維持するのに役立つ Angular の状態管理ライブラリです。
1.コンポーネント
コンポーネントは、ユーザーがアプリと対話する場所です。ショッピング カートに商品を追加するボタンである可能性があります。
コンポーネントとサービスは分離されており、相互に直接通信することはありません。代わりに サービス は エフェクト 内で使用されるため、従来の Angular アプリとは異なるアプリケーション構造が作成されます。
2.アクション
アクションは何が起こったかを説明し、必要なペイロード (データ) を含みます。
3.レデューサー
アクションに基づいて状態を更新します。
4.ストア
ストアは、アプリケーションの状態全体を保持する一元的な場所です。
5.セレクター
ストアからコンポーネントのデータを抽出します。
6.効果
エフェクトは、API 呼び出しなど、リデューサーに属さないロジックを処理する場所です。
7.サービス
サービスは実際のビジネス ロジックまたは API 呼び出しを実行します。エフェクトは多くの場合、サーバーからのデータの取得などのタスクを実行するためにサービスを使用します。
NgRx を使用する場合
アプリの複雑さが正当な場合は NgRx を使用しますが、単純なアプリの場合は、より単純な状態管理方法を使用してください。コンポーネント間のコンポーネント間の Angular の services、signals、および @Input/@Output バインディングは、通常、それほど複雑でないアプリケーションの状態を管理するのに十分です。
例: NgRx を使用してカートに追加機能を構築する
1.新しい Angular プロジェクトを作成します:
ng new shopping-cart
2. NGRX とエフェクトをインストールします
NGRX とエフェクトをインストールするには、ターミナルで次のコマンドを実行します:
ng add @ngrx/store@latest ng add @ngrx/effects
3.製品モデルを定義します
src/app ディレクトリ内に、product.model.ts
製品の構造を表す Product インターフェイスを定義します。
export interface Product { id: string; name: string; price: number; quantity: number; }
4.状態管理のセットアップ
ステップ 1: src/app ディレクトリ内に state フォルダーを作成します
ステップ 2: カート アクションを定義する
state フォルダーに cart.actions.ts を作成します。
import { createActionGroup, emptyProps, props } from '@ngrx/store'; import { Product } from '../product.model'; export const CartActions = createActionGroup({ source: 'Cart', events: { 'Add Product': props<{ product: Product }>(), 'Remove Product': props<{ productId: string }>(), 'Update Quantity': props<{ productId: string; quantity: number }>(), 'Load Products': emptyProps, }, }); export const CartApiActions = createActionGroup({ source: 'Cart API', events: { 'Load Products Success': props<{ products: Product[] }>(), 'Load Products Failure': props<{ error: string }>(), }, });
ステップ 3: レデューサーを作成する
state フォルダーに cart.reducer.ts を作成します。
import { createReducer, on } from '@ngrx/store'; import { Product } from '../product.model'; import { CartActions, CartApiActions } from './cart.actions'; // Initial state for products and cart export const initialProductsState: ReadonlyArray<Product> = []; export const initialCartState: ReadonlyArray<Product> = []; // Reducer for products (fetched from API) export const productsReducer = createReducer( initialProductsState, on(CartApiActions.loadProductsSuccess, (_state, { products }) => products) ); // Reducer for cart (initially empty) export const cartReducer = createReducer( initialCartState, on(CartActions.addProduct, (state, { product }) => { const existingProduct = state.find(p => p.id === product.id); if (existingProduct) { return state.map(p => p.id === product.id ? { ...p, quantity: p.quantity + product.quantity } : p ); } return [...state, product]; }), on(CartActions.removeProduct, (state, { productId }) => state.filter(p => p.id !== productId) ), on(CartActions.updateQuantity, (state, { productId, quantity }) => state.map(p => p.id === productId ? { ...p, quantity } : p ) ) );
ステップ 4: セレクターを作成する
state フォルダーに、cart.selectors.ts
を作成します。
import { createSelector, createFeatureSelector } from '@ngrx/store'; import { Product } from '../product.model'; export const selectProducts = createFeatureSelector<ReadonlyArray<Product>>('products'); export const selectCart = createFeatureSelector<ReadonlyArray<Product>>('cart'); export const selectCartTotal = createSelector(selectCart, (cart) => cart.reduce((total, product) => total + product.price * product.quantity, 0) );
Step 5: Create Effects
Create a new file cart.effects.ts in the state folder that listens for the Load Products action, uses the service to fetch products, and dispatches either a success or failure action.
import { Injectable } from '@angular/core'; import { Actions, createEffect, ofType } from '@ngrx/effects'; import { ProductService } from '../product.service'; import { CartActions, CartApiActions } from './cart.actions'; import { catchError, map, mergeMap } from 'rxjs/operators'; import { of } from 'rxjs'; @Injectable() export class CartEffects { loadProducts$ = createEffect(() => this.actions$.pipe( ofType(CartActions.loadProducts), mergeMap(() => this.productService.getProducts().pipe( map(products => CartApiActions.loadProductsSuccess({ products })), catchError(error => of(CartApiActions.loadProductsFailure({ error }))) ) ) ) ); constructor( private actions$: Actions, private productService: ProductService ) {} }
5. Connect the State Management to Your App
In a file called app.config.ts, set up configurations for providing the store and effects to the application.
import { ApplicationConfig } from '@angular/core'; import { provideStore } from '@ngrx/store'; import { provideHttpClient } from '@angular/common/http'; import { cartReducer, productsReducer } from './state/cart.reducer'; import { provideEffects } from '@ngrx/effects'; import { CartEffects } from './state/cart.effects'; export const appConfig: ApplicationConfig = { providers: [ provideStore({ products: productsReducer, cart: cartReducer }), provideHttpClient(), provideEffects([CartEffects]) ], };
6. Create a Service to Fetch Products
In the src/app directory create product.service.ts to implement the service to fetch products
import { Injectable } from '@angular/core'; import { Observable, of } from 'rxjs'; import { Product } from './product.model'; @Injectable({ providedIn: 'root' }) export class ProductService { getProducts(): Observable<Array<Product>> { return of([ { id: '1', name: 'Product 1', price: 10, quantity: 1 }, { id: '2', name: 'Product 2', price: 20, quantity: 1 }, ]); } }
7. Create the Product List Component
Run the following command to generate the component: ng generate component product-list
This component displays the list of products and allows adding them to the cart.
Modify the product-list.component.ts file:
import { Component, OnInit } from '@angular/core'; import { CommonModule } from '@angular/common'; import { Store } from '@ngrx/store'; import { Observable } from 'rxjs'; import { Product } from '../product.model'; import { selectProducts } from '../state/cart.selectors'; import { CartActions } from '../state/cart.actions'; @Component({ selector: 'app-product-list', standalone: true, templateUrl: './product-list.component.html', styleUrls: ['./product-list.component.css'], imports: [CommonModule], }) export class ProductListComponent implements OnInit { products$!: Observable<ReadonlyArray<Product>>; constructor(private store: Store) { } ngOnInit(): void { this.store.dispatch(CartActions.loadProducts()); // Dispatch load products action this.products$ = this.store.select(selectProducts); // Select products from the store } onAddToCart(product: Product) { this.store.dispatch(CartActions.addProduct({ product })); } }
Modify the product-list.component.html file:
<div *ngIf="products$ | async as products"> <div class="product-item" *ngFor="let product of products"> <p>{{product.name}}</p> <span>{{product.price | currency}}</span> <button (click)="onAddToCart(product)" data-test="add-button">Add to Cart</button> </div> </div>
8. Create the Shopping Cart Component
Run the following command to generate the component: ng generate component shopping-cart
This component displays the products in the cart and allows updating the quantity or removing items from the cart.
Modify the shopping-cart.component.ts file:
import { Component, OnInit } from '@angular/core'; import { CommonModule } from '@angular/common'; import { Store } from '@ngrx/store'; import { Observable } from 'rxjs'; import { Product } from '../product.model'; import { selectCart, selectCartTotal } from '../state/cart.selectors'; import { CartActions } from '../state/cart.actions'; @Component({ selector: 'app-shopping-cart', standalone: true, imports: [CommonModule], templateUrl: './shopping-cart.component.html', styleUrls: ['./shopping-cart.component.css'], }) export class ShoppingCartComponent implements OnInit { cart$: Observable<ReadonlyArray<Product>>; cartTotal$: Observable<number>; constructor(private store: Store) { this.cart$ = this.store.select(selectCart); this.cartTotal$ = this.store.select(selectCartTotal); } ngOnInit(): void {} onRemoveFromCart(productId: string) { this.store.dispatch(CartActions.removeProduct({ productId })); } onQuantityChange(event: Event, productId: string) { const inputElement = event.target as HTMLInputElement; let quantity = parseInt(inputElement.value, 10); this.store.dispatch(CartActions.updateQuantity({ productId, quantity })); } }
Modify the shopping-cart.component.html file:
<div *ngIf="cart$ | async as cart"> <div class="cart-item" *ngFor="let product of cart"> <p>{{product.name}}</p><span>{{product.price | currency}}</span> <input type="number" [value]="product.quantity" (input)="onQuantityChange($event, product.id)" /> <button (click)="onRemoveFromCart(product.id)" data-test="remove-button">Remove</button> </div> <div class="total"> Total: {{cartTotal$ | async | currency}} </div> </div>
Modify the shopping-cart.component.css file:
.cart-item { display: flex; justify-content: space-between; align-items: center; margin-bottom: 10px; } .cart-item p { margin: 0; font-size: 16px; } .cart-item input { width: 50px; text-align: center; } .total { font-weight: bold; margin-top: 20px; }
9. Put Everything Together in the App Component
This component will display the product list and the shopping cart
Modify the app.component.ts file:
import { Component } from '@angular/core'; import { CommonModule } from '@angular/common'; import { ProductListComponent } from './product-list/product-list.component'; import { ShoppingCartComponent } from './shopping-cart/shopping-cart.component'; import { NgIf } from '@angular/common'; @Component({ selector: 'app-root', standalone: true, templateUrl: './app.component.html', imports: [CommonModule, ProductListComponent, ShoppingCartComponent, NgIf], }) export class AppComponent {}
Modify the app.component.html file:
<!-- app.component.html --> <h2>Products</h2> <app-product-list></app-product-list> <h2>Shopping Cart</h2> <app-shopping-cart></app-shopping-cart>
10. Running the Application
Finally, run your application using ng serve.
Now, you can add products to your cart, remove them, or update their quantities.
Conclusion
In this article, we built a simple shopping cart application to demonstrate the core concepts of NgRx, such as the Store, Actions, Reducers, Selectors, and Effects. This example serves as a foundation for understanding how NgRx works and how it can be applied to more complex applications.
As your Angular projects grow in complexity, leveraging NgRx for state management will help you maintain consistency across your application, reduce the likelihood of bugs, and make your codebase easier to maintain.
To get the code for the above project, click the link below:
https://github.com/anthony-kigotho/shopping-cart
以上がNgRx を使用して Angular 状態管理をマスターするの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

ホットAIツール

Undresser.AI Undress
リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover
写真から衣服を削除するオンライン AI ツール。

Undress AI Tool
脱衣画像を無料で

Clothoff.io
AI衣類リムーバー

Video Face Swap
完全無料の AI 顔交換ツールを使用して、あらゆるビデオの顔を簡単に交換できます。

人気の記事

ホットツール

メモ帳++7.3.1
使いやすく無料のコードエディター

SublimeText3 中国語版
中国語版、とても使いやすい

ゼンドスタジオ 13.0.1
強力な PHP 統合開発環境

ドリームウィーバー CS6
ビジュアル Web 開発ツール

SublimeText3 Mac版
神レベルのコード編集ソフト(SublimeText3)

ホットトピック











さまざまなJavaScriptエンジンは、各エンジンの実装原則と最適化戦略が異なるため、JavaScriptコードを解析および実行するときに異なる効果をもたらします。 1。語彙分析:ソースコードを語彙ユニットに変換します。 2。文法分析:抽象的な構文ツリーを生成します。 3。最適化とコンパイル:JITコンパイラを介してマシンコードを生成します。 4。実行:マシンコードを実行します。 V8エンジンはインスタントコンピレーションと非表示クラスを通じて最適化され、Spidermonkeyはタイプ推論システムを使用して、同じコードで異なるパフォーマンスパフォーマンスをもたらします。

Pythonは、スムーズな学習曲線と簡潔な構文を備えた初心者により適しています。 JavaScriptは、急な学習曲線と柔軟な構文を備えたフロントエンド開発に適しています。 1。Python構文は直感的で、データサイエンスやバックエンド開発に適しています。 2。JavaScriptは柔軟で、フロントエンドおよびサーバー側のプログラミングで広く使用されています。

C/CからJavaScriptへのシフトには、動的なタイピング、ゴミ収集、非同期プログラミングへの適応が必要です。 1)C/Cは、手動メモリ管理を必要とする静的に型付けられた言語であり、JavaScriptは動的に型付けされ、ごみ収集が自動的に処理されます。 2)C/Cはマシンコードにコンパイルする必要がありますが、JavaScriptは解釈言語です。 3)JavaScriptは、閉鎖、プロトタイプチェーン、約束などの概念を導入します。これにより、柔軟性と非同期プログラミング機能が向上します。

Web開発におけるJavaScriptの主な用途には、クライアントの相互作用、フォーム検証、非同期通信が含まれます。 1)DOM操作による動的なコンテンツの更新とユーザーインタラクション。 2)ユーザーエクスペリエンスを改善するためにデータを提出する前に、クライアントの検証が実行されます。 3)サーバーとのリフレッシュレス通信は、AJAXテクノロジーを通じて達成されます。

現実世界でのJavaScriptのアプリケーションには、フロントエンドとバックエンドの開発が含まれます。 1)DOM操作とイベント処理を含むTODOリストアプリケーションを構築して、フロントエンドアプリケーションを表示します。 2)node.jsを介してRestfulapiを構築し、バックエンドアプリケーションをデモンストレーションします。

JavaScriptエンジンが内部的にどのように機能するかを理解することは、開発者にとってより効率的なコードの作成とパフォーマンスのボトルネックと最適化戦略の理解に役立つためです。 1)エンジンのワークフローには、3つの段階が含まれます。解析、コンパイル、実行。 2)実行プロセス中、エンジンはインラインキャッシュや非表示クラスなどの動的最適化を実行します。 3)ベストプラクティスには、グローバル変数の避け、ループの最適化、constとletsの使用、閉鎖の過度の使用の回避が含まれます。

PythonとJavaScriptには、コミュニティ、ライブラリ、リソースの観点から、独自の利点と短所があります。 1)Pythonコミュニティはフレンドリーで初心者に適していますが、フロントエンドの開発リソースはJavaScriptほど豊富ではありません。 2)Pythonはデータサイエンスおよび機械学習ライブラリで強力ですが、JavaScriptはフロントエンド開発ライブラリとフレームワークで優れています。 3)どちらも豊富な学習リソースを持っていますが、Pythonは公式文書から始めるのに適していますが、JavaScriptはMDNWebDocsにより優れています。選択は、プロジェクトのニーズと個人的な関心に基づいている必要があります。

開発環境におけるPythonとJavaScriptの両方の選択が重要です。 1)Pythonの開発環境には、Pycharm、Jupyternotebook、Anacondaが含まれます。これらは、データサイエンスと迅速なプロトタイピングに適しています。 2)JavaScriptの開発環境には、フロントエンドおよびバックエンド開発に適したnode.js、vscode、およびwebpackが含まれます。プロジェクトのニーズに応じて適切なツールを選択すると、開発効率とプロジェクトの成功率が向上する可能性があります。
