为什么每次都会调用热观测?

时间:2020-11-01 10:14:00

标签: angular

我有从服务中获得的可观察数据:

public events$: Observable<IEvent[]> = of([]);
public filteredEvents$: BehaviorSubject<IEvent[]> = new BehaviorSubject([]);

this.events$ = this.eventsService.get();

当我调用函数时:

  public checkAll(): void {
    this.events$
      .pipe(
        map((events: IEvent[]) =>
          events.filter(
            (event: IEvent) => (event.checked_export = !event.checked_export)
          )
        )
      )
      .subscribe((events) => this.filteredEvents$.next(events));
  }

它再次调用this.events$ = this.eventsService.get();并向服务器发出请求。

模板是:

<ng-container *ngIf="filteredEvents$ | async; else nodata">

我知道每个.subscribe((events)都会再次调用观察者,但是如何修改可观察数组this.events$并返回到filteredEvents$

为什么我在代码中遇到此错误:

RangeError:超出了最大调用堆栈大小

组件方法是:

  public delete(event: IEvent): void {
    this.confirm
      .open({})
      .pipe(
        filter(Boolean),
        concatMap(() => this.eventService.delete(event))
      )
      .subscribe((response) => this.eventService.next(response));
  }

服务是:

  public next(events: IEvent[]): void {
    this.events$.next(events);
  }

  public events(): Observable<any> {
    return this.events$.asObservable();
  }

  public delete(event: IEvent): Observable<any> {
    return this.eventsService
      .delete(event)
      .pipe(
        concatMap(() =>
          this.events$.pipe(
            map((events: IEvent[]) =>
              events.filter((e) => e.idEvent !== event.idEvent)
            )
          )
        )
      );
  }

1 个答案:

答案 0 :(得分:0)

您的概念是错误的。

  • 您永远不会退订!因此,您在调用checkAll时会多次造成内存泄漏。
  • 不需要主题克隆/副本。
interface IEvent {
    checked_export: boolean;
}

export class EventService {
    private events$: BehaviorSubject<IEvent[]> = new BehaviorSubject<IEvent[]>([]);
    
    public next(events: IEvent[]): void {
        this.events$.next(events);
    }
    
    public checkAll(): Observable<IEvent[]> {
        return this.events$.pipe(
            map((events: IEvent[]) => {
                return events.filter((event: IEvent) => event.checked_export);
            }),
        );
    }
}

// subscribe with async pipe: <ng-container *ngIf="data$ | async; else nodata">
// subscribe with async pipe: <ng-container *ngIf="isEmpty$ | async">
export class Component {
    nodata: any;
    
    constructor(private service: EventService) {
    }
    
    get data$(): Observable<IEvent[]> {
        return this.service.checkAll();
    }
    
    get isEmpty$(): Observable<boolean> {
        return this.data$.pipe(map((data) => data.length === 0));
    }
}

更新评论

@Injectable() // config into module's provider!!!
export class DataFromServerResolve implements Resolve<IEvent[]> {
    constructor(private http: HttpClient) {
    }
    
    resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<IEvent[]> {
        return this.http.get<IEvent[]>('url'); // then navigate here this resolving all time. Or use Ngrx Store / service to cache
    }
}

// to routing
const routes: Routes = [{ path: '', component: MyComponent, resolve: { myData: DataFromServerResolve } }];

interface IEvent {
    checked_export: boolean;
}

// subscribe with async pipe: <ng-container *ngIf="checkAll() | async; else nodata">
// subscribe with async pipe: <ng-container *ngIf="isEmpty$ | async">
@Component({
    selector: '...',
    templateUrl: '...',
    styleUrls: ['...'],
})
export class MyComponent implements OnInit {
    data: BehaviorSubject<IEvent[]>;
    
    constructor(private route: ActivatedRoute) {
    }
    
    ngOnInit(): void {
        // resolve: { myData: DataFromServerResolve } -> snapshot.data.myData (It will normal: IEvent[] type)
        this.data = new BehaviorSubject<IEvent[]>(this.route.snapshot.data.myData);
    }
    
    // There is not ngDestroy to unsubscribe because "| async" will unsubscribe.
    // If we use .subscribe(...) and save to variable then have to!
    
    get isEmpty$(): Observable<boolean> {
        return this.checkAll().pipe(map((data) => data.length === 0));
    }
    
    public next(newData: IEvent[]): void {
        this.data.next(newData); // it will trigger all subscribers again
    }
    
    // or put service and make input parameter and "this.data" replace by parameter name
    public checkAll(): Observable<IEvent[]> {
        return this.data.pipe(
            map((events: IEvent[]) => {
                return events.filter((event: IEvent) => event.checked_export);
            }),
        );
    }
}