Skip to content

挂载卸载

javascript

/**
 * 接口 OnDetach,定义 ngOnDetach 方法。
 */
export interface OnDetach {
  ngOnDetach: () => void;
}

/**
 * 接口 OnAttach,定义 ngOnAttach 方法。
 */
export interface OnAttach {
  ngOnAttach: () => void;
}

@Injectable({
  providedIn: 'root',
})
/**
 * SplitCellsService 服务,用于管理组件的挂载和卸载。
 */
export class SplitCellsService {
  // 实例缓存-还原
  instances = new Map<number, ComponentRef<OnAttach & OnDetach>>();
  // 视图缓存
  views = new Map<number, ViewRef>();
  // 滚动位置缓存
  scrollTop = new WeakMap<ViewRef, number>();

  /**
   * 挂载组件到容器中。
   *
   * @param container - 要挂载组件的 ViewContainerRef 容器。
   * @param wid - 组件的唯一标识符。
   */
  attach(container: ViewContainerRef, wid: number) {
    const viewCache = this.views.get(wid);
    if (viewCache) {
      // 如果存在视图缓存,则插入到容器中并恢复滚动位置
      container?.insert(viewCache);
      this.getScrollContainer(wid)?.scrollTo(0, this.scrollTop.get(viewCache));
      this.instances.get(wid)?.instance?.ngOnAttach?.();
      this.views.delete(wid);
      this.scrollTop.delete(viewCache);
    } else {
      // 如果不存在视图缓存,则创建新的组件实例并保存
      const instance = container?.createComponent<OnDetach & OnAttach>(ChatComponent);
      instance?.setInput('wid', wid);
      this.instances.set(wid, instance);
    }
  }

  /**
   * 从容器中卸载组件。
   *
   * @param container - 要卸载组件的 ViewContainerRef 容器。
   * @param wid - 组件的唯一标识符。
   */
  detach(container: ViewContainerRef, wid: number) {
    if (!container) return;
    // 调用组件实例的 ngOnDetach 方法
    this.instances.get(wid)?.instance?.ngOnDetach?.();
    const view = container?.detach();
    if (view) {
      // 缓存视图和滚动位置
      this.views.set(wid, view);
      const scrollContainer = this.getScrollContainer(wid);
      this.scrollTop.set(view, scrollContainer?.scrollTop);
    }
  }

  /**
   * 将组件从一个容器移动到另一个容器。
   *
   * @param fromContainer - 当前容器。
   * @param toContainer - 目标容器。
   * @param wid - 组件的唯一标识符。
   */
  move(fromContainer: ViewContainerRef, toContainer: ViewContainerRef, wid: number) {
    this.detach(fromContainer, wid);
    this.attach(toContainer, wid);
  }

  /**
   * 获取滚动容器元素。
   *
   * @param wid - 组件的唯一标识符。
   * @returns 滚动容器元素或 null。
   */
  getScrollContainer(wid: number) {
    return this.instances.get(wid)?.location.nativeElement.querySelector('.conv-container');
  }
}