Angular Load Async Data Before Component Initialization

Angular Load Async data before component initialization

There are already several answers here that correctly answer the question you are asking - the reason they dont work for you is because the data you are binding does not matching your ids.

Your previous questions were both basically asking the same thing:

  • angular-nested-ngfor-tags-with-ngif-check-material-data-binding-async-issue
  • angular-material-binding-property-does-not-work-cant-bind-to-checked-ngif

To prevent further wasted effort from the community, I've gone to the effort of writing the debug code you should be writing yourself. This shows how the safe navigation operator fixes the template issue, as several people have suggested already - wrapping with *ngIf would also solve the problem and the values I've added show why your tick boxes are not checked.

https://stackblitz.com/edit/async-angular-resolver-hxxyw4?file=src/app/roles/roles.component.html

Angular | How to initially load data before the app is resolved?

Add to app providers:

{
provide: APP_INITIALIZER,
useFactory: startupServiceFactory,
deps: [ Session ],
multi: true,
},

where:

export function startupServiceFactory(startupService: Session): Function {
return () => startupService.initialize();
}

replace the startupService.initialize() to your method.
the deps contains any dependency your service has, can be omitted.

example:

 async initialize(): Promise<any> {
try {
if (!environment.production) {
this.user = await this.service.init();
this.role = this.user.role;
this.lang = this.user.lang;
this.translate.use(this.user.lang);
this.registerLocale();
this.navigate();
return;
}
this.router.navigateByUrl('/');
} catch (err) {}
}

Loading data before AppComponent.ngOnInit is called

You can do it with APP_INITIALIZER, a function that will be executed before an application is initialized

https://angular.io/api/core/APP_INITIALIZER

In your app.module.ts

export function initializeConfig(initService: InitService) {
const x = (): Promise<any> => {
return initService.Init();
};
return x;
}

@NgModule({
...
providers: [
InitService,
{
provide: APP_INITIALIZER,
useFactory: initializeConfig,
deps: [InitService],
multi: true
},
],
...
})

init.service.ts

@Injectable()
export class InitService {

constructor(private service: YourService) {
}

Init() {
return new Promise<void>((resolve, reject) => {
this.service.loadDataBeforeBootstrap.then( () => {
resolve();
}).catch( error => {
reject(error);
});
});
}
}

Angular2: How to load data before rendering the component?

update

  • If you use the router you can use lifecycle hooks or resolvers to delay navigation until the data arrived.
    https://angular.io/guide/router#milestone-5-route-guards

  • To load data before the initial rendering of the root component APP_INITIALIZER can be used How to pass parameters rendered from backend to angular2 bootstrap method

original

When console.log(this.ev) is executed after this.fetchEvent();, this doesn't mean the fetchEvent() call is done, this only means that it is scheduled. When console.log(this.ev) is executed, the call to the server is not even made and of course has not yet returned a value.

Change fetchEvent() to return a Promise

     fetchEvent(){
return this._apiService.get.event(this.eventId).then(event => {
this.ev = event;
console.log(event); // Has a value
console.log(this.ev); // Has a value
});
}

change ngOnInit() to wait for the Promise to complete

    ngOnInit() {
this.fetchEvent().then(() =>
console.log(this.ev)); // Now has value;
}

This actually won't buy you much for your use case.

My suggestion: Wrap your entire template in an <div *ngIf="isDataAvailable"> (template content) </div>

and in ngOnInit()

    isDataAvailable:boolean = false;

ngOnInit() {
this.fetchEvent().then(() =>
this.isDataAvailable = true); // Now has value;
}

Angular - Wait until I receive data before loading template

After studying the different approaches that people gave me, I found the solution on the async pipe. But, it took me a while to understand how to implement it.

Solution:

// Declaring the Promise, yes! Promise!
filtersLoaded: Promise<boolean>;

// Later in the Component, where I gather the data, I set the resolve() of the Promise
this.getFiltersSubscription = this.getFilters().subscribe(
(filters) => {
this.filters = filters;
log.info('API CALL. getting filters');

this.filtersLoaded = Promise.resolve(true); // Setting the Promise as resolved after I have the needed data
}
);

// In this listener triggered by the dynamic components when instanced,
// I pass the data, knowing that is defined because of the template change

