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

时间:2016-07-27 21:19:43

标签: angularjs angular kendo-ui angular2-directives angular2-components

我正在使用角度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>`
    }
}];

每月调度日课程:

@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');
    }

}

是否有&#34;权利&#34;如何在窗口小部件创建的标记内绑定这些组件?我设法通过从窗口小部件中侦听绑定事件然后循环遍历它创建的元素并使用DynamicComponentLoader来实现它,但感觉不对。

2 个答案:

答案 0 :(得分: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();
                })
            });
        });        
    }
}

PARAMS:

  • 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 );

答案 1 :(得分:0)

Well your solution works fine until the component needs to change its state and rerender some stuff. Because I haven't found yet any ability to get ViewContainerRef for an element generated outside of Angular (jquery, vanilla js or even server-side) the first idea was to call detectChanges() by setting up an interval. And after several iterations finally I came to a solution which works for me.

So far in 2017 you have to replace ComponentResolver with ComponentResolverFactory and do almost the same things:

var svg = document.querySelector('svg')
var h1 = document.querySelector('h1')

console.log('svg', svg.offsetTop)
console.log('h1', h1.offsetTop)

After that you can emulate attaching component instance to the change detection cycle by subscribing to EventEmitters of its NgZone:

<svg width="26" height="30" viewBox="0 0 12 14">
  <path d="M6 4.038L12 0v3.623L6 8 0 3.623V0l6 4.038zm0 6L12 6v3.623L6 14 0 9.623V6l6 4.038z"/>
</svg>
    
<h1>This works</h1>

Of course don't forget to unsubscribe on destroy:

decimal? taskId = dbContext.My_Tasks.Max(m=> m.TaskId)

UPD after stackoverflowing once more

Forget all the words above. It works but just follow this answer