Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Moving a component to another parent component in Angular

Tags:

This question is the Angular version of this question.

The following snippet summarizes my first attempt:

class NewParentComponent {

  constructor(elRef: ElementRef) {
    elRef.nativeElement.appendChild(myMovableElement);
    // where the reference to myMovableElement may come from a service.
  }
}

This attempt comes with the following issues:

  1. We are not supposed to manipulate the DOM directly in Angular. Is moving a component achievable with the Renderer somehow? (Ans: Per cgTag's answer, use Renderer2).
  2. If the original parent component gets destroyed after the child component was moved, the ngOnDestroy method is called on the child component.

See this Plunker demonstrating the issue. This Plunker is built on top of the Angular Dynamic Component Loader example. Open the browser console log and note that the "destroyed!" text is logged every time the ad banner changes, even when the "Drag Me!" text was dragged into the drop box.

like image 743
Sam Herrmann Avatar asked Jul 13 '17 01:07

Sam Herrmann


People also ask

What is the use of @input and @output in Angular?

@Input() and @Output() give a child component a way to communicate with its parent component. @Input() lets a parent component update data in the child component. Conversely, @Output() lets the child send data to a parent component.

Can we import one component to another component in Angular?

Yes , It possible to include child component by using selector as class name. This way will require some changes in selector metadata of the component.


2 Answers

Moving a child component from one parent component to another can be achieved using ViewContainerRef:

oldParentViewContainerRef.detach(index);
newParentViewContainerRef.insert(viewRef);

The index is the index of the child component's view within the view-container. That can be obtained using the following:

let index = oldParentViewContainerRef.indexOf(viewRef);

The viewRef corresponds to the view of the child component. You can only get a hold of viewRef, if the child component was created programmatically with the help of ComponentFactoryResolver (see edit below for Angular v13):

let factory = componentFactoryResolver.resolveComponentFactory(MyChildComponentType);
let componentRef = oldParentViewContainerRef.createComponent(factory);
let viewRef = componentRef.hostView;

The takeaway here is, if you want to be able to move components between different parent components, make sure you create those movable components programmatically, not statically in the HTML. This gives you a ViewRef instance of those movable components, which works nicely together with ViewContainerRef.

See working Plunker for an example.

Edit: Angular v13

As of Angular v13, ComponentFactoryResolver is no longer needed to create components programmatically. Components can now be created as follows:

let componentRef = oldParentViewContainerRef.createComponent(MyChildComponentType);
let viewRef = componentRef.hostView;
like image 120
Sam Herrmann Avatar answered Sep 22 '22 14:09

Sam Herrmann


Yes, you can do this with the Renderer2 class and it is the preferred way. The reason for using the class is to keep your application compatible with Angular Universal.

Angular is not coupled to the DOM tree like it is in AngularJS. Angular now creates ViewRef objects which handle the coupling between a component and the DOM. This means that the native element used by the ViewRef can be moved around without breaking the change detention tree or the injection tree.

While the Renderer2 class is limited in API methods. It does have the methods you need.

abstract appendChild(parent: any, newChild: any): void;
abstract insertBefore(parent: any, newChild: any, refChild: any): void;
abstract removeChild(parent: any, oldChild: any): void;

I wrote a UI library that has windows as components. Those windows can dock into parent window panels. The only way I could do it was to move the DOM element for the component.

My only concern is the life-cycle for the component. When my windows destroy they move those elements back to where they were. I don't know if ngOnDestroy will be called if the element is removed from the DOM by a different parent.

like image 25
Reactgular Avatar answered Sep 21 '22 14:09

Reactgular