Добавление / удаление объекта в Obervable массива объектов в Angular / RX JS? - PullRequest
2 голосов
/ 07 февраля 2020

Цель: Добавить объект в существующий массив объектов 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$ Наблюдаемый и отражающий его в пользовательском интерфейсе.

Я советую использовать элемент BehaviorSubject, чтобы это произошло. Кто-нибудь может посоветовать, как это легко сделать?

Ответы [ 3 ]

2 голосов
/ 07 февраля 2020

Вы можете создать 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

Надеюсь, это даст вам подсказку.

0 голосов
/ 18 февраля 2020

Я нашел два способа go об этом решении.

Решение 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;
}
0 голосов
/ 07 февраля 2020

Я думаю, что ответ Харуна хорош ({ ссылка })

Если вы обновляете эту наблюдаемую только один раз (при инициализации), вы можете сделать что-то простое:

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

Refs:

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...