Angular 中依赖注入问题造成 Observable 订阅不更新

news/2025/1/19 6:07:59/文章来源:https://www.cnblogs.com/dudu/p/18678346

这是园子博客后台从 angular 15 升级到 angular 19 后遇到的一个问题。博客后台「随笔 」的侧边栏会显示随笔的分类列表 ,通过这个列表的上下文菜单可以修改分类名称,升级后测试时发现一个问题,修改分类名称后分类列表没有随之更新。

侧边栏随笔分类列表是 SidebarBlogCategoriesComponent 通过订阅 BlogCategoryStore 的 Observable 类型的 categories$ 属性更新的

class SidebarBlogCategoriesComponent implements OnInit {ngOnInit(): void {this._store.categories$.pipe(this._rxHelper.autoUnsubscribe,distinctUntilChanged<BlogCategoryEditDto[]>(isEqual),map(categories => {//...})).subscribe(async nodes => {//...});}
}

对应的 BlogCategoryStore 部分实现代码

@Injectable({providedIn: 'any'
})
export class BlogCategoryStore {readonly categoryList$: Observable<BlogCategoryList>;protected readonly $categoryList = new BehaviorSubject<BlogCategoryList | undefined>(undefined);private _categories$?: Observable<BlogCategoryEditDto[]> | null;get categories$(): Observable<BlogCategoryEditDto[]> {return (this._categories$ ??= this.$categoryList.pipe(map(x => x?.categories ?? [])));}
}

页面加载时通过 SidebarBlogCategoriesComponent 的 parent component PostsSidebarComponent 调用 BlogCategoryStore.refresh 方法触发分类列表的订阅更新,这个地方正常

export class PostsSidebarComponent implements OnInit {ngOnInit() {this.categoryStore.refresh(BlogCategoryType.postCollection);}
}

修改分类名称通过上下文菜单打开编辑分类的对话框进行的:

1)打开上下文菜单是通过调用 CategoryContextMenuServiceopen 方法动态创建 CategoryContextMenuComponent

@Injectable({providedIn: 'any',
})
export class CategoryContextMenuService {constructor(private readonly contextMenuServ: NzContextMenuService,) { }async open<THost extends object>(categoryType: BlogCategoryType,ev: MouseEvent,containerRef: ViewContainerRef) {const { CategoryContextMenuComponent: _CategoryContextMenuComponent } = await import('./category-context-menu/category-context-menu.component');const compRef = containerRef.createComponent(_CategoryContextMenuComponent);//...if (comp.menu) this.contextMenuServ.create(ev, comp.menu);}
}

2)在上下文菜单中点击编辑按钮,则调用 BlogCategoryEditorModalServiceopen 方法通过 NzModalService 打开模态对话框

openEditCategoryModal() {if (this.category) {this._categoryModalServ.open(this.categoryType, {category: this.category,});}
}

BlogCategoryEditorModalService 中对应的部分实现代码

@Injectable({providedIn: 'any',
})
export class BlogCategoryEditorModalService {constructor(private _categoryServ: BlogCategoryService,private _categoryStore: BlogCategoryStore,private readonly nzModalService: NzModalService) { }open(categoryType: BlogCategoryType, { category, createdCallback }: CategoryEditModalOption = {}) {const isCreating = !category || category.categoryId <= 0;const categoryTypeName = BlogCategoryTypeNameMap[categoryType];const title = isCreating ? `新建${categoryTypeName}` : `编辑${categoryTypeName}`;const modalRef = this.nzModalService.create({nzTitle: title,nzContent: BlogCategoryEditComponent,nzData: { categoryType, category },nzOnOk: async comp => {//.... await Promise.all(Array.from(parentsToRefresh).map(p =>// 在编辑分类后刷新分类列表 this._categoryStore.refreshAsync(categoryType, p)));//....return true;},});}
}

3)模态对话框中运行的是进行分类编辑操作的 BlogCategoryEditComponent

export class BlogCategoryEditComponent implements OnInit {constructor(private readonly fb: NonNullableFormBuilder,private readonly systemInfoServ: SystemInfoService,@Inject(NZ_MODAL_DATA)private readonly nzModalData: {categoryType: BlogCategoryType,category: BlogCategoryEditDto | null},) {//...}//...
}

在模态对话框中完成分类编辑(修改分类名称)后,模态对话框会关闭,然后执行 BlogCategoryEditorModalService 中的 nzOnOk 回调方法,调用 this._categoryStore.refreshAsync 方法更新 BlogCategoryStore 中的 _categories$SidebarBlogCategoriesComponent 订阅了这个 Observable,从而触发侧边栏分类列表的更新。

现在的问题是虽然 this._categoryStore.refreshAsync 用修改后分类列表数据更新了 _categories$,但 SidebarBlogCategoriesComponent 并没有响应这个 Observable 的更新。

一开始折腾了一段时间,没找到任何线索。

后来突然想到,出现这个问题唯一可以解释得通的原因就是 SidebarBlogCategoriesComponent 订阅的 BlogCategoryStoreBlogCategoryEditorModalService 更新的 BlogCategoryStore 不是同一个实例。

有了这个思路后,立马想到的是动手验证这个2个实例是否是同一个,参考这篇博文 Get object reference IDs in JavaScript/TypeScript,很快完成了验证,详见博问 https://q.cnblogs.com/q/151643

验证结果如下:

CategoryContextMenuService.categoryStore id:  2
SidebarBlogCategoriesComponent._store id:  2
CategoryContextMenuComponent.categoryStore id:  3
BlogCategoryEditorModalService._categoryStore id:  3

果然不是同一个实例!从 CategoryContextMenuComponent 开始就不是同一个实例,这个 Component 是通过下面的代码动态创建的

const compRef = containerRef.createComponent(_CategoryContextMenuComponent);

改为在 createComponent 时将 Injector 与 EnvironmentInjector 传递过去,问题就解决了。

const compRef = containerRef.createComponent(_CategoryContextMenuComponent,{injector: this.injector,environmentInjector: this.envInjector}
);

之所以升级后出现这个问题,是因为在升级过程中重构代码时删除了上面的 createComponent 时传递 Injector 的代码。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/871584.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

极紫外光刻掩模上三维图案的严格模拟(下)

1D线掩模:全3D计算域 首先,使用包含吸收体结构和多层反射镜的3D计算域重新审视EUV线掩模。图5显示了对几何体进行离散化的网格(使用网格生成器JCMgeo自动生成)。对于三维设置,网格由棱柱形元素组成(而不是二维设置中的三角形元素)。使用不同的空间网格对相同的物理设置进…

极紫外光刻掩模上三维图案的严格模拟(上)

对具有二维周期性吸收体图案的极紫外光刻掩模的光散射进行了模拟。在一项详细的收敛研究中,表明在相对较大的3D计算域以及存在侧壁角度和拐角圆角的情况下,可以获得准确的结果。 材料和参数设置 所研究的结构由多层反射镜上的吸收器堆叠组成(共120层)。图1显示了几何形状的…

如何在M芯片的Mac上爽玩原神

如何在M芯片的Mac上爽玩原神 【热点速递】苹果震撼发布全新M4 Mac mini,国补福利下惊喜价如何在M芯片的Mac上爽玩原神【热点速递】苹果震撼发布全新M4 Mac mini,国补福利下惊喜价仅约3500元!这不仅是一次办公体验的全新升级,更是对高效能与性价比完美融合的一次致敬。想象一…

macOS安装软件过程中常见几种报错的解决办法

macOS安装软件过程中常见几种报错的解决办法 对于刚使用 macOS 或者在更新系统后尝试运行应用对于刚使用 macOS 或者在更新系统后尝试运行应用时遇到问题的用户,可能会看到以下几种错误提示:xxx已损坏,无法打开,你应该将它移到废纸篓打不开 xxx,因为它来自身份不明的开发者…

我来告诉你怎么在macOS上畅玩金铲铲之战

我来告诉你怎么在macOS上畅玩金铲铲之战 ❝ 天选福星,灵蛇献瑞,《金铲铲之战》“天选福星”赛季好我来告诉你怎么在macOS上畅玩金铲铲之战天选福星,灵蛇献瑞,《金铲铲之战》“天选福星”赛季好运上线!请接收这份来自《金铲铲之战》的新春邀约——“天选福星”正式回归,羁…

4本书推荐《AI芯片开发核心技术详解》、《智能汽车传感器:原理设计应用》、《TVM编译器原理与实践》、《LLVM编译器原理与实践》,谢谢

4本书推荐《AI芯片开发核心技术详解》、《智能汽车传感器:原理设计应用》、《TVM编译器原理与实践》、《LLVM编译器原理与实践》由清华大学出版社资深编辑赵佳霓老师策划编辑的新书《AI芯片开发核心技术详解》已经出版,京东、淘宝天猫、当当等网上,相应陆陆续续可以购买。该…

Cain的2024小记

2024の总结在清水中放一块糖,不会太甜 但放一勺醋,就会很酸 人不能因为一件事高兴一整年 却能因为一个创伤郁郁终身 痛苦给人的刺激总是远远大于快乐 所以人们宁可不得到,也不愿失去 渐渐的 不喜不悲又到了一年一度的破壳日,祝我生日快乐的同时,写下拖延许久的年度总结,剖…

【Windows内核】早期级联注入:Windows 进程创建、Early bird APC 注入和 EDR 预加载

一、介绍 在这篇博客文章中,我们介绍了一种名为早期级联注入的新型进程注入技术,探讨了 Windows 进程创建,并识别了几种端点检测和响应系统(EDR)如何初始化其进程内检测能力。这种新的早期级联注入技术针对进程创建的用户模式部分,结合了众所周知的 Early bird APC 注入技…

CODEFORCE DIV2 NO.996(好社畜的场次名)

这一次的博客其实早就应该发布了,但是当时急着回家睡觉,于是就直接把博客的编辑页面给关闭了,于是没有保存,完成了3/4的博客就这样没有了,对,所以这件事启示了我们写完博客一定要保存好草稿,不然就是唐完了。问就是唐龙 首先是这场比赛的评价,当时真的是犯蠢了,感觉是…

【Atcoder训练记录】AtCoder Beginner Contest 389

训练情况赛后反思 赛后VP的,C题忘记vector里面erase复杂度是 O(n) 的了,导致TLE了两发,换成双端deque就过了 A题 取字符串第一位和第三位取int相乘 #include <bits/stdc++.h> // #define int long long #define endl \nusing namespace std;void solve(){string s; ci…

一图理解RAG与Agentic RAG的区别

RAG 是一种结合了信息检索和生成模型的自然语言处理技术框架,能够提高 AI 系统在回答自然语言问题时准确性和可靠性,但是传统 RAG 还有不少问题,比如: 它检索一次生成一次。如果上下文不足,无法动态搜索更多信息。 它无法对复杂查询进行推理。 系统无法根据具体问题调整策…

常用的9款工业调试工具

modbus调试工具 这个工具是用来调试modbus通讯协议报文的。分二个一个是模拟modbus协议一个是监听modbus通讯协议。 poll是监听工具slave是模拟工具。大家在我提供的安装包里都有可自行选择,怎么使用可以看往期间文章串口调试工具 串口调试工具,需要设备对应的波特率、停止位…