Angular 8 如果我不关心响应是否需要订阅请求
Angular 8 do I need to subscribe to a request if I don't care about the response
我希望这是有道理的。
我决定改变我的一些服务的工作方式,因为在不同的视图中订阅响应和处理创建、更新和删除变得有点麻烦。所以我决定提供这样的通用服务:
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 数组以随任何更改更新。
所以,我有几个服务"extend"这个服务,例如:
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(() => {});
}
}
正如您在私有方法中看到的那样,我先 pipe
,然后是 first
,然后是 subscribe
,如果我想从那里获得结果,我会这样做。在我的 "child" 组件中,我有这个:
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 数组。
所以,在我的父控制器中,我认为我实际上不需要订阅,但如果我删除它,它就不起作用。
我以为我可以做类似的事情:
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
return 是一个冷 observable,这意味着它不会 运行 直到有人订阅它(与立即启动 运行ning 的热 observable 相反)。
此外,来自 HttpClient
的可观察对象只会发出一个值,即响应,这意味着不需要将其通过管道传输到 first
。您的最终逻辑将如下所示:
this.filterService.initialize(this.feedId).subscribe(() => undefined);
或者,您可以在 DataService
中订阅,而不是在使用服务的地方订阅,然后您的调用将如下所示:
this.filterService.initialize(this.feedId);
HttpClient
的一个好处是它们 return 的可观察对象将永远不会再次发出,因此无需跟踪订阅并在以后关闭它。
我希望这是有道理的。 我决定改变我的一些服务的工作方式,因为在不同的视图中订阅响应和处理创建、更新和删除变得有点麻烦。所以我决定提供这样的通用服务:
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 数组以随任何更改更新。
所以,我有几个服务"extend"这个服务,例如:
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(() => {});
}
}
正如您在私有方法中看到的那样,我先 pipe
,然后是 first
,然后是 subscribe
,如果我想从那里获得结果,我会这样做。在我的 "child" 组件中,我有这个:
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 数组。 所以,在我的父控制器中,我认为我实际上不需要订阅,但如果我删除它,它就不起作用。
我以为我可以做类似的事情:
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
return 是一个冷 observable,这意味着它不会 运行 直到有人订阅它(与立即启动 运行ning 的热 observable 相反)。
此外,来自 HttpClient
的可观察对象只会发出一个值,即响应,这意味着不需要将其通过管道传输到 first
。您的最终逻辑将如下所示:
this.filterService.initialize(this.feedId).subscribe(() => undefined);
或者,您可以在 DataService
中订阅,而不是在使用服务的地方订阅,然后您的调用将如下所示:
this.filterService.initialize(this.feedId);
HttpClient
的一个好处是它们 return 的可观察对象将永远不会再次发出,因此无需跟踪订阅并在以后关闭它。