在页面中多次引导相同的angular2模块

时间:2016-11-24 09:59:31

标签: angular

我有一个Web表单应用程序,它根据配置动态地将用户控件加载到页面上(就像具有可重用小部件的CMS一样)。我想使用Angular2组件实现一个用户控件/小部件。

<%@ Control Language="C#" AutoEventWireup="true" CodeBehind="Ng2Widget.ascx.cs" Inherits="Namespace.Ng2Widget" %>
<div>
     <app-root inputValue="<%= settingsValue %>"></app-root>
</div>

&#34; settingsValue&#34;将是服务器端变量,其中包含服务器要传递给组件的某个值。

因此,如果管理员使用这个新实现的小部件的多个小部件配置页面,则呈现的页面将具有多个相同类型的Angular2组件。

渲染页面基本上有

<div>
         <app-root inputValue="settingsValue1"></app-root>
</div>
<!-- Some other widgets -->
<div>
         <app-root inputValue="settingsValue2"></app-root>
</div>

为了将值传递给根组件,我使用了本文中提到的方法。 Angular 2 input parameters on root directive

在页面底部,我放置了引导模块的js文件。

<script type="text/javascript" src="ng2/dist/inline.bundle.js"></script>
<script type="text/javascript" src="ng2/dist/styles.bundle.js"></script>
<script type="text/javascript" src="ng2/dist/main.bundle.js"></script>

加载页面后,Angular会将模块仅引导到第一个标记实例,并完全忽略第二个实例。

我在网上找到的所有例子都试图引导不同模块类型的多个模块,这似乎工作正常。

在Angular1中,根应用程序的范围限定为dom元素,并且任何带有ng-controller属性修饰的dom元素都会将组件加载到各自的位置。

在早期版本的Angular2中,我们能够引导组件。但随着NgModule的推出,一个模块正在被引导。但即便如此,我也不认为相同的组件可以被引导到多个实例。(即标签的所有实例,呈现组件。)

我看到了关于同样的讨论 https://www.reddit.com/r/Angular2/comments/424nwn/using_angular_2_without_it_being_a_single_page_app/

我觉得这对于遗留服务器端驱动的应用程序试图利用像Angular2这样的客户端框架而言是相关的。我能够非常轻松地使用React组件,因为它们在运行时通过简单的渲染方法调用呈现并附加到dom元素。

有没有办法/解决方法可以在页面中引导同一模块的多个实例?即。在所有出现的标签中加载组件。

3 个答案:

答案 0 :(得分:4)

我有完全相同的问题,在挖掘网页后,我终于在这个plnkr中找到了解决方案:

https://plnkr.co/edit/I6kxKa78Np73sIWbbYHz?p=preview

神奇的部分在于ngDoBootstrap功能:

ngDoBootstrap(appRef : ApplicationRef) {
    this.components.forEach((componentDef : {type: Type<any>, selector: string}) => {
      const factory = this.resolver.resolveComponentFactory(componentDef.type);
      factory.selector = componentDef.selector;
      appRef.bootstrap(factory);
    });
}

想法是在包装器模块中注入组件实例列表,并引导由ComponentFactoryResolver检索的每个组件。

希望得到这个帮助。

答案 1 :(得分:0)

这适用于angular5:

export class DetailOfferAppModule {

    constructor(private resolver: ComponentFactoryResolver) {

    }

    ngDoBootstrap(appRef: ApplicationRef) {

        ...

        //find matching elements on the page and bootstrap each one
        var elements = $('my-widget').toArray();
        if (elements && elements.length > 0) {

            elements.forEach(sectionElement => {
                appRef.bootstrap(MyComponent, sectionElement);
            });
        }
    }
}

HTML

<my-widget id="widgetA"></my-widget>
<my-widget id="widgetB"></my-widget>

答案 2 :(得分:0)

希望将此包含在此作为完整的模块示例。这可以通过在NgModule ngDoBootstrap方法中手动引导根级别组件来完成。

(请注意,在Angular 5+中可能不再需要此方法,请参阅this Angular PR

我们首先找到我们想要引导的所有根元素,并为它们提供唯一的ID。然后,对于每个实例,使用新ID攻击组件工厂选择器并触发引导程序。

const entryComponents = [
  RootComponent,
];

@NgModule({
  entryComponents,
  imports: [
    BrowserModule,
  ],
  declarations: [
    RootComponent,
  ],
})
export class MyModule {
  constructor(private resolver: ComponentFactoryResolver) {}

  ngDoBootstrap(appRef: ApplicationRef) {
    entryComponents.forEach((component: any) => {
      const factory = this.resolver.resolveComponentFactory(component);
      let selectorName;
      let elements;

      // if selector is a class
      if (factory.selector.startsWith('.')) {
        selectorName = factory.selector.replace(/^\./, '');
        elements = document.getElementsByClassName(selectorName);

      // else assume selector is an element
      } else {
        selectorName = factory.selector;
        elements = document.getElementsByTagName(selectorName);
      }

      // no elements found, early return
      if (elements.length === 0) {
        return;
      }

      // more than one root level componenet found, bootstrap unique instances
      if (elements.length > 1) {
        const originalSelector = factory.selector;

        for (let i = 0; i < elements.length; i += 1) {
          elements[i].id = selectorName + '_' + i;
          (<any>factory).factory.selector = '#' + elements[i].id;
          appRef.bootstrap(factory);
        }

        (<any>factory).factory.selector = originalSelector;

      // only a single root level component found, bootstrap as usual
      } else {
        appRef.bootstrap(factory);
      }
    });
  }
}

现在,假设我们的RootComponent的选择器是'.angular-micro-app',这将按预期工作:

<body>
    <div class="angular-micro-app"></div>
    ...
    <div class="angular-micro-app"></div>
</body>