I am migrating my Web application from AngularJS 1.5.3 to Angular 14.2 , there is a usage of popover template in my application, See below
html
<a id="{{mapObj.stId}}" popover-template="'{{mapObj.statusTemplate}}'"
popover-trigger="outsideClick" popover-classes="editable-container editable-popup"
popover-title="{{mapObj.statusTitle}}" ng-click="onStatusClick(mapObj)"
style="color: rgb(128, 128, 128);cursor:pointer"
tabindex="0">
<span><b>Accept, Decline, or Reduce total </b></span>
</a>
<script type="text/ng-template" id="{{mapObj.stId}}status.html">
<div>
<form >
<div >
.....
.....
</div>
</form>
</div>
</script>
Controller.js
onStatusClick= function (mapObj) {
if (!mapObj.isHidePopover) {
mapObj.statusTemplate = mapObj.stId "status.html";
mapObj.statusTitle = "Accept, Decline, or Request Reduction";
}
mapObj.isHidePopover = false;
};
I need help in writing the same code in Angular 14.2 without using any third party ( I am using bootstrap with angular, which is fine). Any suggestion/help on this?
CodePudding user response:
Angular 2 (or 14.2 in your case) is completely different to Angular JS. So a simple conversion can be very hard, as you must have noticed. There are some libraries which can help you to create a popover very easy, like Bootstrap (ng-bootstrap
). But if you wanna do it with the pure Angular 2 way to learn how it works then here it is! (Stackblitz)
I think for a popover Angulars Directive
is the way to go. A Directive
can manipulate its host component appearance or behavior. Read all about it here.
The follow Directive
does the follow: OnInit loads the ng-template
and show it on bottom to its parent. Some click listener handle backdrop clicks and/or the popovers state itself.
@Directive({
selector: "[popoverTrigger]"
})
export class PopoverDirective implements OnDestroy, OnInit {
@Input()
popoverTrigger!: TemplateRef<object>;
@Input()
closeOnClickOutside: boolean = false;
private unsubscribe = new Subject();
private overlayRef!: OverlayRef;
constructor(
private elementRef: ElementRef,
private overlay: Overlay,
private vcr: ViewContainerRef,
private popoverService: PopoverService
) {}
ngOnInit(): void {
this.createOverlay();
this.popoverService.getState().subscribe(resp => {
if (resp) {
this.detachOverlay();
}
});
}
ngOnDestroy(): void {
this.detachOverlay();
this.unsubscribe.next();
this.unsubscribe.complete();
}
@HostListener("click") clickou() {
this.attachOverlay();
}
private createOverlay(): void {
const scrollStrategy = this.overlay.scrollStrategies.block();
const positionStrategy = this.overlay.position().connectedTo(
this.elementRef,
{ originX: "start", originY: "bottom" },
{ overlayX: "start", overlayY: "top" }
//ToDo entender como funciona o posicionamento
// new ConnectionPositionPair(
// { originX: "start", originY: "bottom" },
// { overlayX: "start", overlayY: "bottom" }
// ),
// new ConnectionPositionPair(
// { originX: "start", originY: "bottom" },
// { overlayX: "start", overlayY: "bottom" }
// )
);
this.overlayRef = this.overlay.create({
positionStrategy,
scrollStrategy,
hasBackdrop: true,
backdropClass: ""
});
this.overlayRef
.backdropClick()
.pipe(takeUntil(this.unsubscribe))
.subscribe(() => {
if (this.closeOnClickOutside) {
this.detachOverlay();
}
});
}
private attachOverlay(): void {
if (!this.overlayRef.hasAttached()) {
const periodSelectorPortal = new TemplatePortal(
this.popoverTrigger,
this.vcr
);
this.overlayRef.attach(periodSelectorPortal);
}
}
private detachOverlay(): void {
if (this.overlayRef.hasAttached()) {
this.overlayRef.detach();
}
}
}
So.. a lot of code. But write only once and use it for all components you want. Now in your component, like app.component.ts, you need to set this in the html:
<p [popoverTrigger]="popoverVazio">
Popover no texto
</p>
<ng-template #popoverVazio>
<popover-container>
<p>exemplo de popovere acionado por um texto</p>
<button mat-raised-button color="primary" (click)="closePopover()">
Fechar
</button>
</popover-container>
</ng-template>
That's all. The ng-template
is not shown. Only if the popup open. A ng-template
, by the way, is also always not shown by default. If you set ngIf="true"
to it (as example) then it will be visible. More about it here.