我正在angular 2项目中使用剑道。
正确设置小部件没有问题:
ngOnInit() {
let options = inputsToOptionObject(KendoUIScheduler, this);
options.dataBound = this.bound;
this.scheduler = $(this.element.nativeElement)
.kendoScheduler(options)
.data('kendoScheduler');
}
当它运行时,插件会修改DOM(据我所知,在不修改angular2维护的阴影DOM的情况下)。我的问题是,如果我想在插件内部的任何地方使用组件,比如在模板中,Angular都不知道它的存在,也不会绑定它
示例:
public views:kendo.ui.SchedulerView[] = [{
type: 'month',
title: 'test',
dayTemplate: (x:any) => {
let date = x.date.getDate();
let count = this.data[date];
return `<monthly-scheduler-day [date]="test" [count]=${count}"></monthly-scheduler-day>`
}
}];
每月计划日课程:
@Component({
selector: 'monthly-scheduler-day',
template: `
<div>{{date}}</div>
<div class="badge" (click)=dayClick($event)>Available</div>
`
})
export class MonthlySchedulerDayComponent implements OnInit{
@Input() date: number;
@Input() count: number;
constructor() {
console.log('constructed');
}
ngOnInit(){
console.log('created');
}
dayClick(event){
console.log('clicked a day');
}
}
有没有一种"正确"的方法将这些组件绑定到小部件创建的标记中?我通过监听小部件中的绑定事件,然后在它创建的元素上循环,并使用DynamicComponentLoader实现了这一点,但感觉不对。
我在这个线程中找到了一些需要的细节:https://github.com/angular/angular/issues/6223
我启动了这项服务来处理绑定我的组件:
import { Injectable, ComponentMetadata, ViewContainerRef, ComponentResolver, ComponentRef, Injector } from '@angular/core';
declare var $:JQueryStatic;
@Injectable()
export class JQueryBinder {
constructor(
private resolver: ComponentResolver,
private injector: Injector
){}
public bindAll(
componentType: any,
contextParser:(html:string)=>{},
componentInitializer:(c: ComponentRef<any>, context: {})=>void):
void
{
let selector = Reflect.getMetadata('annotations', componentType).find((a:any) => {
return a instanceof ComponentMetadata
}).selector;
this.resolver.resolveComponent(componentType).then((factory)=> {
$(selector).each((i,e) => {
let context = contextParser($(e).html());
let c = factory.create(this.injector, null, e);
componentInitializer(c, context);
c.changeDetectorRef.detectChanges();
c.onDestroy(()=>{
c.changeDetectorRef.detach();
})
});
});
}
}
参数:
- componentType:要绑定的组件类。它使用反射来拉动所需的选择器
- contextParser:回调,它获取现有的子html并构造上下文对象(初始化组件状态所需的任何东西)
- componentInitializer-回调,使用您解析的上下文初始化创建的组件
示例用法:
let parser = (html: string) => {
return {
date: parseInt(html)
};
};
let initer = (c: ComponentRef<GridCellComponent>, context: { date: number })=>{
let d = context.date;
c.instance.count = this.data[d];
c.instance.date = d;
}
this.binder.bindAll(GridCellComponent, parser, initer );
在组件需要更改状态并重新提交一些内容之前,您的解决方案运行良好。因为我还没有发现任何能力为Angular之外生成的元素(jquery、vanilla-js甚至服务器端)获取ViewContainerRef第一个想法是通过设置一个间隔来调用detectChanges()。经过几次迭代,我终于找到了一个适合我的解决方案
到目前为止,在2017年,你必须用ComponentResolverFactory取代ComponentResolver,并做几乎相同的事情:
let componentFactory = this.factoryResolver.resolveComponentFactory(componentType),
componentRef = componentFactory.create(this.injector, null, selectorOrNode);
componentRef.changeDetectorRef.detectChanges();
之后,您可以通过订阅其NgZone:的EventEmitters来模拟将组件实例附加到更改检测周期
let enumerateProperties = obj => Object.keys(obj).map(key => obj[key]),
properties = enumerateProperties(injector.get(NgZone))
.filter(p => p instanceof EventEmitter);
let subscriptions = Observable.merge(...properties)
.subscribe(_ => changeDetectorRef.detectChanges());
当然,不要忘记在销毁时取消订阅:
componentRef.onDestroy(_ => {
subscriptions.forEach(x => x.unsubscribe());
componentRef.changeDetectorRef.detach();
});
再次堆叠后的UPD
忘记上面所有的话。它可以工作,但只需遵循这个答案