简体   繁体   中英

Javascript / Angular - html displays before rendering code

I have a function to get rates from products, so lets say I have one product with two rates. So my product has two rates. Then, when I get those rates I must get the prices attached to my product. So for each rate I have to look for its prices.

The next code below explains this:

this.loadProductInfo = true; // bool to load data in my form

// First of all, I get rates from API
// const rates = this._http....
// Now, for each rate I must search If my product/products have a price:

this.rates.forEach((rate, index, arr) => {
   this._glbGetPricesForProduct.getPrice(params).subscribe(response => {
      if (!arr[index + 1]) {
        this.initForm();
        this.loadProductInfo = false;
      } 
   })
});

The variable loadProductInfo it loads content in my form, so in my html I have:

<form *ngIf="!loadProductInfo"></form>

But form it still give me error: could not find control name.

But if I do this instead, it works correctlly:

setTimeout(() => {
    this.initForm();
    this.loadProductInfo = false;
}, 2000);

So what I want its to say my form to wait until I have all code loaded and then after it, load its contents. But instead it cant find the control because it loads before code. Any help I really appreciate it.

The main mistake I see there is that you are looping over async data which may not be there when your code execute the for each loop (your rates).
I would build an observable with your rates as a source:

...
$rates: Observable<any> = this._http.get(...);
rates.pipe(
    mergeMap((rates) => {
        const priceByRates: Observable<any>[] = rates.map((rate, index, arr) => this._glbGetPricesForProduct.getPrice(params));
        return combineLatest(pricesByRates); // if getPrice complete right away, use forkJoin() instead
    })
).subscribe(res => {
    // No need to check for the last item, all rates have been checked for possible price
    this.initForm();
    this.loadProductInfo = false; 
});
...

This implementation should wait for your api calls to resolve before printing your form.

Since you are hiding the entire form, it may be better to just move the API call into a resolver so that the page does not render until the data is ready.

Here is a minimal StackBlitz showcasing this behavior: https://stackblitz.com/edit/angular-ivy-4beuww

Component

In your component, include an ActivatedRoute parameter via DI.

@Component(/*omitted for brevity*/)
export class MyComponent {
  constructor(private route: ActivatedRoute) {
    // note: 'data' is whatever you label your resolver prop in your routing setup
    route.data.subscribe(resolved => {
      if ("data" in resolved) this.resolveData = resolved["data"];
    });
  }
}

Route Setup

And in your router setup you would have the following:

const routes: Routes = [
  {
    path: 'my-route-path',
    component: MyComponent,
    resolve: {
      data: MyResolver
    }
  }
];

Resolver

Finally, your resolver would make your API call utilizing your service:

@Injectable({providedIn: 'root'})
export class MyResolver() implements Resolve<T> {

  constructor(private service: MyService) {}

  resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<T> | Promise<T> | any {
    return this.service.myRequest();
  }
}

The final result will be that your view will not be rendered until your data is ready.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM