【问题标题】:Adding/Deleting an object to an Obervable of an object array in Angular/RXJS?在Angular / RXJS中向对象数组的Observable添加/删除对象?
【发布时间】:2020-05-23 06:32:45
【问题描述】:

目标:将对象添加到现有的 Observable 对象数组中。最后一步是在 DOM 上进行反射。

NewObject.ts:

export class NewObject {
  name: string;
  title: string;
}

这是example.component.ts

import { Observable } from 'rxjs';
import { Component, OnInit, Inject, EventEmitter } from '@angular/core';
import { NewObject } from 'objects';

@Component({
  selector: 'app-example',
  templateUrl: './example.component.html',
  styleUrls: ['./example.component.css']
})
export class ExampleComponent implements OnInit {

  // Initializing the object array Observable from a service (step 1)
  readonly objects$: Observable<NewObject[]> = this.objectSvc.getAllObjects("objects").pipe(
    map(obj => obj.map(x => ({ name: x.name, title: alterString(x.title) }))),
    shareReplay(1)
  );

  constructor(
    private objectSvc: ObjectService
  ) { }

  ngOnInit() {

    somethingThatHappensToAdd = (response: any) => {
      let data = JSON.parse(response);
      data.forEach(x => {
        let obj: NewObject = { name: x.name, title: alterString(x.title) }

        // Here's where I'm trying to add the obj object into the already existing object array Observable
      });
    };

    somethingThatHappensToDelete = (response: any) => {
      let data = JSON.parse(response);
      data.forEach(x => {
        let obj: NewObject = { name: x.name, title: alterString(x.title) }

        // Here's where I'm trying to delete the obj object from the already existing object array Observable
      });
    };

  }
}

这是我的example.component.html

<div *ngFor="let o of objects$ | async">
   <p>{{ o.name }}</p>
   <p>{{ o.title}}</p>
</div>

这是我的服务object.service.ts

import { Injectable } from '@angular/core';
import { Observable, throwError, of } from 'rxjs';
import { map, catchError } from 'rxjs/operators';
import { ClientApi, ApiException, NewObject } from '../client-api.service';

@Injectable({
  providedIn: 'root'
})
export class ObjectService {

  constructor(
    private clientApi: ClientApi
  ) { }

  getAllObjects(name: string): Observable<NewObject[]> {
    return this.clientApi.getAllObjects(name)
      .pipe(
        map((x) => x.result),
        catchError(err => {
          if (ApiException.isApiException(err)) {
            if (err.status === 404) {
              return of<NewObject[]>(undefined);
            }
          }
          return throwError(err);
        })
      );
  }
}

JSON 中格式化响应后,我希望能够将obj 对象插入objects$ Observable 并让它反映在用户界面上。

建议我使用BehaviorSubject 元素来实现这一点。任何人都可以建议如何轻松做到这一点?

