What is the best way to retrieve the dimensions of an Angular 5 Component prior to displaying it on the screen?

I am currently working on integrating a generic tooltip feature in Angular 5. In order to ensure proper positioning, especially centering relative to the target element, I need to obtain the width and height of the tooltip before it is rendered.

While I have already discovered how to acquire the dimensions of a component from this particular question, my challenge lies in setting the tooltip content first (allowing the browser to determine the size) for position calculation. Subsequently, I aim to set the position and apply a class for display.

Below is an excerpt of the code I am working with:

// tooltip.component.html
<div class="popup fade"
  [ngClass]="tooltip?.isOpen ? 'show' : ''"
  [style.left.px]="calculatedPos?.left"
  [style.top.px]="calculatedPos?.top">
  {{ tooltip?.text }}
</div>

// tooltip.component.ts
export class TooltipComponent {
  public tooltip: any;
  public calculatedPos: TooltipPosition = {
    left: 0,
    top: 0
  }

  constructor(private store: Store<any>, private ref: ElementRef) {
    this.store.subscribe((state) => this.render(state));
  }

  render () {
    let targetRect = this.tooltip.targetEl 
          && this.tooltip.targetEl.nativeElement
          && this.tooltip.targetEl.nativeElement.getBoundingClientRect()
          || null;
    let tooltipRect = this.ref 
          && this.ref.nativeElement
          && this.ref.nativeElement.getBoundingClientRect()
          || null;
    if (targetRect && tooltipRect) { // <-- tooltipRect.height /.width is zero here!
      switch (this.tooltip.placement) {
        case 'right':
          this.calculatedPos = this.calculatePositionRight(targetRect, tooltipRect, this.tooltip.offset)
          break;
          // and so on...
  }
}

I have experimented with using lifecycle hooks as follows:

  • ngAfterContentChecked resulted in the same outcome
  • ngAfterViewChecked produced an expected
    ExpressionChangedAfterItHasBeenCheckedError
  • ngOnChanges did not trigger when rendering

Hence, I pose the following question: How can I obtain the dimensions of the Component before the content is rendered? Or alternatively, how can I detect the content being rendered and then update the position accordingly?

My initial consideration was to use a timeout to allow the component to render before setting the position (without the user noticing), but I am curious to explore any best practices that may exist.

Answer №1

In order to avoid the issue of

ExpressionChangedAfterItHasBeenCheckedError
, it seems necessary to set up the subscription within the ngAfterViewInit lifecycle hook. By injecting the ChangeDetectorRef and manually invoking detectChanges at the end of your render-Method, you are essentially informing Angular that you understand the consequences of your actions.

Answer №2

One must wait for an element to be rendered in order to obtain its dimensions.

The dimensions of an element cannot be obtained if it is not present in the DOM.

Utilize ngAfterViewInit to ensure that the view is initialized before obtaining dimensions.

Answer №3

In my code, I initiate a process in ngAfterViewInit and it gets triggered again whenever the resize event occurs. Rather than setting it up in the constructor, I opt to reference a wrapper element in the template.

  <div #componentElement>
    ... content goes here ...
  </div>

Then, in the TypeScript file:

  @ViewChild("componentElement") componentElementRef: ElementRef


  ngAfterViewInit(): void {
    this.render()
  }

  @HostListener("window:resize")
  onResize(): void {
    this.render()
  }

  render () {
    console.log(this.componentElementRef.nativeElement.getBoundingClientRect())
    ... insert your custom code here ...
  }

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Transforming XML into Json using HTML string information in angular 8

I am currently facing a challenge with converting an XML document to JSON. The issue arises when some of the string fields within the XML contain HTML tags. Here is how the original XML looks: <title> <html> <p>test</p> ...

Create a pipeable stream that does not trigger any events when data is piped

I have been trying to utilize the renderToPipeableStream function from React18, and although it is functional, I am struggling with handling the pipe properly. The key section of my code involves an array of strings representing HTML. I am splitting the s ...

Typescript allows for the creation of a static class that can be accessed without needing to instantiate it

My goal is to design a TypeScript class that can be accessed directly without the need to create new instances. This is necessary because some classes will modify the variables in this shared class, while others must reference those changes. Below is the ...

Changing Angular component styles dynamically

I am currently working on a new project using the latest version of Angular 5.0 and as a newcomer to this framework, I could really use some guidance from experienced Angular developers. My goal is to dynamically set the styleUrls property with a class-le ...

The component from the service experienced an error due to the absence of an argument for 'payload'

I am encountering an issue with a form that is sending data to a web API. I have a service that is responsible for handling this process and it is injected into the form component in the onSubmit function. The error message "An argument for 'payload&a ...

Encountering a 403 error when attempting to upload files to Google Cloud Storage (GCS) using Signed URLs

The main aim is to create a signed URL in the api/fileupload.js file for uploading the file to GCS. Then, retrieve the signed URL from the Nextjs server through the nextjs API at localhost://3000/api/fileupload. Finally, use the generated signed URL to upl ...

Trouble Loading TypeScript Class in Cast Situation

I've encountered an issue with my TypeScript model while using it in a cast. The model does not load properly when the application is running, preventing me from accessing any functions within it. Model export class DataIDElement extends HTMLElement ...

Transform the process.env into <any> type using TypeScript

Need help with handling logging statements: log.info('docker.r2g run routine is waiting for exit signal from the user. The container id is:', chalk.bold(process.env.r2g_container_id)); log.info('to inspect the container, use:', chalk.b ...

Having trouble accessing a variable from the material theme in Angular 7

Currently, I am working with Angular 7.0.3 and endeavoring to establish an scss variable with the primary color of my material theme. // src/styles/_variables.scss @import "~@angular/material/theming"; @include mat-core(); $app-primary: mat-palette($mat-i ...

What are the steps for creating a custom repository with TypeORM (MongoDB) in NestJS?

One query that arises is regarding the @EntityRepository decorator becoming deprecated in typeorm@^0.3.6. What is now the recommended or TypeScript-friendly approach to creating a custom repository for an entity in NestJS? Previously, a custom repository w ...

Utilizing Angular to Fetch JSON Data from a Server

I am currently retrieving data from a JSON file that resides on a server and is updated regularly. It's crucial for me to access this JSON content in order to consistently showcase the most recent information on my website. At present, I have stored ...

What is the reason behind angular 4.3's httpclient returning Object instead of any?

The Angular 4.3 update introduces the new HttpClient class, which appears to return Object instead of any as the default type. Is there a specific reason for this decision, especially considering the TypeScript documentation advises against using types li ...

Display the content of an md-dialog with a scroll bar

I am experiencing a problem with printing a long report created using md-list displayed in a md-dialog. When I attempt to print it, only the section that is currently visible on the screen gets printed instead of the entire length of the md-list. I have at ...

I need a way to call a function in my Typescript code that will update the total value

I am trying to update my total automatically when the quantity or price changes, but so far nothing is happening. The products in question are as follows: this.products = this.ps.getProduct(); this.form= this.fb.group({ 'total': ...

The current issue I am facing is that the option disabled with value="null" selected is not being shown on the screen. Instead, it should display as "Choose User Types"

https://i.sstatic.net/JqZ7O.png <select class="form-control" id="ddSelectaTopic" onchange="if(this.value==='') {this.style.color='#999'} else {this.style.color='#333'}" [(ngModel)]="us ...

Angular users should be cautious of the 'grid zero width' warning that may arise when employing ag-Grid's sizeColumnsToFit() on multiple ag-Grids simultaneously

I'm encountering an issue with ag-grid where I see the following warning in the console. Despite conducting some research, none of the solutions I found have resolved my problem. It appears that there may be a memory leak within my application based o ...

Using the Angular KeyValue pipe in a Material table to showcase arrays of objects

I have a response object that contains various data for different categories: { NET_TIME: [ { createdBy: 'Alex', from: '9.0', to: '8.0' }, { createdB ...

Issue with interceptor functionality in Angular version 18.0.5 causing it to not work properly

There seems to be a bug with the standalone component and HttpInterceptor implementation! export const appConfig: ApplicationConfig = { providers: [ provideHttpClient(withInterceptors([interceptorFN])), provideRouter(routes), provideAnimation ...

The hide function of the NgxSpinner does not seem to be working properly

Lately I've been incorporating NgxSpinner into my projects, but I've encountered a peculiar issue with the spinner.hide() function. I want to trigger this function within the subscribe complete function because that's the ideal moment for it ...

Detecting changes in Angular from the parent to the child component can be accomplished by utilizing a specific approach

The Sample Component contains data in the form of an array of objects with child components within a loop. Sample.component export class SampleComponent implements OnInit { data = [{ value: 3 }, { value: 1 }]; constructor() {} ngOnInit(): void {} ...