Home > Enterprise >  Migrating Angularjs popover to Angular 14
Migrating Angularjs popover to Angular 14

Time:01-23

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.

  • Related