RXJS Application Summary in Angular (Part 1)


foreword

In Angular's management information system (MIS) project, RxJS mainly has the following common application scenarios:
(This article introduces the first three scenes)

Asynchronous operations : The core function of RxJS is to handle asynchronous operations, such as initiating HTTP requests, setTimeout, WebSocket, etc.

State management : BehaviorSubject can be used to create an observable data flow, which is very useful for state management. For example, you can use it to share a user's login status, theme settings, and more.

Form handling : Angular's form module works very well with RxJS. For example, when the value of a form changes, you can use valueChanges Observable, combined with debounceTime, filter, map and other operators for complex form validation.

Routing management : Angular's Router module provides a series of Events Observables, you can subscribe to these Observables to handle routing navigation start, end, cancel and other events.

Communication : If your application needs to use Websocket or Server-Sent Events, RxJS provides an easy way to handle these communication processes.

UI interaction : RxJS can be used to handle complex user interactions, such as debounce and throttle, drag and drop operations, etc.

Error handling : With RxJS, you can easily handle errors and retries. For example, if an HTTP request fails, you can use the retry or retryWhen operators to retry.

Combine multiple asynchronous operations : Using operators such as forkJoin, combineLatest or zip, you can combine multiple Observables and wait for them all to complete.

Polling : If you need to periodically check for data changes on the server, you can use the interval or timer function combined with the switchMap operator to create a polling.


提示:以下是本篇文章正文内容,下面案例可供参考

1. Asynchronous operation

Suppose we want to get data from an API server and display it on the user interface. We can use Angular's HttpClient module to initiate HTTP requests, and then use NG-ZORRO components to display data.

First, we create a service DataService to make HTTP requests:

The code is as follows (example):

import {
    
     Injectable } from '@angular/core';
import {
    
     HttpClient } from '@angular/common/http';
import {
    
     Observable } from 'rxjs';

@Injectable({
    
    
  providedIn: 'root'
})
export class DataService {
    
    
  constructor(private http: HttpClient) {
    
    }

  getData(): Observable<any> {
    
    
    return this.http.get('https://api.example.com/data');
  }
}

Then, we create a component DataComponent to display the data:

import {
    
     Component, OnInit } from '@angular/core';
import {
    
     DataService } from './data.service';

@Component({
    
    
  selector: 'app-data',
  template: `
    <nz-spin *ngIf="loading"></nz-spin>
    <nz-table [nzData]="data" *ngIf="!loading"></nz-table>
  `
})
export class DataComponent implements OnInit {
    
    
  data: any[] = [];
  loading = true;

  constructor(private dataService: DataService) {
    
    }

  ngOnInit() {
    
    
    this.dataService.getData().subscribe(
      data => {
    
    
        this.data = data;
        this.loading = false;
      },
      error => {
    
    
        console.error(error);
        this.loading = false;
      }
    );
  }
}

In this example, DataService uses the get method of HttpClient to make HTTP requests. The get method returns an Observable that emits the data returned by the server when the request is successful.

When DataComponent is initialized, it calls the getData method of DataService to obtain data and subscribes to the returned Observable. After the data is fetched successfully, we assign the data to the data variable and set the loading variable to false to hide the loading indicator. If the request fails, we log the error to the console and set the loading variable to false.

In the component's template, we used NG-ZORRO's nz-spin component to display the loading indicator, and nz-table component to display the data. When the loading variable is true, the loading indicator is displayed; when the loading variable is false, the data grid is displayed.

2. Status management

First, we need to create a service to manage the login status of users. In this example, we'll use a BehaviorSubject to represent the user's login status:

The code is as follows (example):

import {
    
     Injectable } from '@angular/core';
import {
    
     BehaviorSubject, Observable } from 'rxjs';

@Injectable({
    
    
  providedIn: 'root',
})
export class AuthService {
    
    
  private loggedIn = new BehaviorSubject<boolean>(false); // 默认用户未登录

  get isLoggedIn(): Observable<boolean> {
    
    
    return this.loggedIn.asObservable(); // 提供一个获取登录状态的 Observable
  }

  login(): void {
    
    
    // 假设登录验证通过
    this.loggedIn.next(true); // 更新登录状态
  }

  logout(): void {
    
    
    this.loggedIn.next(false); // 更新登录状态
  }
}

Then, we can use AuthService in the component to display the user's login status and provide login and logout buttons:

import {
    
     Component, OnInit } from '@angular/core';
import {
    
     AuthService } from './auth.service';
import {
    
     NzMessageService } from 'ng-zorro-antd/message';

@Component({
    
    
  selector: 'app-login',
  template: `
    <div *ngIf="isLoggedIn | async; else loggedOut">
      <button nz-button nzType="primary" (click)="logout()">Logout</button>
    </div>
    <ng-template #loggedOut>
      <button nz-button nzType="primary" (click)="login()">Login</button>
    </ng-template>
  `,
})
export class LoginComponent implements OnInit {
    
    
  isLoggedIn: Observable<boolean>;

  constructor(private authService: AuthService, private message: NzMessageService) {
    
    }

