Angular 8 如果我不关心响应,我是否需要订阅请求

标签 angular rxjs rxjs-subscriptions

我希望这是有道理的。 我决定改变一些服务的工作方式,只是因为在不同的 View 中订阅响应和处理创建、更新和删除变得有点麻烦。所以我决定做一个像这样的通用服务:

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

import { environment } from '@environments/environment';
import { Resource } from '../models/resource';
import { ToastrService } from 'ngx-toastr';
import { BehaviorSubject } from 'rxjs';

@Injectable({
    providedIn: 'root',
})
export class DataService<T extends Resource> {
    items: BehaviorSubject<T[]>;

    constructor(private endpoint: string, private http: HttpClient, private toastr: ToastrService) {
        this.items = new BehaviorSubject<T[]>([]);
    }

    initialize(feedId: number) {
        return this.http.get<T[]>(`${environment.apiUrl}/feeds/${feedId}/${this.endpoint}`).pipe(
            map(response => {
                console.log(this.endpoint, response);
                this.items.next(response);
                return response;
            }),
        );
    }

    get(id: number) {
        return this.http.get<T>(`${environment.apiUrl}/${this.endpoint}/${id}`);
    }

    create(filter: T) {
        return this.http.post<T>(`${environment.apiUrl}/${this.endpoint}`, filter).pipe(
            map((response: any) => {
                const message = response.message;
                const item = response.model;

                let items = this.items.value;
                items.push(item);

                this.emit(items, message);

                return response.model;
            }),
        );
    }

    update(filter: T) {
        return this.http.put<T>(`${environment.apiUrl}/${this.endpoint}`, filter).pipe(
            map((response: any) => {
                const message = response.message;
                const item = response.model;

                let items = this.items.value;
                this.remove(items, filter.id);
                items.push(item);

                this.emit(items, message);

                return response.model;
            }),
        );
    }

    delete(id: number) {
        return this.http.delete<string>(`${environment.apiUrl}/${this.endpoint}/${id}`).pipe(
            map((response: any) => {
                let items = this.items.value;
                items.forEach((item, i) => {
                    if (item.id !== id) return;
                    items.splice(i, 1);
                });

                this.emit(items, response);

                return response;
            }),
        );
    }

    private remove(items: T[], id: number) {
        items.forEach((item, i) => {
            if (item.id !== id) return;
            items.splice(i, 1);
        });
    }

    private emit(items: T[], message: string) {
        this.items.next(items);
        this.toastr.success(message);
    }
}

此服务背后的想法是 initialize 方法仅被调用一次,当它被调用时,您可以看到它将响应映射到 items 数组在服务本身内。然后,当执行创建、更新或删除时,更改的是该数组。

这(理论上)允许任何组件订阅 items 数组,以便根据任何更改进行更新。

因此,我有一些“扩展”此服务的服务,例如:

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

import { Filter } from '@models';
import { DataService } from './data.service';
import { ToastrService } from 'ngx-toastr';

@Injectable({
    providedIn: 'root',
})
export class FilterService extends DataService<Filter> {
    constructor(httpClient: HttpClient, toastr: ToastrService) {
        super('filters', httpClient, toastr);
    }
}

到目前为止,一切都很好。所以,我的问题是:我是否必须调用 initialize 方法并调用订阅?

例如,目前我有这个组件:

import { Component, OnInit, Input } from '@angular/core';
import { first } from 'rxjs/operators';

import { FilterService } from '@services';
import { NgAnimateScrollService } from 'ng-animate-scroll';

@Component({
    selector: 'app-feed-filters',
    templateUrl: './filters.component.html',
    styleUrls: ['./filters.component.scss'],
})
export class FiltersComponent implements OnInit {
    @Input() feedId: number;
    displayForm: boolean;

    constructor(private animateScrollService: NgAnimateScrollService, private filterService: FilterService) {}

    ngOnInit() {
        this.initialize();
    }

    navigateToForm() {
        this.displayForm = true;
        this.animateScrollService.scrollToElement('filterSave');
    }

    private initialize(): void {
        this.filterService
            .initialize(this.feedId)
            .pipe(first())
            .subscribe(() => {});
    }
}

正如您在私有(private)方法中所看到的,我先pipe,然后first,然后subscribe,如果我愿意的话,我会这样做从那里得到结果。在我的“子”组件中,我有这个:

import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core';
import { first } from 'rxjs/operators';

import { Filter } from '@models';
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
import { ConfirmationDialogComponent } from '@core';
import { FilterService } from '@services';
import { FiltersSaveComponent } from './filters-save.component';

@Component({
    selector: 'app-filters',
    templateUrl: './filters.component.html',
    styleUrls: ['./filters.component.scss'],
})
export class FiltersComponent implements OnInit {
    filters: Filter[];

    constructor(private modalService: NgbModal, private filterService: FilterService) {}

    ngOnInit() {
        this.filterService.items.subscribe(filters => (this.filters = filters));
    }

    openModal(id: number) {
        const modalRef = this.modalService.open(ConfirmationDialogComponent);
        modalRef.componentInstance.message = 'Deleting a filter is irreversible. Do you wish to continue?';
        modalRef.result.then(
            () => {
                this.filterService.delete(id);
            },
            () => {
                // Do nothing
            },
        );
    }

    openSaveForm(filter: Filter) {
        const modalRef = this.modalService.open(FiltersSaveComponent);
        modalRef.componentInstance.feedId = filter.feedId;
        modalRef.componentInstance.filterId = filter.id;
        modalRef.componentInstance.modal = true;
    }
}

如您所见,我从 filterService 订阅了 items 数组。 因此,在我的父 Controller 中,我认为我实际上不需要订阅,但如果我删除它,它就不起作用。

我以为我能够做这样的事情:

private initialize(): void {
    this.filterService.initialize(this.feedId);
}

而不是

private initialize(): void {
    this.filterService
        .initialize(this.feedId)
        .pipe(first())
        .subscribe(() => {
            // I don't need this
        });
}

我做错了什么,还是这就是我必须做的事情? 我希望我能解释清楚:)

最佳答案

您必须在 HttpClient 上的任何请求方法上调用 subscribe 才能发送请求。 HttpClient 返回一个冷 observable,这意味着它只有在订阅它之后才会运行(而不是立即开始运行的热 observable)。

此外,来自 HttpClient 的可观察对象只会发出一个值,即响应,这意味着不需要将其传输到first。您的最终逻辑将如下所示:

this.filterService.initialize(this.feedId).subscribe(() => undefined);

或者,您可以在 DataService 中订阅,而不是在使用服务的地方订阅,然后您的调用将如下所示:

this.filterService.initialize(this.feedId);

HttpClient 的一个好处是它们返回的可观察量永远不会再次发出,因此无需跟踪订阅并稍后关闭它。

关于Angular 8 如果我不关心响应,我是否需要订阅请求,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57960917/

相关文章:

android - deviceready 事件未在 Angular 混合应用程序中触发

css - 如何在 Angular 应用程序中向父级添加类?

javascript - 使用 RxJS 在另一个序列之后运行一个序列

angular - Rxjs 间隔请求如果仍在进行中则不应重置,但如果用户更改输入则应重置

Angular 清除订阅的更好方法

javascript - Angular:如何防止组件模板闪烁两个条件语句?

angular - 无法读取 angular2 组件函数中未定义的属性 'navigate'

Angular2 重定向到自定义错误页面

javascript - 按属性值过滤对象数组