4

我正在使用 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 来做到这一点,但感觉不对。

4

2 回答 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 );
于 2016-08-01T18:56:44.170 回答
0

好吧,您的解决方案可以正常工作,直到组件需要更改其状态并重新渲染一些东西。因为我还没有发现任何能力为在 Angular(jquery、vanilla js 甚至服务器端)之外生成的元素获取 ViewContainerRef,所以第一个想法是通过设置间隔来调用 detectChanges()。经过几次迭代后,我终于找到了一个适合我的解决方案。

到目前为止,在 2017 年,您必须将 ComponentResolver 替换为 ComponentResolverFactory 并执行几乎相同的操作:

    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后的UPD

忘记上面所有的话。它有效,但只需遵循这个答案

于 2017-08-30T20:55:40.647 回答