我有一个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元素。
有没有办法/解决方法可以在页面中引导同一模块的多个实例?即。在所有出现的标签中加载组件。
答案 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>