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-guardsTo 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 anObservable
or aPromise
from what I understood, that's why the only way to make it work was by creating aPromise
- 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
- (and 2.) call to
resolve(...)
missing - 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. - Put an
*ngIf
in parent. Only render child when parent'sitems
is ready. - Separate your input
getter
setter
in child. Then act whenever the value is set, you can use RxJSBehaviorSubject
also. - Do it during the
ngOnChanges
of the child. Refer to here for example. https://angular.io/docs/ts/latest/guide/lifecycle-hooks.html#!#onchanges
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 */);
});
});
}
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:
<div *ngIf="items">
<child [items]="items | async">
</div>
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);
})
}
Related Topics
Html5 and JavaScript to Play Videos Only When Visible
Calculate and Display % Discount for Price Classes JavaScript
How to Compress an Image Via JavaScript in the Browser
How to Close Sweet Alert on Ajax Request Completion
If a Div Contains a Specific String of Text, Edit the Parent'S CSS
Detect If String Contains Any Spaces
How to Download a File With Node.Js (Without Using Third-Party Libraries)
How to Validate This Select Option If Not Selected
Violation Long Running JavaScript Task Took Xx Ms
How to Get Convert an Object to HTML Markup
Reactnativejs: Referenceerror: Can't Find Variable: View
How to Remove a Json Inside a Json by a Key Using Jquery
Node.Js MySQL Query in a for Loop
How to Refresh Specific Div Using Javascript/Jquery With the Variables on It
How to Take Screenshot of a Div With JavaScript
How to Remove Captcha Verification from Firebase Phone Auth Using JavaScript
Laravel - How to Pass a PHP Variable to a JavaScript Function from a Blade File
How to Trigger a Jquery Function Only Once in One Page, Per Session