Angular自定义装饰器实现组件加载时添加Spinner组件的技术咨询
Angular 方法装饰器实现组件内加载Spinner方案
核心问题分析
装饰器在类定义阶段执行,无法直接获取Angular组件工厂,必须借助组件实例初始化后注入的依赖服务(如ViewContainerRef、Renderer2),通过this上下文访问这些服务来完成Spinner的创建与销毁。
具体实现步骤
1. 目标组件准备
在需要添加Spinner的组件中,注入并公开ElementRef、Renderer2和ViewContainerRef,供装饰器调用:
import { Component, ElementRef, Renderer2, ViewContainerRef } from '@angular/core'; import { loaderDecorator } from './loader.decorator'; @Component({ selector: 'app-target-component', template: `<button (click)="fetchData()">发起请求</button>` }) export class TargetComponent { constructor( public elRef: ElementRef, public renderer: Renderer2, public viewContainerRef: ViewContainerRef ) {} @loaderDecorator() async fetchData() { // 此处编写API请求逻辑,例如: // return await this.http.get('/api/data').toPromise(); } }
2. 装饰器实现
通过组件实例的this上下文获取依赖服务,完成Spinner的创建、插入与销毁:
import { ComponentFactoryResolver } from '@angular/core'; import { HttpSpinnerComponent } from './http-spinner.component'; export function loaderDecorator() { return function (target: any, propertyName: string, descriptor: PropertyDescriptor) { const originalMethod = descriptor.value; descriptor.value = async function (...args: any[]) { let spinnerComponentRef: any; const compInstance = this; try { // 解析Spinner组件工厂并创建实例 const spinnerFactory = compInstance.viewContainerRef.injector .get(ComponentFactoryResolver) .resolveComponentFactory(HttpSpinnerComponent); spinnerComponentRef = compInstance.viewContainerRef.createComponent(spinnerFactory); // 将Spinner插入到当前组件的上方位置 compInstance.renderer.insertBefore( compInstance.elRef.nativeElement.parentNode, spinnerComponentRef.location.nativeElement, compInstance.elRef.nativeElement ); // 执行原请求方法 return await originalMethod.apply(this, args); } catch (error) { console.error('请求异常:', error); throw error; } finally { // 无论请求成败都销毁Spinner if (spinnerComponentRef) { spinnerComponentRef.destroy(); } } }; return descriptor; }; }
3. Spinner组件配置
确保HttpSpinnerComponent在模块中正确声明:
import { NgModule } from '@angular/core'; import { HttpSpinnerComponent } from './http-spinner.component'; import { TargetComponent } from './target.component'; @NgModule({ declarations: [HttpSpinnerComponent, TargetComponent], entryComponents: [HttpSpinnerComponent] // Angular 9+ 使用独立组件可省略此项 }) export class AppModule {}
Angular 13+ 独立组件简化方案
若HttpSpinnerComponent是独立组件,创建逻辑可简化为:
// 替换装饰器中创建Spinner的代码 spinnerComponentRef = compInstance.viewContainerRef.createComponent(HttpSpinnerComponent);
拦截器方案失败的常见原因
拦截器是全局生效的,无法直接关联到发起请求的具体组件实例,因此无法精准在目标组件上方插入Spinner。而方法装饰器绑定组件内的请求方法,能通过组件实例上下文精准操作DOM。
内容的提问来源于stack exchange,提问作者Remy
相关产品推荐
相关产品推荐