  ngOnInit(): void {
    
    
    this.isLoggedIn = this.authService.isLoggedIn;
  }

  login(): void {
    
    
    this.authService.login();
    this.message.success('Logged in!');
  }

  logout(): void {
    
    
    this.authService.logout();
    this.message.success('Logged out!');
  }
}

In this example:

AuthService is used to manage the user's login status. It provides an Observable of isLoggedIn that allows components to subscribe to the user's login status.
The LoginComponent component uses the isLoggedIn Observable of AuthService to display the user's login status, and provides buttons for logging in and logging out.
When the user clicks the login or logout button, the component calls the AuthService's login or logout method to update the user's login status and displays a message using the NzMessageService.
We use an async pipe to subscribe to the isLoggedIn Observable so that Angular can automatically manage the subscription and avoid memory leaks.

BehaviorSubject is an important concept in RxJS, which is a special type of Subject. Subject is seen in RxJS as an object that is both Observable (observable object) and Observer (observer). In other words, you can either send (emit) data to it or subscribe (subscribe) data from it.

The main difference between a BehaviorSubject and a normal Subject is that it remembers the last value sent. When a new Observer subscribes to it, the new Observer will immediately receive the last sent value. This can be very useful in many situations, for example, in this example, we want new subscribers to be immediately aware of the user's current login status.

This code private loggedIn = new BehaviorSubject(false); defines a BehaviorSubject instance and sets the initial value to false, which means the user is not logged in. This BehaviorSubject can be used to send new login statuses, and can also be subscribed to receive new login statuses.

Then, we define a getter method isLoggedIn() that returns the Observable version of loggedIn. This method is implemented by calling loggedIn.asObservable(), which returns a new Observable that mirrors all values ​​of loggedIn, but cannot send new values ​​through it. This way, we can protect loggedIn from accidentally sending new values ​​outside the AuthService service.

Therefore, the main purpose of the isLoggedIn method in the AuthService service is to provide a safe way for external code to subscribe to the user's login status without worrying about accidentally changing the login status.

3. Form processing

Suppose we have a form for user registration and we need to check if the username is already registered as soon as the user enters it.

First, we need to import ReactiveFormsModule and NgZorroAntdModule in the Angular module:

The code is as follows (example):

import {
    
     ReactiveFormsModule } from '@angular/forms';
import {
    
     NgZorroAntdModule } from 'ng-zorro-antd';

@NgModule({
    
    
  imports: [
    ReactiveFormsModule,
    NgZorroAntdModule,
    // ...
  ],
  // ...
})
export class AppModule {
    
     }

Then, we can create a service to mock the API call:

import {
    
     Injectable } from '@angular/core';
import {
    
     of, Observable } from 'rxjs';
import {
    
     delay } from 'rxjs/operators';

@Injectable({
    
    
  providedIn: 'root'
})
export class UserService {
    
    
  checkUsername(username: string): Observable<boolean> {
    
    
    const isTaken = (username === 'user1');
    return of(isTaken).pipe(delay(500));
  }
}

Next, we can create the form in the component and listen to the valueChanges Observable of the username field:

import {
    
     Component, OnInit } from '@angular/core';
import {
    
     FormGroup, FormBuilder } from '@angular/forms';
import {
    
     debounceTime, switchMap } from 'rxjs/operators';
import {
    
     UserService } from './user.service';

@Component({
    
    
  selector: 'app-register',
  templateUrl: './register.component.html',
  styleUrls: ['./register.component.css']
})
export class RegisterComponent implements OnInit {
    
    
  form: FormGroup;
  usernameTaken = false;

  constructor(private fb: FormBuilder, private userService: UserService) {
    
     }

  ngOnInit() {
    
    
    this.form = this.fb.group({
    
    
      username: '',
      password: '',
    });

    this.form.get('username').valueChanges.pipe(
      debounceTime(500),
      switchMap(username => this.userService.checkUsername(username))
    ).subscribe(isTaken => {
    
    
      this.usernameTaken = isTaken;
    });
  }
}

Finally, we can use NG-ZORRO's form controls and nz-typography components in the template to display the validation results:

<form [formGroup]="form" nz-form>
  <nz-form-item>
    <nz-form-label nzRequired>Username</nz-form-label>
    <nz-form-control>
      <input nz-input formControlName="username" />
      <nz-typography *ngIf="usernameTaken" nzType="danger">Username is already taken</nz-typography>
    </nz-form-control>
  </nz-form-item>
  <nz-form-item>
    <nz-form-label nzRequired>Password</nz-form-label>
    <nz-form-control>
      <input nz-input formControlName="password" type="password" />
    </nz-form-control>
  </nz-form-item>
</form>

In this example, we use the following RxJS features:

debounceTime(500): We wait for the user to stop typing for 500 milliseconds before making an API request to avoid excessive API requests.
switchMap: We initiate a new API request after each user input and cancel the last outstanding API request. This way, we always get the validation result for the last entered username.


Guess you like

Origin blog.csdn.net/weixin_45876175/article/details/132050727