Angular 2
Component Rendering
Asynchronous Data
Data Gathering
Web Development

how to wait rendering component till all data is gathert in Angular 2

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

In Angular, you usually do not “stop” a component from being created until data arrives. Instead, you let the component exist and control what the template renders while the data is loading. The standard solutions are conditional template rendering, the async pipe, and route resolvers when navigation should wait for required data before activation.

The Basic Pattern: Load in ngOnInit, Guard in the Template

The most common approach is to fetch data in ngOnInit and use *ngIf to render the real UI only when the data is ready.

typescript
1import { Component, OnInit } from '@angular/core';
2
3@Component({
4  selector: 'app-profile',
5  template: `
6    <ng-container *ngIf="user; else loading">
7      <h1>{{ user.name }}</h1>
8      <p>{{ user.email }}</p>
9    </ng-container>
10
11    <ng-template #loading>
12      <p>Loading...</p>
13    </ng-template>
14  `
15})
16export class ProfileComponent implements OnInit {
17  user: { name: string; email: string } | null = null;
18
19  ngOnInit(): void {
20    setTimeout(() => {
21      this.user = { name: 'Ava', email: '[email protected]' };
22    }, 1000);
23  }
24}

The component renders immediately, but the expensive or data-dependent content does not appear until user is populated.

Prefer Observables and the async Pipe

In Angular applications, data usually comes from Observables. The async pipe handles subscription and change detection for you.

typescript
1import { Component } from '@angular/core';
2import { Observable, of } from 'rxjs';
3import { delay } from 'rxjs/operators';
4
5@Component({
6  selector: 'app-dashboard',
7  template: `
8    <ng-container *ngIf="user$ | async as user; else loading">
9      <h2>{{ user.name }}</h2>
10    </ng-container>
11
12    <ng-template #loading>
13      <p>Loading dashboard...</p>
14    </ng-template>
15  `
16})
17export class DashboardComponent {
18  user$: Observable<{ name: string }> = of({ name: 'Noah' }).pipe(delay(1000));
19}

This is cleaner than subscribing manually in the component class unless you need extra logic around the result.

Waiting for Multiple Requests

If the page depends on several API calls, wait for all of them together. forkJoin is appropriate when each request completes once.

typescript
1import { Component, OnInit } from '@angular/core';
2import { forkJoin, of } from 'rxjs';
3import { delay } from 'rxjs/operators';
4
5@Component({
6  selector: 'app-report',
7  template: `
8    <ng-container *ngIf="vm; else loading">
9      <h2>{{ vm.user.name }}</h2>
10      <p>Orders: {{ vm.orders.length }}</p>
11    </ng-container>
12
13    <ng-template #loading>
14      <p>Preparing report...</p>
15    </ng-template>
16  `
17})
18export class ReportComponent implements OnInit {
19  vm: { user: any; orders: any[] } | null = null;
20
21  ngOnInit(): void {
22    forkJoin({
23      user: of({ name: 'Ava' }).pipe(delay(500)),
24      orders: of([{ id: 1 }, { id: 2 }]).pipe(delay(700))
25    }).subscribe(result => {
26      this.vm = result;
27    });
28  }
29}

This gives the template a single ready state instead of several partial states to coordinate manually.

Route Resolvers for Required Data

If a route should not activate until data is available, use a resolver. This moves loading to the routing layer instead of the component template.

typescript
1import { Injectable } from '@angular/core';
2import { Resolve } from '@angular/router';
3import { Observable, of } from 'rxjs';
4
5@Injectable({ providedIn: 'root' })
6export class UserResolver implements Resolve<any> {
7  resolve(): Observable<any> {
8    return of({ name: 'Ava', email: '[email protected]' });
9  }
10}

Then read the resolved data in the component:

typescript
1import { ActivatedRoute } from '@angular/router';
2
3constructor(private route: ActivatedRoute) {}
4
5ngOnInit(): void {
6  const user = this.route.snapshot.data['user'];
7  console.log(user);
8}

Use this when the page is meaningless without the initial data and you would rather delay navigation than show a loading state inside the component.

Avoid Manual “Render Blocking”

Trying to manually block Angular from rendering the component is usually the wrong mental model. Angular change detection is designed to update the view as state changes. Instead of preventing rendering, render a loading state, a skeleton, or a placeholder until the data is ready.

That leads to simpler code and a better user experience than hiding the entire app until every request completes.

Error Handling Matters

Loading logic also needs a failure state. If you only model “loading” and “loaded,” the UI gets stuck when a request fails.

typescript
state: 'loading' | 'ready' | 'error' = 'loading';

Then render different template branches for each state. This is more robust than assuming the happy path.

Common Pitfalls

The main mistake is trying to delay component creation rather than controlling template output. Another common problem is nesting subscriptions instead of combining async sources with operators such as forkJoin. Developers also often forget to model an error state, which leaves the template permanently stuck on “Loading...” after a failed request. Finally, manual subscriptions without cleanup create avoidable memory leaks when the async pipe would have handled the lifecycle automatically.

Summary

  • In Angular, do not try to block component creation; control what the template shows.
  • Use *ngIf for simple loading guards.
  • Prefer the async pipe for Observable-driven data.
  • Use forkJoin when several one-time requests must finish before showing the UI.
  • Use route resolvers when navigation itself should wait for required data.

Course illustration
Course illustration

All Rights Reserved.