サーチ…


前書き

Ngrxは、あなたがAngular2で使用できる強力なライブラリです。 2つのコンセプトを統合して、予測可能な状態のコンテナを備えたリアクティブなアプリケーションを作成することです。アプリケーションのコアロジックをテストすることは、Angular2(非常に簡単です)[1]:http://redux.js.org [2]:http:// reactxに依存することなく、純粋な関数をテストすることから成り立っています。 io / rxjs

完全な例:ユーザのログイン/ログアウト

前提条件

このトピックは、Reduxおよび/またはNgrxについてのトピックではありません

  • あなたはReduxに慣れている必要があります
  • 少なくとも、RxJsとObservableパターンの基本を理解する

最初に、最初からサンプルを定義し、いくつかのコードで試してみましょう:

開発者として、私はしたい:

  1. Userプロパティを定義するIUserインターフェイスを持つ
  2. 後でStore Userを操作するために使用するアクションを宣言する
  3. UserReducer初期状態を定義する
  4. レデューサーUserReducer作成する
  5. UserReducerをメインモジュールにインポートしてStoreを構築する
  6. Storeからのデータを使用してビューに情報を表示する

Spoiler alert :すぐにデモを試したり、コードを読んだりする前に、Plunkr( 埋め込みビューまたは実行ビューをご覧ください

1) IUserインターフェースを定義する

私は2つの部分で私のインターフェイスを分割するのが好きです:

  • サーバーから取得するプロパティ
  • UIに対してのみ定義するプロパティ(ボタンが回転している場合など)

そしてここで私たちが使用するインターフェースIUserがあります:

user.interface.ts

export interface IUser {
  // from server
  username: string;
  email: string;
  
  // for UI
  isConnecting: boolean;
  isConnected: boolean;
};

2) Userを操作するアクションを宣言する

今、私たちは、 減速機がどのような行動を取るべきかについて考える必要があります。
ここで言う:

user.actions.ts

export const UserActions = {
  // when the user clicks on login button, before we launch the HTTP request
  // this will allow us to disable the login button during the request
  USR_IS_CONNECTING: 'USR_IS_CONNECTING',
  // this allows us to save the username and email of the user
  // we assume those data were fetched in the previous request
  USR_IS_CONNECTED: 'USR_IS_CONNECTED',

  // same pattern for disconnecting the user
  USR_IS_DISCONNECTING: 'USR_IS_DISCONNECTING',
  USR_IS_DISCONNECTED: 'USR_IS_DISCONNECTED'
};

しかし、私たちがこれらの行動を使用する前に、私たちになぜそれらの行動のうちのいくつかを派遣するサービスが必要となる理由を説明しましょう。

ユーザーと接続したいとしましょう。ログインボタンをクリックすると、次のようなことが起こります:

  • ボタンをクリック
  • コンポーネントはイベントをキャッチし、 userService.loginを呼び出しますuserService.login
  • userService.loginメソッドdispatch 、イベントをdispatchしてstoreプロパティを更新しますuser.isConnecting
  • HTTPコールが起動されます(デモのsetTimeoutを使用して非同期動作をシミュレートします
  • HTTPコールが終了すると、ユーザーがログに記録されていることを店舗に警告する別のアクションをHTTPます

user.service.ts

@Injectable()
export class UserService {
  constructor(public store$: Store<AppState>) { }

  login(username: string) {
    // first, dispatch an action saying that the user's tyring to connect
    // so we can lock the button until the HTTP request finish
    this.store$.dispatch({ type: UserActions.USR_IS_CONNECTING });

    // simulate some delay like we would have with an HTTP request
    // by using a timeout
    setTimeout(() => {
      // some email (or data) that you'd have get as HTTP response
      let email = `${username}@email.com`;

      this.store$.dispatch({ type: UserActions.USR_IS_CONNECTED, payload: { username, email } });
    }, 2000);
  }

  logout() {
    // first, dispatch an action saying that the user's tyring to connect
    // so we can lock the button until the HTTP request finish
    this.store$.dispatch({ type: UserActions.USR_IS_DISCONNECTING });

    // simulate some delay like we would have with an HTTP request
    // by using a timeout
    setTimeout(() => {
      this.store$.dispatch({ type: UserActions.USR_IS_DISCONNECTED });
    }, 2000);
  }
}

3) UserReducer初期状態を定義する

user.state.ts

export const UserFactory: IUser = () => {
  return {
    // from server
    username: null,
    email: null,

    // for UI
    isConnecting: false,
    isConnected: false,
    isDisconnecting: false
  };
};

4)減速機を作成するUserReducer

減速器には2つの引数があります。

  • 現在の状態
  • ActionタイプAction<{type: string, payload: any}>

注意:減速機はある時点で初期化する必要があります

パート3の減速機のデフォルト状態を定義したので、次のように使用することができます:

user.reducer.ts

export const UserReducer: ActionReducer<IUser> = (user: IUser, action: Action) => {
  if (user === null) {
    return userFactory();
  }
  
  // ...
}

