在 Jquery 插件模板中绑定 Angular2 组件
Binding Angular2 components inside of a Jquery plugin template
我正在 angular 2 项目中使用 kendo。
正确设置小部件没问题:
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>`
}
}];
每月调度程序日class:
@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');
}
}
是否有 "right" 方法将这些组件绑定到小部件创建的标记内?我设法通过侦听来自小部件的绑定事件然后遍历它创建的元素并使用 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:您要绑定的组件class。它使用反射来拉取它需要的选择器
- 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();
});
Whosebug 后的更新
忘记上面的所有单词。它有效,但只需遵循此
我正在 angular 2 项目中使用 kendo。
正确设置小部件没问题:
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>`
}
}];
每月调度程序日class:
@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');
}
}
是否有 "right" 方法将这些组件绑定到小部件创建的标记内?我设法通过侦听来自小部件的绑定事件然后遍历它创建的元素并使用 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:您要绑定的组件class。它使用反射来拉取它需要的选择器
- 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();
});
Whosebug 后的更新
忘记上面的所有单词。它有效,但只需遵循此