Ami*_*mit 11 observable rxjs angular
假设我使用可观察的数据服务构建我的Angular应用程序,由服务器端点支持:
@Injectable()
export class TodoService {
todos: Observable<Todo[]>
private _todos: BehaviorSubject<Todo[]>;
constructor(private http: Http) {
this._todos = <BehaviorSubject<Todo[]>>new BehaviorSubject([]);
this.todos = this._todos.asObservable();
}
loadData() {
this.http.get(dataUrl).subscribe(data => this._todos.next(data));
}
}
Run Code Online (Sandbox Code Playgroud)
现在假设我的数据引用了一些其他模型,并通过其他一些服务(某种形式的多对多关系)公开:
interface ToDo {
title: string;
status: StatusEnum;
requiredResouces: Resource[];
}
interface Resource {
name: string;
todos: ToDo[];
}
@Injectable()
export class ResourcesService {
resources: Observable<Resource[]>
private _resources: BehaviorSubject<Resource[]>;
...
}
Run Code Online (Sandbox Code Playgroud)
现在,假设我将一个方法添加到"链接"待办事项和资源的服务,该服务将能够将更新状态推送到主题,但其他服务将不知道该更改.例如:
export class TodoService {
...
addResourceRequirement(todo: ToDo, resource: Resource) {
this.http.post(`${url}/${todo.id}/`, {addResource: resource.id})
.subscribe(() => this.loadData());
}
}
Run Code Online (Sandbox Code Playgroud)
会导致任何"todos"观察者刷新,但任何"资源"观察者仍会显示旧状态......
您将使用什么设计/模式/架构来同步两种服务?
(我知道有一些架构可以避免这种困难 - 特别是基于磁通的解决方案 - NgRX等......但我对专门针对可观察数据服务模式的解决方案感兴趣)
模式几乎完成了,您只需要避免在启动时创建可观察的对象,还请记住,它是BehaviorSubjectextends Observable,因此可以按原样使用它,如果要使其易于使用,可以使用隐式getter。
@Injectable()
export class TodoService {
private _todos: BehaviorSubject<Todo[]>;
constructor() {
this._todos = new BehaviorSubject<Todo[]>([]);
}
public get todos(): Observable<Todo[]>{
return this._todos;
}
}
Run Code Online (Sandbox Code Playgroud)
然后,当您要添加数据时,只需在_todos主题中发出一个新值,请参阅RxJs:如果您希望每次添加一些数据时都发出所有内容,则将数据流递增地推入BehaviorSubject <[]>以进行递增数组发出。
如果服务对todos数据有依赖性,则只需使用map,mergeMap等操作符来构建新的Observable,todos以使用源来构建新的Observable ,从而允许您在根数据更改时发出新值。
例:
@Injectable()
export class ResourcesService{
private _resources: Observable<Resources[]>;
constructor(private todoService: TodoService){
this._resources = this.todoService.todos.map(todos => {
// I suppose you want to aggreagate all resources, you can do it using reduce or anything, I'll use forEach in this example
const resources: Resource[] = [];
todos.forEach(todo => resources.push(...todo.requiredResouces);
});
}
//And then implicit getter for _resources.
}
Run Code Online (Sandbox Code Playgroud)
这样,如果您TodoService发出一个新数组,ResourcesService则将发出一个新的待办事项数组,状态为完成,而无需任何其他操作。
如果您的资源来自另一个端点(意味着您在更新数据后需要另一个请求来获取它们),则最好使用重新加载器模式:
@Injectable()
export class DataReloaderService{
public readonly reloader: BehaviorSubject<void> = new BehaviorSubject<void>(null);
public reload():void{
this.reloader.next(null);
}
}
Run Code Online (Sandbox Code Playgroud)
然后,无论何时创建数据服务,只需要将其与可观察到的重新加载器合并即可:
@Injectable()
export class ResourceService {
private _resources: Observable<Resource[]>;
constructor(private reloaderService: DataReloaderService, private http: HttpClient){
this._resources = this.reloaderService.reloader.mergeMap(() => this.http.get(...));
}
}
Run Code Online (Sandbox Code Playgroud)
最后,在您的服务中进行修改:
export class TodoService {
private _todos: BehaviorSubject<Todo[]>;
constructor(private reloaderService: DataReloaderService, private http: HttpClient) {
this._todos = this.reloaderService.reloader.mergeMap(() => this.http.get(dataUrl));
}
public get todos(): Observable<Todo[]>{
return this._todos;
}
addResourceRequirement(todo: ToDo, resource: Resource) {
this.http.post(`${url}/${todo.id}/`, {addResource: resource.id})
.subscribe(() => this.reloader.reload());
}
}
Run Code Online (Sandbox Code Playgroud)
注意:您不应该订阅服务,Observables的目的是建立一个冷链,您只需在显示部分进行订阅,第二种模式可确保您的所有Observables都链接到中央重新加载器(您也可以创建多个重新加载程序(例如,每个模型系列一个),订阅会使您失去这一点,从而导致只能以奇怪的方式编辑数据。如果所有内容都取决于您的api,那么您的可观察对象应仅使用此api,并在每次编辑内容时都调用reloader,以确保所有链接的数据也得到更新。
| 归档时间: |
|
| 查看次数: |
1661 次 |
| 最近记录: |