1

I'm developing an application with Angular.

I have created a page component that consists of several subcomponents. I set up each subcomponent to get data via an api call in ngOnInit and then update the screen to match the data.

The problem is that the layout shift is exposed to the user. Different components take different amounts of time to make the API calls, the data written to the component is not of a fixed length, so the height of the components is not fixed, resulting in a large layout shift, which is visible to the user in the process. (After rendering component A, the position of B and C changes)

I tried to fix this by using ngAfterViewInit or other angular lifecycle methods, but even if I adjust the template rendering with ngIf, hidden, etc, the layout change is still visible to the user.

The only way I've found is to use setTimeout to hide the screen for the duration of the layout change.

However, this seems like a bad idea. Is there any good way to solve this problem except applying skeleton UI, using setTimeout, and emitting events?

https://stackblitz.com/edit/angular-parent-to-child-communication-9ribd2?file=src%2Fapp%2Fapp.module.ts

This is an example created with the same configuration as my project. You can see the rendering process for each list in their project as well. (Exposing the process of updating templates on the Defaults screen)

I really want to get this fixed. Any advice would be greatly appreciated

haaaam
  • 31
  • 1
  • Does this answer your question? [How to call function after dom renders in Angular2?](https://stackoverflow.com/questions/31171084/how-to-call-function-after-dom-renders-in-angular2) – Get Off My Lawn Apr 17 '23 at 14:58
  • no I also tried using ngAfterViewChecked, but it didn't give me the desired result. @GetOffMyLawn – haaaam Apr 17 '23 at 15:18

2 Answers2

1

One way to do it is this…

Create a property on your components something like dataReady: boolean = false; and then use *ngIf="dataReady" on the main HTML element of your components.

Once the data is loaded, set dataReady to true. This way your components won’t be displayed until the process is complete.

1

The layout shift issue you are experiencing is a common problem in web development when rendering dynamic content. The solution to this problem is to ensure that the height of the container for each subcomponent is fixed and to use a loading indicator while the data is being fetched from the API.

One solution to this problem is to use the CSS Flexbox layout. You can use the CSS display: flex property on the parent container and set the flex-direction property to column to stack the subcomponents vertically. Then you can set the flex-grow property on each subcomponent to 1 to ensure that each subcomponent takes up an equal amount of space within the parent container. This will ensure that the height of the container is fixed, and the layout shift will not occur.

Here's an example of how you can apply this solution to your example project:

<div class="container">
  <app-list-one class="subcomponent"></app-list-one>
  <app-list-two class="subcomponent"></app-list-two>
  <app-list-three class="subcomponent"></app-list-three>
</div>
.container {
  display: flex;
  flex-direction: column;
  height: 500px; /* set a fixed height for the container */
}

.subcomponent {
  flex-grow: 1; /* set each subcomponent to take up equal space */
  margin-bottom: 20px;
}

In addition to setting a fixed height for the container and using the Flexbox layout, you can also use a loading indicator to inform the user that data is being fetched from the API. This can be done by adding a loading state to each subcomponent and displaying a spinner or progress bar while the data is being fetched.

Here's an example of how you can implement a loading state for each subcomponent:

<ng-container *ngIf="loading; else content">
  <!-- show loading indicator while data is being fetched -->
  <div class="loading-indicator">
    <mat-spinner></mat-spinner>
  </div>
</ng-container>

<ng-template #content>
  <!-- show subcomponent content when data is available -->
  <div class="subcomponent-content">
    <!-- subcomponent content goes here -->
  </div>
</ng-template>

In this example, the loading variable is a boolean flag that indicates whether data is being fetched from the API. When loading is true, the loading indicator is displayed, and when loading is false, the subcomponent content is displayed.

By using a fixed height for the container, the Flexbox layout, and a loading indicator, you can ensure that the layout shift is minimized, and the user is informed that data is being fetched from the API.

Hope it helps and everything is clear! :)

Don
  • 366
  • 1
  • 10