Get ComponentRef from DOM element

2019-02-13 08:38发布

It has already been answered how to get the DOM element from an Angular 2 component: ComponentRef.location.nativeElement (ComponentRef.location gives the ElementRef that gives a direct access to DOM).

But how to do the opposite, i.e. get the reference to the ComponentRef when I only have the native DOM object?

I am in the case when I try to drag/drop Angular 2 components using interact.js. The library uses callback functions to notify which element is dragged, and on which element we are trying to drop. An event object is provided, and the only useful information I found was the DOM element (target attribute).

Example:

interact('my-component-tag').draggable({
    // ...
    onstart: function (event:any) {
        var dom = event.target; // ref to the <my-component-tag> tag
        // How to find the Angular ComponentRef object here?
    }
    // ...
}).dropzone({
    // ...
    ondragenter: function (event:any) {
        var targetDom = event.relatedTarget; // targeted <my-component-tag> tag
        // Same question here,
        // to identify where we want to insert the dragged element.
    }
    // ...
});

Plunker here

You can check the handlers in src/Interactjs.ts. Open the console to see associated logs and drop a component on another. I have information about elements by the DOM, but I want Angular components instead, let's say access the count attribute.

Findings and tries:

I found a solution for the jquery-ui-draggable plugin, but this trick does not work here, at least for the target where to drop.

There are also topics about how to insert in the DOM, talking about the DomAdapter, but I haven't found any method that seems to help from DOM to Angular component reference.

I just thought about a manual search over my components: looping in the DOM nodes, count to find the position, and reach the component from the component list at the same position, but it is so ugly...

Any advice on that is welcome. Thanks for reading!

2条回答
地球回转人心会变
2楼-- · 2019-02-13 09:21

This could be achieved using the ElementProbe API. It is mainly intended for debugging / protractor integration, similar to element.scope() in Angular 1.

In order to use this API, you would need to include ELEMENT_PROBE_PROVIDERS in your bootstrap() call. You will then be able to get any component instance by calling the global ng.probe().

For example, here is how you get the component instance for the current event target:

ng.probe(event.target).componentInstance

Updated Plunker showing this is action

You can see the actual implementation of the ElementProbe API Here.

查看更多
我欲成王,谁敢阻挡
3楼-- · 2019-02-13 09:22

I just had to do this. I was able to accomplish it by using a combination of @ViewChildren in the "parent" component class and setting a unique identifier on the "child" component itself.

So, it the parent component, you have:

@ViewChildren(YourChildComponent)
elements: QueryList<YourChildComponent>;

Then, in your child component, you do the following (note that I imported the angular2 uuid package, but you can use any mechanism for generating a unique id):

constructor(private elementRef:ElementRef) {    
    this.UniqueID = UUID.UUID();
    this.elementRef.nativeElement.setAttribute('unique-id', this.UniqueID);
}

 //and we define a public property here
UniqueID: string;

Now, you can access your children and match them up by identifier like this:

private findElementByNativeDOMObject(el: any) : YourChildComponent{


let uniqueId = el.attributes.getNamedItem('unique-id').value;
if ( ! uniqueId)
  return null;

// ok, let's go through our children
return this.elements.find( x=> x.UniqueID == uniqueId);

}

查看更多
登录 后发表回答