angular - 角度混乱与可观察对象和行为主体。想不出重构我的代码的正确方法
问题描述
我知道这个标题有点含糊,但我会用我的服务来解释。
基本上,我有这个应用程序范围的服务,我可以使用它从我从后端获取的源列表中设置一个源,并且我的应用程序的各个部分都使用该源。
当用户第一次进入我的网站并且尚未选择来源时,我想将其设置为从我的服务器获取的列表中的第一个来源。一旦用户更改了选择的源,我将他的选择存储在浏览器存储中,这样当他回来时,我可以将其加载回来。
这一切都很好,但是我想添加的新功能是我希望可以发送带有“ ?source=abc ”的链接,然后当有人打开它时,我会将当前源覆盖为该值。
这是我的代码:
@Injectable({
providedIn: 'root'
})
export class SourceService {
private apiUrl = environment.apiUrl;
private sources = new BehaviorSubject<Map<string, Source>>(new Map<string, Source>());
private source = new BehaviorSubject<Source>(null);
private sourceOverride: string;
constructor(private http: HttpClient) {
this.http.get<Source[]>(this.apiUrl + "/sources").subscribe(
sources => {
const sourceMap = new Map<string, Source>();
sources.forEach(source => sourceMap.set(source.id, source));
this.sources.next(sourceMap);
}
);
}
overrideSourceById(sourceId: string): void {
this.sourceOverride = sourceId;
}
getSource(): Observable<Source> {
if (this.sourceOverride) {
const sourceOverride = this.sourceOverride;
this.sourceOverride = null;
return this.getSourceById(sourceOverride).pipe(mergeMap(source => {
this.setSource(source);
return this.getSource();
}));
} else if (!this.source.getValue()) {
return this.sources.pipe(mergeMap(sourceMap => {
const historySourceId = localStorage.getItem('source');
if (historySourceId) {
const historySource = sourceMap.get(historySourceId);
if (historySource) {
this.setSource(historySource);
} else {
this.setSource(sourceMap.get(sourceMap.keys().next().value));
}
} else {
this.setSource(sourceMap.get(sourceMap.keys().next().value));
}
return this.getSource();
}));
}
return this.source;
}
setSource(source: Source): void {
localStorage.setItem("source", source.id);
this.source.next(source);
}
getAvailableSources(): Observable<Source[]> {
return this.sources.pipe(skip(1)).pipe(map(sources => Array.from(sources.values())));
}
getSourceById(id: string): Observable<Source> {
return this.sources.pipe(skip(1)).pipe(map(sources => sources.get(id))).pipe(first());
}
}
除了看起来不对,我在同步所有内容时遇到问题。
例如,我有一些页面使用getSourceById方法来获取当前选择的源,读取它的特定字段,然后为img标签生成一个 url。
事情的顺序让我失望。源在实际填充之前被读取,依此类推。
我能做些什么来改善这一点?
解决方案
这几乎可以肯定是因为您使用了错误的主题。简要介绍一下。
- 主题 - 本质上是一个消息泵。没有以前发射的“记忆”。
- ReplaySubject - 与 Subject 类似,但会在订阅时立即抽取 X 量的先前发射。
- BehaviorSubject - 类似于 Replay Subject,但只会发出一个先前的事件,但也必须有一个默认值。
更多阅读:https ://tutorialsforangular.com/2020/12/12/subject-vs-replaysubject-vs-behaviorsubject/
无论如何,这就是我注意到的:
private source = new BehaviorSubject<Source>(null);
这段代码意味着如果有人订阅了这个,他们将立即收到一个空值。这是预期的吗?我认为不是因为:
getSourceById(id: string): Observable<Source> {
return this.sources.pipe(skip(1)).pipe(map(sources => sources.get(id))).pipe(first());
}
我认为这里的跳过是因为你得到了一个正确的空值?所以你想,如果我跳过第一个事件(null),那么我很高兴。但是,一旦您通过源发出另一个事件,您现在就跳过了合法数据。
这是我会尝试的:
将所有 BehaviourSubjects 更改为 ReplaySubject(1)。这意味着它总是返回最后一条数据,但不会有默认的 null 发射。
删除你的跳过。您只是跳过,因为您正在使用 BehaviourSubjects 并且您不想要默认值。但如果换成 ReplaySubject,就不再需要跳过了。
推荐阅读
- c - 可以使用 .c 和 .cu 文件混合生成文件吗?如果是这样,如何?
- python - 如何将列表转换为文件并将该文件再次加载到列表中?
- azure - “无效模板”;消息部署模板解析失败:
- android - 将数据从 Activity 移动到另一个 Activity 的 TabFragment 的 Fragment
- c# - Unity 项目仅适用于 IL2CPP 上的 Development Build
- node.js - 在 Azure Active Directory 上自动创建基于应用注册的广告管理员用户名和密码
- python - Matplotlib 图形的输出图像在不同操作系统中具有不同的大小
- html - 将类添加到响应式站点上最右侧的 div
- ios - 如何正确添加蒙版图像?
- vue.js - 如何从离子输入(Vue)中获取绑定值