Reputation: 1253
I am working on a date-picker component and would like the date-picker popup to be a child of the body element. The date-picker component currently renders an input element and displays a popup that is a sibling to the input when the input is focused. Because this component is used in many places css rules of parent elements have cause many problems. We've had problems with overflow, problems with animations, problems with transformations. I would like to move the popup to be a child of the body element to avoid these problems. However, to keep the component easy to use, I don't want to create a separate date-picker-popup component that the needs to be included elsewhere in the app.
Can I do something along the lines of: create a dom element, append it to the body, then render an angular component as a child of that dom element?
Upvotes: 1
Views: 966
Reputation: 105439
Can I do something along the lines of: create a dom element, append it to the body, then render an angular component as a child of that dom element?
Yes, you can do that. You need to make popup component dynamic and pass a DOM element when creating it. You also need to attach the resulting view to ApplicationRef.views. This approach is similar to what @angular/material CDK is using.
Here is the stackblitz demo and the code:
import { Component, ComponentFactoryResolver, Injector, Inject, Renderer2, ApplicationRef } from '@angular/core';
import { PopupComponent } from './popup.component';
import { DOCUMENT } from '@angular/platform-browser';
@Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: [ './app.component.css' ]
})
export class AppComponent {
name = 'Angular 4';
constructor(
resolver: ComponentFactoryResolver,
injector: Injector,
@Inject(DOCUMENT) document,
renderer: Renderer2,
appRef: ApplicationRef) {
setTimeout(()=>{
const element = renderer.createElement('div');
renderer.appendChild(document.body, element);
const factory = resolver.resolveComponentFactory(PopupComponent);
const compRef = factory.create(injector, [], element);
appRef.attachView(compRef.hostView);
})
}
}
To learn more about dynamic components read:
Upvotes: 2
Reputation: 196
I think that the best solution is to include your date-picker-popup component into your parent component. You add a boolean property to your parent, show-popup
, and you pass this property to the date-picker-popup component as an @Input
: [show-popup]="show-popup"
.
In your parent you implement two functions, the first one on the focus event to set show-popup = true
, and the second one on the focusout event to set show-popup = false
.
And depending on the value of this property, you change css style to show/hide the date-picker-popup.
Upvotes: 0
Reputation: 2862
If you absolutely need it to be a child of body, and specifically body, then I think Angular's Renderer2 API is where you should look. I don't have much experience with it.
Otherwise, if you have a parent component of the entire project, like app.component, why not just house the date-picker component there? Use event-emitters to toggle from child components to the app component (parent), show the calendar, send the results/data to a shared service.
Upvotes: 0