You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Angular ModalService中自动推断组件@Input属性类型?

Angular ModalService 自动推断@Input参数优化方案

核心思路

通过TypeScript的映射类型与条件类型,直接从组件类型中过滤出@Input属性(排除@Output及其他非输入成员),无需在组件中添加冗余的_options字段,同时通过泛型实现通用的open方法,适配所有模态组件。

实现步骤

1. 定义类型工具提取@Input属性

首先编写一个类型工具,精准筛选出组件类中的@Input属性:

import { EventEmitter } from '@angular/core';

// 判断属性是否为@Output的EventEmitter类型
type IsOutput<T> = T extends EventEmitter<any> ? true : false;

// 从组件类型中排除所有@Output属性,仅保留@Input属性
type InputsOf<T> = Omit<T, { [K in keyof T]: IsOutput<T[K]> }[keyof T]>;

2. 实现通用ModalService

基于Angular CDK的Overlay实现模态框服务,利用泛型自动推断组件的@Input参数类型:

import { Injectable, Type, ComponentRef, inject } from '@angular/core';
import { Overlay, OverlayRef } from '@angular/cdk/overlay';
import { ComponentPortal } from '@angular/cdk/portal';

@Injectable({ providedIn: 'root' })
export class ModalService {
  private overlay = inject(Overlay);

  open<T>(component: Type<T>, options: InputsOf<T>): ComponentRef<T> {
    // 创建Overlay实例,配置模态框位置与遮罩
    const overlayRef = this.overlay.create({
      positionStrategy: this.overlay.position().global().centerHorizontally().centerVertically(),
      hasBackdrop: true,
      backdropClass: 'modal-backdrop'
    });

    // 创建组件门户并挂载到Overlay
    const portal = new ComponentPortal(component);
    const componentRef = overlayRef.attach(portal);

    // 将options赋值给组件的@Input属性
    Object.entries(options).forEach(([key, value]) => {
      componentRef.instance[key as keyof T] = value;
    });

    // 点击遮罩关闭模态框
    overlayRef.backdropClick().subscribe(() => {
      overlayRef.dispose();
    });

    return componentRef;
  }
}

3. 编写模态组件示例

无需添加冗余字段,正常定义@Input和@Output即可:

import { Component, Input, Output, EventEmitter } from '@angular/core';

@Component({
  template: `
    <div class="modal-container">
      <h3>确认操作</h3>
      <p>{{ message }}</p>
      <div class="modal-actions">
        <button (click)="confirm.emit()" class="btn-confirm">确认</button>
        <button (click)="cancel.emit()" class="btn-cancel">取消</button>
      </div>
    </div>
  `,
  styles: [`
    .modal-container { padding: 20px; background: white; border-radius: 8px; }
    .modal-actions { margin-top: 20px; display: flex; gap: 10px; justify-content: flex-end; }
  `]
})
export class ConfirmationModal {
  @Input() message!: string;
  @Output() confirm = new EventEmitter<void>();
  @Output() cancel = new EventEmitter<void>();
}

4. 使用示例

调用open方法时,TypeScript会自动推断第二个参数只能包含@Input字段:

// 正确:仅传入message字段(符合@Input类型)
const modalRef = this.modalService.open(ConfirmationModal, { message: '确定要删除这条数据吗?' });

// 错误:传入了@Output的confirm字段,TypeScript会报错
this.modalService.open(ConfirmationModal, { message: '提示', confirm: () => {} });

注意事项

  • 如果组件中存在非@Output但类型为EventEmitter的属性,类型工具会误判为@Output,此时可根据实际需求调整IsOutput的判断逻辑。
  • 运行时赋值@Input属性后,Angular会自动触发变更检测,无需手动调用detectChanges。

内容的提问来源于stack exchange,提问作者Mark Johansen

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.16 05:02:49