在Jquery插件模板内绑定Angular2组件

4

我正在将Kendo用于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>`
    }
}];

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来实现了这个目标,但感觉不太对。

2个回答

1

我在这个讨论中找到了我需要的一些细节: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 );

0

直到组件需要改变其状态并重新渲染一些内容,您的解决方案都很好用。 因为我还没有找到任何获取 ViewContainerRef 的能力,以用于在 Angular 之外生成的元素(jQuery、vanilla js 或甚至服务器端) 第一个想法是通过设置间隔调用 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();
    });

在经过反复的stackoverflow搜索之后更新

忘记上面的所有话。它可以工作,但只需按照这个answer


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接