kittencup / angular2-ama-cn

angular2 随便问
691 stars 101 forks source link

如何动态加载不确定的组件 #125

Open Imporial opened 8 years ago

Imporial commented 8 years ago

在 #21 #73 #121 讲解了动态加载组件的方法,但这些要加载的组件都是确定的, 有这种情况,将某个组件作为容器,但是作为内容的子组件不确定,有可能是其他任何一个。

有没有办法只绑定一个 selector 或 什么东东作为参数,由这个容器组件根据参数来动态加载子组件

kittencup commented 8 years ago

贴点具体代码。

Imporial commented 8 years ago

根据 selector 反查组件,或者 传递 ng-content 的内容到动态加载的组件上,应该怎么做

kittencup commented 8 years ago

还是贴点代码吧,这个实在不理解。

Imporial commented 8 years ago
export class MdlWindowBtn {

    @Input() cmp :string;
    windowPromise: Promise<ComponentRef>;

    constructor(private loader: DynamicComponentLoader, 
        @Inject(forwardRef(() => Framework)) private app: Framework, private el: ElementRef) { }
     /**
     * 单击此按钮弹出窗口
     * @param event
     */
    btnClick(event: JQueryEventObject) {
         this.loader.loadNextToLocation(MdlWindow, this.app.RootRef);
    }
}

这里用 DynamicComponentLoader 动态加载一个 MdlWindow 组件,这个 MdlWindow 组件是已知的,想知道如何动态加载某个组件,这个组件能由 cmp 这个参数确定?

Imporial commented 8 years ago

转换思路, 在外层组件中 MdlWindow 的内容组件是已知的,可以用脚本通过逐层绑定事件,最终将 MdlWindow 的 ElementRef 传递到外层组件中动态加载内容组件,而不是通过 DOM 元素加载,这样就可以不用动态加载未知组件了。

Imporial commented 8 years ago

beta.16 最新API

DynamicComponentLoader.loadNextToLocation();

有点这个意思了,不知道可不可以直接 @Input() 一个类

LinboLen commented 8 years ago

用@Input()不是一个好的实践, 应该用 di 进行包装覆写

hstarorg commented 8 years ago

这个问题,我也遇到了。我这边采用的做法如下:

1、使用了ui-router-ng2,这个时候我可以在路由钩子中执行操作。 2、在路由切换时,我会判断组件是否存在,如果不存在,则执行我的加载函数:

uiRouter.transitionService.onBefore({}, t => {
      // return false;
      // // debugger
      // console.log('onBefore', t);
      let toState = t.$to().name;
      if (!t.router.stateService.get(toState)) {
        return new Promise<boolean>((resolve, reject) => {
          this.negModuleLoader.load('nk-common').then(_ => {
            uiRouter.stateService.go('nkCommon.comp1');
          });
          resolve(true);
        });
      }
      return Promise.resolve(true);
    });

negModuleLoader.ts

import { Injectable, Inject } from '@angular/core';
import {Http} from '@angular/http';

import {UIRouter} from 'ui-router-ng2';

@Injectable()
export class NegModuleLoader {

  private uiRouter: UIRouter;

  private loadedModules: Set<string>;

  constructor(private http: Http) {
    this.loadedModules = new Set<string>();
  }

  setRouter(uiRouter) {
    this.uiRouter = uiRouter;
  }

  load(moduleName): Promise<any> {
    if (this.loadedModules.has(moduleName)) {
      return Promise.resolve();
    }
    return new Promise((resolve, reject) => {
      this.http.get(`/assets/js/${moduleName}.js`)
        .toPromise()
        .then(res => {
          let mod = eval(res.text());
         //这里可以修改下,使用单个组件。我这里由于需求原因,是一次性加载多个组件的代码。
          mod.APP_STATES.forEach(state => {
            this.uiRouter.stateRegistry.register(state);
          });
          this.loadedModules.add(moduleName);
          resolve();
        }).catch(err => reject(err));
    });
  }
}