// Listens to field's init and creates the fieldset triggering a service call
// that will be listened by the field component
this.iboService.initIBOsFilters$.subscribe(
(fieldName) => {
if (fieldName === 'IBOsRankSelectorFieldComponent') {
log.data('inside initIBOsFilters$ subscription, calling updateIBOsFilters()', fieldName);
this.iboService.updateIBOsRankList(this.filters['iboRank'].data);
}
}
);

In the template, I use the async pipe that needs an Observable or a Promise

<div *ngIf="filtersLoaded | async">
<div [saJquiAccordion]="{active: group.value['collapsed']}" *ngFor="let group of filterGroupsTemplate | keysCheckDisplay;">
<div>
<h4>{{group.key | i18n}}</h4>
<form id="ibo-{{group.key}}" class="form-horizontal" autocomplete="off" style="overflow: initial">
<fieldset *ngFor="let field of group.value | keys">
<ng-container *ngComponentOutlet="fieldSets[field.value.template];
ngModuleFactory: smartadminFormsModule;"></ng-container>
</fieldset>
</form>
</div>
</div>
</div>

NOTE:

  • async pipe need an Observable or a Promise from what I understood, that's why the only way to make it work was by creating a Promise
  • I didn't use the resolver approach because it's used when you arrive to the component through Angular's routing. This component is part of a larger component and it's not instanced through routing like any other normal component. (Tried that approach though, worked a bit with it, didn't do the job)

Load data before component inialization - angular 2

  1. (and 2.) call to resolve(...) missing


    1. init = (isRegistered) => {
      return new Promise((resolve, reject) => {
      this.account.init(isRegistered).then(data => {
      //some data inialization
      console.log("OnInit Called User Service")
      resolve(/*someValue */); // <<<== missing
      // or reject(/* someValue */);
      });
      });
      }

      1. Why do you expect it to block anything? ngOnInit() can't be blocked. You can for example use *ngIf to not show anything before the data is not yet available.
        You can also use a router guard to prevent component creation before data is available. https://angular.io/docs/ts/latest/guide/router.html#!#resolve-guard
        But there is no way to block execution in any way in JS.

      Angular 4 load data before initialize the application

      I've found a workaround on this question.

      I found a way to use canActivate. I had already tried to use .map() to return an Observable, but the key point is the .first() at the end, this is waht solved the issue.

      I'll keep it open tho, because if there is a way to achieve the same result using Resolve, maybe it is better.

      How to load data before rendering page without using a resolver?

      After checking your stackblitz and question, I understand, you need a guard on route.

      I have first modified your getInfo service to return a Promise

        getInfo(titel: string, type: string) {
      return this.http.get(...).toPromise()
      .then( data => {this.obj = data; return data; });
      }

      I have created a guard service and implemented CanActivate

      @Injectable({
      providedIn: "root"
      })
      export class AppGuardService implements CanActivate {
      constructor(private router: Router, private srv: MyserviceService) {}

      canActivate(next: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> | Promise<boolean> | boolean {
      return this.srv.getInfo("The Beatles", "artist")
      .then(() => return true; })
      .catch(error => {
      console.error(error);
      return false;
      });
      }
      }

      Modified routing to implement canActivate:

      { path: "hello", component: HelloComponent, canActivate:[AppGuardService] }

      You can see the whole stackblitz at https://stackblitz.com/edit/angular-nazeby

      Update:

      However, if the use case needs only one navigation, you can modify the navigate function which now gets the data from service as a Promise and can skip the app-guard service and CanActivate

        navigate(){
      this.srv.getInfo("The Beatles", "artist").then(
      () => this.router.navigate(["/hello"])
      )
      }

      Happy Coding!

      Angular 2 how to make child component wait for async data to be ready

      There are three ways to do this:

      1. Put an *ngIf in parent. Only render child when parent's items is ready.
        1. <div *ngIf="items">
          <child [items]="items | async">
          </div>

          1. Separate your input getter setter in child. Then act whenever the value is set, you can use RxJS BehaviorSubject also.
            1. private _items = new BehaviorSubject<Items[]>([]);

              @Input() set items(value: Items[]) {
              this._items.next(value);
              }

              get items() {
              return this._items.getValue();
              }

              ngOnInit() {
              this._items.subscribe(x => {
              this.chunk(x);
              })
              }

              1. Do it during the ngOnChanges of the child. Refer to here for example. https://angular.io/docs/ts/latest/guide/lifecycle-hooks.html#!#onchanges


              Related Topics



Leave a reply



Submit