# Loading data before components in Angular

Source: https://tpiros.dev/blog/loading-data-before-components-in-angular

We'll walk through a few ways to make sure data is ready for a component before it loads and appears on screen.

# Setup the app

First, let's scaffold an application using the angular-cli. Notice the routing flag, which gives us a routing module out of the box: `ng new route-change-app --routing`.

Let's also generate two new components: `ng g c home && ng g c products`.

We also need to update `app-routing.module.ts` and add our routes:

```typescript
// excerpt

const routes: Routes = [
  { path: '', component: HomeComponent },
  { path: 'products', component: ProductComponent },
];
```

And finally, open `app.component.html` and add this HTML:

```html
<nav>
  <a routerLink="/">Home</a> |
  <a routerLink="/products">products</a>
</nav>
<router-outlet></router-outlet>
```

# A slow API

In most full-stack JavaScript applications, we're going to talk to an API. That means our app's speed is partly tied to how fast the API responds.

For demonstration purposes, let's fake a slow API. Create a service via the CLI called `ApiService`: `ng g s api`.

> To keep things simple, we won't use an actual backend. Instead, we'll return static data via an Observable with a delay.

The API service should look like this:

```typescript

@Injectable({
  providedIn: 'root',
})

  constructor() {}

  getProducts(): Observable<any> {
    const products = [
      { name: 'shoe', price: 15.99 },
      { name: 'shirt', price: 25.99 },
      { name: 'jeans', price: 54.5 },
    ];
    return of(products).pipe(delay(3000));
  }
}
```

Now let's consume this API from the `ProductComponent`:

```typescript

// ... more code
constructor(private api: ApiService) { }
products;
ngOnInit() {
  this.api.getProducts().subscribe(products => this.products = products);
}
```

And display it in the component template:

```html
<ul>
  <li *ngFor="let product of products">
    {{ product.name }} costs {{ product.price }}
  </li>
</ul>
```

Standard setup. Launch the application and navigate to the "products" route. You'll see a blank page for 3 seconds. Then the data arrives and renders.

That's a problem. For 3 seconds the user has no idea what's happening. They might assume the site's broken and leave. Not great.

Let's look at what we can do about it.

# Option 1: `ngIf`

The most obvious approach: add an `ngIf` to the template that shows a loading indicator until `this.products` gets populated:

```html
<p *ngIf="!products">Loading data ...</p>
<ul>
  <li *ngFor="let product of products">
    {{ product.name }} costs {{ product.price }}
  </li>
</ul>
```

> When accessing properties of an object that hasn't loaded yet, use a [Safe Navigation Operator](https://fullstack-developer.academy/question-mark-in-angular-expression/).

# Option 2: Router resolver

A router resolver attaches a resolve function to the route that loads a component with an API call. Angular won't load or display the component until the API call (or whatever else we define) completes.

Let's see how to set it up.

First, create a separate class for the resolver using the Angular CLI: `ng g class resolver`.

The class should look like this:

```typescript
// resolver.ts

@Injectable()

  constructor(private api: ApiService) {}

  resolve() {
    return this.api.getProducts();
  }
}
```

Don't forget to add this class as a provider:

```typescript
// app.module.ts

// ...
providers: [Resolver],
```

The code above implements the `Resolve` interface, which requires a `resolve()` method. Inside that method, we consume the API we built earlier.

Next, update `app-routing.module.ts` to tell the route where and how to resolve the data:

```typescript
// app-routing.module.ts

const routes: Routes = [
  { path: '', component: HomeComponent },
  {
    path: 'products',
    component: ProductsComponent,
    resolve: { products: Resolver },
  },
];
```

One more change. Right now we're making an API call in both the product component and the resolver class. That's a duplicate call we don't need. So let's update the product component.

The resolved data is available on the router snapshot, which we access from `@angular/router`:

```typescript
// products.component.ts

// ...
constructor(private route: ActivatedRoute) { }
products;
ngOnInit() {
  this.products = this.route.snapshot.data.products;
}
```

Now refresh the application and click the Products link. You'll notice a delay before the route changes. Once the API resolves, the route switches and the product list appears.

# Conclusion

We've looked at a couple of ways to handle situations where a slow API call would leave users staring at a blank screen. Pick one and use it. Your users will stick around longer.
