Suche…


Einführung

Ngrx ist eine leistungsstarke Bibliothek, die Sie mit Angular2 verwenden können . Die Idee dahinter ist , zwei Konzepte zu verschmelzen , die gut zusammen spielen eine reaktive App mit einem vorhersagbaren Zustand Behältern haben: - [Redux] [1] - [RxJs] [2] Die wichtigsten Vorteile: - Gemeinsame Nutzung von Daten in der App zwischen Komponenten wird einfacher - Das Testen Ihrer App-Kernlogik besteht darin, reine Funktionen zu testen, ohne auf Angular2 angewiesen zu sein (sehr einfach!) [1]: http://redux.js.org [2]: http: // interactivex. io / rxjs

Vollständiges Beispiel: Anmelden / Abmelden eines Benutzers

Voraussetzungen

Dieses Thema behandelt nicht Redux und / oder Ngrx:

  • Sie müssen mit Redux vertraut sein
  • Verstehe zumindest die Grundlagen von RxJs und Observable Pattern

Zuerst definieren wir ein Beispiel von Anfang an und spielen mit etwas Code:

Als Entwickler möchte ich:

  1. IUser eine IUser Schnittstelle, die die Eigenschaften eines User
  2. Deklarieren Sie die Aktionen, die wir später verwenden werden, um den User im Store
  3. Definieren Sie den Anfangszustand des UserReducer
  4. Erstellen Sie den Reduzierer UserReducer
  5. Importieren Sie unseren UserReducer in unser Hauptmodul, um den Store zu erstellen
  6. Verwenden Sie Daten aus dem Store , um Informationen in unserer Ansicht anzuzeigen

Spoiler-Alarm : Wenn Sie die Demo sofort ausprobieren oder den Code lesen möchten, bevor wir überhaupt anfangen, ist hier ein Plunkr (Embed- View oder Run-View ).

1) Definieren Sie die IUser Schnittstelle

Ich mag es, meine Schnittstellen in zwei Teile aufzuteilen:

  • Die Eigenschaften erhalten wir von einem Server
  • Die Eigenschaften, die wir nur für die Benutzeroberfläche definieren (sollte sich beispielsweise eine Schaltfläche drehen)

Und hier ist das Interface, IUser ich verwenden werde:

user.interface.ts

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

2) Deklarieren Sie die Aktionen zur Manipulation des User

Jetzt müssen wir darüber nachdenken, mit welchen Aktionen unsere Reduzierer umgehen sollen.
Sagen wir hier:

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'
};

Bevor wir diese Aktionen verwenden, lassen Sie mich erklären, warum wir einen Dienst benötigen, um einige dieser Aktionen für uns auszuführen:

Angenommen, wir möchten einen Benutzer verbinden. Wir werden also auf einen Login-Button klicken und Folgendes wird passieren:

  • Klicken Sie auf die Schaltfläche
  • Die Komponente fängt das Ereignis ab und ruft userService.login
  • userService.login Methode dispatch ein Ereignis unseren Speicher Eigenschaft zu aktualisieren: user.isConnecting
  • Ein HTTP-Aufruf wird ausgelöst (wir verwenden ein setTimeout in der Demo, um das async-Verhalten zu simulieren)
  • Sobald der HTTP Aufruf abgeschlossen ist, werden wir eine weitere Aktion auslösen, um unseren Shop zu warnen, dass ein Benutzer angemeldet ist

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) Definieren Sie den Anfangszustand des UserReducer

user.state.ts

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

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

4) Erstellen Sie den Reduzierer UserReducer

Ein Reduzierer benötigt 2 Argumente:

  • Der aktuelle Zustand
  • Eine Action vom Typ Action<{type: string, payload: any}>

Zur Erinnerung: Ein Reduzierer muss irgendwann initialisiert werden

Da wir in Teil 3 den Standardzustand unseres Reduzierers definiert haben, können wir ihn so verwenden:

user.reducer.ts

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

Hoffentlich gibt es eine einfachere Möglichkeit, dies zu schreiben, indem Sie unsere factory Funktion verwenden, um ein Objekt zurückzugeben, und innerhalb des Reduzierers einen (ES6) Standardparameterwert verwenden :

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

Dann müssen wir alle Aktionen in unserem Minderer zu handhaben : TIP: Verwenden Sie ES6 Object.assign Funktion unseres Staates unveränderlich zu halten

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) Importieren Sie unseren UserReducer in unser Hauptmodul, um den Store zu erstellen

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) Verwenden Sie Daten aus dem Store , um Informationen in unserer Ansicht anzuzeigen

Alles ist jetzt auf der Logikseite fertig und wir müssen nur das zeigen, was wir wollen, in zwei Komponenten:

  • UserComponent : [Dumme Komponente] Wir übergeben das Benutzerobjekt einfach mit der @Input Eigenschaft und der async Pipe aus dem Speicher. Auf diese Weise erhält die Komponente den Benutzer erst, wenn er verfügbar ist (und der user ist vom Typ IUser und nicht vom Typ Observable<IUser> !).
  • LoginComponent [Smart-Komponente] Wir injizieren den Store direkt in diese Komponente und arbeiten nur für den user als Observable .

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();
  }
}

Wie Ngrx eine Zusammenführung ist Redux und RxJs Konzepte, kann es sehr schwierig sein , die In eine outs am Anfang zu verstehen. Dies ist jedoch ein leistungsfähiges Muster, mit dem Sie, wie wir in diesem Beispiel gesehen haben, eine reaktive App haben können und Ihre Daten problemlos freigeben können. Vergiss nicht, dass es einen Plunkr gibt und du kannst ihn für deine eigenen Tests zusammenfassen!

Ich hoffe, es war hilfreich, obwohl das Thema ziemlich lang ist, Prost!



Modified text is an extract of the original Stack Overflow Documentation
Lizenziert unter CC BY-SA 3.0
Nicht angeschlossen an Stack Overflow