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:
Renderer
somehow? (Ans: Per cgTag's answer, use Renderer2).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.
@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.
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.
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.
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;
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With