- Java 双重比较
- java - 比较器与 Apache BeanComparator
- Objective-C 完成 block 导致额外的方法调用?
- database - RESTful URI 是否应该公开数据库主键?
我需要一些关于 ComponentFactoryResolver 和架构的建议。我的代码中有 n 个面板(后端提供面板的数量),在这些面板中,我有动态字段(数量也由后端提供)。例如,每个面板的开头应该有 4 个输入字段。可以删除或添加字段,具体取决于用户请求。我尝试用 ComponentFactoryResolver 解决这个问题,但有点卡住了。
首先,我尝试了两个嵌套循环,一个用于面板,一个用于字段 - 不起作用,下面的代码永远不会在页面上呈现。似乎 ng 模板没有弄清楚我的动态字段 - 或者我遗漏了一些东西。
<div #container *ngFor="let i of [1,2,3]"></div>
其次,我已将代码从 HTML 移动到 TypeScript,现在我正在使用 AfterViewInit 循环,并且我已经设法在我的页面上进行了动态归档 - 但现在我遇到了一个问题,即所有字段都显示在第一个面板中,然后每个面板应该有 4 个字段...
此外,用于添加和删除字段的按钮应该仅适用于混凝土面板。例如:如果我单击第二个面板中的第二个添加按钮,我会在第二个面板中显示添加字段。就我而言,这仅适用于第一个面板。
我不想使用一些 ngIf 语句并定义一些字段。我想学习解决此类问题的动态和通用方法。
我做了一个plunker demo: https://plnkr.co/edit/FjCbThpmBmDcgixTpXOy?p=preview
抱歉这篇文章太长了。我希望我已经很好地解释了这个问题。任何建议将不胜感激。
最佳答案
我有类似的要求并使用了 ComponentFactoryResolver
.但是,我在 ng-template
周围放了一个 wrapper 像这样:
@Component({
selector: 'tn-dynamic',
template: `<ng-template #container></ng-template>`,
providers: [SubscriptionManagerService],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class DynamicComponent<T>
extends DynamicComponentBase<T, T, ComponentData>
implements OnChanges, OnDestroy {
@Input()
set componentData(data: ComponentData) {
if (!data) {
return;
}
try {
let type: Type<T> = getComponentType(data.type);
this._factory = this.resolver.resolveComponentFactory(type);
let injector = Injector.create([], this.vcRef.parentInjector);
this._factoryComponent = this.container.createComponent(this._factory, 0, injector);
this._currentComponent = this._factoryComponent.instance;
this._factoryComponent.location.nativeElement.classList.add('tn-dynamic-child');
} catch (er) {
console.error(`The type ${data.type.library}
.${data.type.key} has not been registered. Check entryComponents?`);
throw er;
}
}
// Handle ngOnChanges, ngOnDestroy
}
然后我会围绕我的组件 <tn-dynamic [componentData]="myComponentData">
放置循环
您的 componentData 包含控件的类型,因此您将拥有另一个服务,该服务会根据请求的类型返回正确的类型。
当您开始使用解析器时,您的输入/输出不会被分配。所以你必须自己处理。
ngOnChanges(changes: SimpleChanges) {
let propertyWatch = this.getPropertyWatch();
for (let change in changes) {
if (change === propertyWatch) {
let data = this.getComponentData(changes[change].currentValue);
if (data) {
if (data.inputs) {
this.assignInputs(data.inputs);
}
if (data.outputs) {
this.assignOutputs(data.outputs);
}
if (this.implementsOnChanges()) {
let dynamiChanges = DynamicChanges.create(data);
if (dynamiChanges) {
(<OnChanges><any>this._currentComponent).ngOnChanges(dynamiChanges);
}
}
}
}
}
}
private unassignVariables() {
if (this.factory && this.factory.inputs) {
for (let d of this.factory.inputs) {
this._currentComponent[d.propName] = null;
}
}
}
protected assignInputs(inputs: ComponentInput) {
for (let key in inputs) {
if (inputs[key] !== undefined) {
this._currentComponent[key] = inputs[key];
}
}
}
private assignOutputs(outputs: ComponentOutput) {
for (let key in outputs) {
if (outputs[key] !== undefined) {
let eventEmitter: EventEmitter<any> = this._currentComponent[key];
let subscription = eventEmitter.subscribe(m => outputs[key](m));
this.sm.add(subscription);
}
}
}
然后,我发现用 formControl
处理我的表单输入会更好而不是 ngModel
.特别是在处理验证器时。如果你保持 ngModel
,您将无法轻松添加/删除验证器。但是,使用 [formControl]
创建动态组件附加到使用 ComponentFactoryResolver
生成的控件似乎是不可能的。所以我不得不即时编译模板。所以我用另一个服务创建了一个控件,如下所示:
const COMPONENT_NAME = 'component';
@Injectable()
export class RuntimeComponent {
constructor(
private compiler: Compiler,
@Optional() @Inject(DEFAULT_IMPORTS_TOKEN)
protected defaultImports: DefaultImports
) {
}
protected createNewComponent(tmpl: string, args: any[]): Type<any> {
@Component({
selector: 'tn-runtime-component',
template: tmpl,
})
class CustomDynamicComponent<T> implements AfterViewInit, DynamicComponentData<T> {
@ViewChild(COMPONENT_NAME)
component: T;
constructor(
private cd: ChangeDetectorRef
) { }
ngAfterViewInit() {
this.cd.detectChanges();
}
}
Object.defineProperty(CustomDynamicComponent.prototype, 'args', {
get: function () {
return args;
}
});
// a component for this particular template
return CustomDynamicComponent;
}
protected createComponentModule(componentType: any) {
let imports = [
CommonModule,
FormsModule,
ReactiveFormsModule
];
if (this.defaultImports && this.defaultImports.imports) {
imports.push(...this.defaultImports.imports);
}
@NgModule({
imports: imports,
declarations: [
componentType
],
})
class RuntimeComponentModule {
}
// a module for just this Type
return RuntimeComponentModule;
}
public createComponentFactoryFromStringSync(template: string, attributeValues?: any[]) {
let type = this.createNewComponent(template, attributeValues);
let module = this.createComponentModule(type);
let mwcf = this.compiler.compileModuleAndAllComponentsSync(module);
return mwcf.componentFactories.find(m => m.componentType === type);
}
public createComponentFactoryFromMetadataSync(selector: string, attributes: { [attribute: string]: any }) {
let keys = Object.keys(attributes);
let attributeValues = Object.values(attributes);
let attributeString = keys.map((attribute, index) => {
let isValueAFunctionAsString = typeof attributeValues[index] === 'function' ? '($event)' : '';
return `${attribute}="args[${index}]${isValueAFunctionAsString}"`;
}).join(' ');
let template = `<${selector} #${COMPONENT_NAME} ${attributeString}></${selector}>`;
return this.createComponentFactoryFromStringSync(template, attributeValues);
}
}
我的代码并不完美,这就是为什么我只给你重要的部分。你必须玩弄这个想法并让它按照你的方式发挥作用。有一天我应该写一篇关于这个的博文:)
我查看了您的 plunker,发现您在使用 ngFor 时没有正确使用#name。如果您的循环正常工作,您将无法在 TypeScript 中正确获取它。
另外,你不能做 *ngFor=""
在 ng 模板上。所以你的循环不工作。参见 https://toddmotto.com/angular-ngfor-template-element
祝你好运!
关于 Angular 4 : ComponentFactoryResolver - architecture advice,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49607603/
我有一个组件,使用 ComponentFactoryResolver 创建另一个组件。它似乎工作正常,我可以通过@Input 访问数据。问题是 ngOnChanges 在组件启动或数据更改时永远不会被
我正在编写一个列出一些用户并在单击用户时显示信息页面的应用程序。用户类型不同(约 20 种),详细信息页面在布局和功能上因用户类型而异。 我有一个主 DetailsComponent 和 20 个必须
我需要一些关于 ComponentFactoryResolver 和架构的建议。我的代码中有 n 个面板(后端提供面板的数量),在这些面板中,我有动态字段(数量也由后端提供)。例如,每个面板的开头应该
我尝试使用 ComponentFactoryResolver 来创建动态 Angular 组件。下面是我注入(inject) ComponentFactoryResolver 的代码。 import
我从 NG4 -> NG6 重写库(使用 angular-cli 6)。这个 lib 改变了 dynamicli angular 组件,所以我使用 ComponentFactoryResolver 在
我想创建一个动态加载不同组件的 Angular 7 Web 应用程序,如本官方文档中所示: https://angular.io/guide/dynamic-component-loader 但我不确
是否有一种方法可用于在动态创建的 Angular 2 组件上定义 @Input 属性? 我正在使用 ComponentFactoryResolver在容器组件中创建组件。例如: let compone
正如您所知,在早期版本的 Angular 中,可以在 ComponentFactoryResolver 的帮助下创建动态组件。作为 export class DialogService { dia
我想使用 ComponentFactoryResolver 动态创建组件,我已阅读有关如何实现此目的的教程(例如 this),但看不到它与 Material 组件有何关系。 示例 - 动态呈现 md-
我有一个组件,它呈现 DOM,它应该在 svg 标签内: import { Component, Input } from '@angular/core'; @Component({ select
我使用 ComponentFactoryResolver 访问所有入口组件工厂,然后在 Angular 7 中动态添加这些组件的路由。 constructor(private componentFac
我现在有点迷茫,需要帮助。所以,我有一个组件使用 componentFactoryResolver 将子组件动态注入(inject)到它的模板中,这是我的 HTML Back
在我的 Angular 10 应用程序中,我正在通过组件工厂解析器加载一个包含 AG 网格的组件。 当我通过按钮触发工厂解析器时,一切正常。显示网格和 gridReady事件被正确触发。 但是,如果我
我是一名优秀的程序员,十分优秀!