【问题讨论】:

    标签: angular typescript rxjs observable behaviorsubject


    【解决方案1】:

    您可以创建一个BehaviorSubject 以发出来自somethingThatHappens() 的结果,并使用combineLatest() 将结果与来自服务的结果合并。

    objectsFromService$ = this.objectSvc.getAllObjects("objects").pipe(
        map(obj => obj.map(x => ({ name: x.name, title: alterString(x.title) })))
    );
    
    resultsArray = []
    resultsSubject = new BehaviorSubject(this.resultsArray);
    results$ = this.resultsSubject.asObservable()
    
    readonly objects$: Observable<NewObject[]> = combineLatest(
        this.results$,
        this.objectsFromService$
    ).pipe(
       map(([results, objects]) => ([...results, ...objects])),
       shareReplay(1)
    )
    
    
    somethingThatHappens = (response: any) => {
          let data = JSON.parse(response);
          data.forEach(x => {
            let obj: NewObject = { name: x.name, title: alterString(x.title) }
            this.resultsArray.push(obj)          
          });
          this.resultsSubject.next(this.resultsArray)
    };
    

    我创建了一个有效的 stackblitz 项目:https://stackblitz.com/edit/angular-jnngwh

    希望这能给你一个线索。

    【讨论】:

    • 在向数组添加其他项目时,这是一个很棒的解决方案。如果您要删除项目,这会导致问题吗?
    • 很高兴它为你工作 :) 这不会造成任何问题
    • 看来删除有点问题,能不能帮帮忙,我已经复制了stackblitz:stackblitz.com/edit/angular-22trl1
    • 当然。明天一到我的电脑我就看看
    • 我查看了您共享的代码。问题是您正在尝试删除来自服务的项目。为了删除它们,您需要将其从服务的Observable 源中删除,因为您想使用async 管道。
    【解决方案2】:

    我觉得哈伦的回答不错(https://stackoverflow.com/a/60108390/1974681)

    如果你只更新这个 observable 一次(在初始化时),你可以做一些简单的事情:

    export class ExampleComponent implements OnInit {
    
        let objects$: Observable<NewObject[]>;
    
        constructor(
            private objectSvc: ObjectService
        ) { }
    
        private initializeObservable() {
            return this.objectSvc.getAllObjects("objects").pipe(
                map(obj => obj.map(x => ({ name: x.name, title: alterString(x.title) }))),
                shareReplay(1)
            );
        }
    
        private getObservableWithNewValues() {
            const response = callService();
            let array = [];
            let data = JSON.parse(response);
            data.forEach(x => {
                let obj: NewObject = { name: x.name, title: alterString(x.title) }
                array.push(obj);
            });
    
            // This creates an observable of the array
            return of(array);
        }
    
        ngOnInit() {
            const initialObservable = this.initializeObservable();
    
            const anotherObservable = this.getObservableWithNewValues();
    
            this.objects$ = concat(initialObservable, anotherObservable);
        }
    }
    

    参考:

    【讨论】:

      【解决方案3】:

      我找到了两种方法来解决这个问题。

      解决方案 1: https://stackblitz.com/edit/angular-rajvqq

      constructor(private myService: MyService){
        this.myService.getObjects().subscribe(x => this.somethingThatHappensToAdd(x));
      }
      
      resultsArray = []
      resultsSubject = new BehaviorSubject(this.resultsArray);
      
      readonly objects$: Observable<number[]> = combineLatest(
        this.resultsSubject,
      ).pipe(
        map(([results]) => ([...results])),
        shareReplay(1)
      )
      
      somethingThatHappensToAdd( arrayOfValues ) {
        arrayOfValues.forEach(x => {
          this.resultsArray.push(x)          
        });
        this.resultsSubject.next(this.resultsArray)
      };
      
      delete(o: any) {
      
        // This is the same as just accessing this.resultsArray
        let newArray: any[] = this.resultsSubject.getValue();
        for (let i = newArray.length - 1; i >= 0; i--) {
          if (newArray[i] == o) {
            newArray.splice(i, 1);
          }
        }
      
        this.resultsSubject.next(newArray);
      }
      

      解决方案 2: https://stackblitz.com/edit/angular-gxfk16

      deleteSubject = new Subject<any>();
      addSubject = new Subject<any[]>();
      
      objects$ = this.myService.getObjects().pipe(
        switchMap(x => merge(
          of(x), 
          this.deleteSubject.pipe(map(y => this.deleteAll(y, x))),
          this.addSubject.pipe(map(y => { x.push(...y); return x; }))
        )),
        shareReplay(1)
      );
      constructor(private myService: MyService){ }
      
      deleteAll(o: any, v: any[]) {
        for (let i = v.length - 1; i >= 0; i--) {
          if (v[i] == o) {
            v.splice(i, 1);
          }
        }
        return v;
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2020-10-14
        • 1970-01-01
        • 2018-07-08
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-08-01
        相关资源
        最近更新 更多