うまくいけば、私たちのfactory関数を使ってオブジェクトを返すことで、それを書く簡単な方法があり、減速機内には(ES6) デフォルトのパラメータ値を使う:

export const UserReducer: ActionReducer<IUser> = (user: IUser = UserFactory(), action: Action) => {
  // ...
}

それでは、減速機のすべてのアクションを処理する必要があります。 ヒントES6 Object.assign関数を使用して状態を不変に保ちます

export const UserReducer: ActionReducer<IUser> = (user: IUser = UserFactory(), action: Action) => {
  switch (action.type) {
    case UserActions.USR_IS_CONNECTING:
      return Object.assign({}, user, { isConnecting: true });

    case UserActions.USR_IS_CONNECTED:
      return Object.assign({}, user, { isConnecting: false, isConnected: true, username: action.payload.username });

    case UserActions.USR_IS_DISCONNECTING:
      return Object.assign({}, user, { isDisconnecting: true });

    case UserActions.USR_IS_DISCONNECTED:
      return Object.assign({}, user, { isDisconnecting: false, isConnected: false });

    default:
      return user;
  }
};

5) UserReducerをメインモジュールにインポートしてStoreを構築する

app.module.ts

@NgModule({
    declarations: [
    AppComponent
    ],
    imports: [
    // angular modules
    // ...

    // declare your store by providing your reducers
    // (every reducer should return a default state)
    StoreModule.provideStore({
        user: UserReducer,
        // of course, you can put as many reducers here as you want
        // ...
    }),

    // other modules to import
    // ...
    ]
});

6) Storeからのデータを使用して、ビューに情報を表示する

すべてのものが論理的な面で準備されており、私たちが望むものを2つのコンポーネントに表示するだけです。

  • UserComponent[Dumb component] @Inputプロパティとasyncパイプを使用して、ストアからユーザーオブジェクトを渡すだけです。このようにして、コンポーネントは使用可能になるとユーザーを受け取ります( userはタイプObservable<IUser>ではなくタイプIUserなります)。
  • LoginComponent [Smartコンポーネント]このコンポーネントにStoreを直接注入し、 Observableとしてuserのみで作業しuser

user.component.ts

@Component({
  selector: 'user',
  styles: [
    '.table { max-width: 250px; }',
    '.truthy { color: green; font-weight: bold; }',
    '.falsy { color: red; }'
  ],
  template: `
    <h2>User information :</h2>

    <table class="table">
      <tr>
        <th>Property</th>
        <th>Value</th>
      </tr>

      <tr>
        <td>username</td>
        <td [class.truthy]="user.username" [class.falsy]="!user.username">
          {{ user.username ? user.username : 'null' }}
        </td>
      </tr>

      <tr>
        <td>email</td>
        <td [class.truthy]="user.email" [class.falsy]="!user.email">
          {{ user.email ? user.email : 'null' }}
        </td>
      </tr>

      <tr>
        <td>isConnecting</td>
        <td [class.truthy]="user.isConnecting" [class.falsy]="!user.isConnecting">
          {{ user.isConnecting }}
        </td>
      </tr>

      <tr>
        <td>isConnected</td>
        <td [class.truthy]="user.isConnected" [class.falsy]="!user.isConnected">
          {{ user.isConnected }}
        </td>
      </tr>

      <tr>
        <td>isDisconnecting</td>
        <td [class.truthy]="user.isDisconnecting" [class.falsy]="!user.isDisconnecting">
          {{ user.isDisconnecting }}
        </td>
      </tr>
    </table>
  `
})
export class UserComponent {
  @Input() user;

  constructor() { }
}

login.component.ts

@Component({
  selector: 'login',
  template: `
    <form
      *ngIf="!(user | async).isConnected"
      #loginForm="ngForm"
      (ngSubmit)="login(loginForm.value.username)"
    >
      <input
        type="text"
        name="username"
        placeholder="Username"
        [disabled]="(user | async).isConnecting"
        ngModel
      >
 
      <button
        type="submit"
        [disabled]="(user | async).isConnecting || (user | async).isConnected"
      >Log me in</button>
    </form>
 
    <button
      *ngIf="(user | async).isConnected"
      (click)="logout()"
      [disabled]="(user | async).isDisconnecting"
    >Log me out</button>
  `
})
export class LoginComponent {
  public user: Observable<IUser>;
 
  constructor(public store$: Store<AppState>, private userService: UserService) {
      this.user = store$.select('user');
  }
 
  login(username: string) {
    this.userService.login(username);
  }
 
  logout() {
    this.userService.logout();
  }
}

NgrxReduxRxJsコンセプトのマージであるため、最初はインアウトを理解するのはかなり難しいでしょう。しかし、これは強力なパターンです。この例では、 反応的なアプリを持っていて、データを簡単に共有できるようになっています。利用可能なPlunkrがあることを忘れないでください。あなた自身のテストを行うためにそれをフォークすることができます!

私はそれもトピックがかなり長い歓迎であっても助けてくれることを願っています!



Modified text is an extract of the original Stack Overflow Documentation
ライセンスを受けた CC BY-SA 3.0
所属していない Stack Overflow