AppInitializer class
dataloadcomplete = false;
initializeApp() {
let promise = new Promise((resolve, reject) => {
Promise.all(promise).then(
(res) => { dataloadcomplete = true; resolve() },
(err) => { reject(); },
);
return promise;
}
CanactivateRoute
canActivate(route: ActivatedRouteSnapshot): Promise<Boolean> {
// if data load completed return true else false;
}
want to reinitialize the application on country change.
With the small amount of shown code, it's difficult to suggest the best course of action as there are several options.
First, in any case, be sure that the class that holds the data (Class 1 in your example) is an injectable service. Inject it into any component that needs to access the data.
If you need to load some data and wait to route to a specific component until that data is loaded, you can use route resolvers.
If you need to wait for any of the application to load before launching the application, you can use APP_INITIALIZER: Angular: How to correctly implement APP_INITIALIZER
If you want to better manage your application state, consider using the Redux library for Angular called NgRx. You can use it to manage the flow of data in your application.
If you have multiple HTTP calls, I think, you can use forkJoin to retrieve all the call into one point then use EventEmitter and catch the event from the function of class 2. no need to wait for setting the variable of class 1.
You can use also interval in Angular and wait until you get the true value of the variable of class 2.
Related
In my project there is one API which is called on init method when component is called as follows:
ngOnInit(): void {
this.getCurrentUserpost();
}
and the function looks like this
getCurrentUserpost(){
this.friend.getAllFriendsPost(this.friendsId).subscribe(res => {
this.allPosts = res;
});
}
and the service API function looks like the following
getAllFriendsPost(ids:any){
return this.http.post(this.BaseUrl+'/allfriendspost',ids);
}
When I navigate between component using routeLink then every time when I visit component where I have added this API, it will call the API multiple times at the same time with increasing order.
See the network tap where you can see multiple API calls are done
Can anyone advise me how to I can resolve this issue?
You have a memory leak. You need to unsubscribe from the observable.
One of multiple possibilities would be to use take(1) rxjs-operator.
getCurrentUserpost(){
this.friend.getAllFriendsPost(this.friendsId).pipe(
take(1),
).subscribe(res => {
this.allPosts = res;
});
}
Just to mention some other possibilities:
async pipe
takeUntil operator (or other take*) operators
Subscription.unsubscribe
how to subscribe to response when tap operator is used in the service.
Have anyone know how to resolve this?
edit(status) {
dataObj.val = status;
// call post service with status..
this.service
.update(dataObj)
.pipe(takeUntil(this._ngUnsubscribe$))
.subscribe(() => {
//i would like to wait until response come from backend and then navigate to the page so i get data over there.
if (res.status === 'Success') {
this.router
.navigate(['../../success'], {
relativeTo: this.route,
})
.then(() => {});
} else {
this.location.back();
}
});
}
//akita store service
update(
obj: any,
): Observable < any > {
return this.service.update(obj).pipe(
delay(800),
map((data: RestfulResponse < any > ) => data.data),
tap((data: anny) => {
this.store.update((state) => {
state.updateValue = data; // value is not updating and it is navigating to route
});
}),
);
}
//post service
update(obj){
//post call
}
Is there any way I can use tap and in service side and subscribe on component side?
I know I can use finalize but it is not helping for writing conditions inside.
The tap operator, by design, handles side effects which don't happen within the context of your observable pipeline. This means that your pipeline will never wait for results from the tap itself. I don't recommend using it in this manner. Under most circumstances, I only use tap for debugging.
If you are waiting for a particular state change, you should create a separate observable, selecting from your store, to watch the state for the expected change.
If you want to trigger an additional action when something happens, I recommend using ngrx Effects to achieve this.
Have a look at this post, where I talked about how to implement a similar use case:
https://stackoverflow.com/a/64491398/166850
You should also strive to set up reducers that apply your state changes, rather than updating the store directly.
Consider each of the following as a separate concern that you can implement independently of the others:
When user does an edit, trigger an edit action.
The reducer should update the state based on the edit action (for example, to show that a save is in progress)
When the edit action is triggered, trigger an effect. The app should make an HTTP call to save the change, then trigger a save finished action.
When the save is finished, the router navigation should be triggered.
This separates your code into multiple units which are easy to test and verify independently.
If #1 produces an action which is consumed by your reducer (#2), you can also create an ngrx Effect for #3 which listens for the same action, handles the HTTP call using switchMap, then triggers another action to signal that it's done.
Edit
Here's a simple example. The first time an action called APP_LOADED is triggered (from the AppComponent), this Effect makes an HTTP call to get data from the server, then triggers an action using the response data as the action payload.
The actual HTTP call is delegated to another service, the HttpMyConfigDataService, which simply calls HttpClient and returns an Observable.
#Injectable({
providedIn: 'root'
})
export class LoadMyConfigEffect {
constructor(
private httpMyConfigDataService: HttpMyConfigDataService,
private action$: Actions
) {
}
loadMyConfigData$ = createEffect(() => {
return this.action$.pipe(
filter((action) => action.type === 'APP_LOADED'),
take(1),
switchMap(() => this.httpMyConfigDataService.get().pipe(
map(data => {
return {type: 'MY_CONFIG_DATA_LOADED', payload: data};
}),
catchError(err => {
console.error('Error loading config data.', err);
return of({type: 'CONFIG_LOAD_ERROR', payload: err.message, isError: true);
})
))
);
});
}
How do I run methods synchronously in Angular Typescript?
These are not functions, but methods.
First one calls a service, and then second saves into an array.
runTwoMethods()
{
this.validateAddress();
this.saveJsonArchive();
}
Validate address may call more sub-method,may not even be Api, so want to wait until everything completes before saving.
Following syntax is for functions, currently searching for class methods,
Angular / TypeScript - Call a function after another one has been completed
At the end, data is stored into a current data object. And I want to be
save to an archive. Maybe another possibility, how do I keep AddressCurrentMailing and JSONArchive[2] in sync?
Current Data object is sourced from API, not sure where (nor I am not allowed to edit the APIs, then calls transformations), and then would like to save into JsonArchive.
this.jsonArchive[this.jsonArchiveCounter] = this.addressCurrentMailingFinalData
You can use Observable for the 1st and use your 2nd method after Subscribe in callback:
validateAddress(): Observable<Someclassname>
{
return this._http.get<Someclassname>('url');
}
this.yourService.validateAddress().subscribe(
(result) => {
this.saveJsonArchive();
},
(error) => console.log('NO saveJsonArchive'));
EDIT (validateAddress with more one Observable)
const ox: Observable<Someclassname>[] = [];
ox.push(this.http.get<Someclassname>(url1));
ox.push(this.http.get<Someclassname>(url2));
ox.push(this.http.get<Someclassname>(url3));
forkJoin(ox).subscribe(result => this.saveJsonArchive());
You could return a Promise in "this.validateAddress();" and await it.
I assume you use the Angular HttpClient.
For example:
private validateAddress(): Promise<someclassname>
{
return this._http.get<someclassname>("someurl").toPromise()
}
async addSeasonal()
{
await this.validateAddress();
await this.saveJsonArchive();
}
I hope this helps.
I've added an interceptor for my HTTP requests where I have to use the access token of my user instance. In my app component I initialise my user:
app.component.ts
private async restoreUser(): Promise<UserModel | any> {
// ... some view stuff
return this.userService.restore()
// login instance could be found
.then(async () => {
// ... some view stuff
})
// local storage is empty -> login is necessary
.catch(async () => {
// ... some view stuff
this.subscription = this.networkSrv.getNetworkStatus()
.subscribe((status: ConnectionStatus) => {
if (status === ConnectionStatus.Online) {
// ... some view stuff
} else {
// ... some view stuff
}
});
});
}
http.interceptor.ts
return this.userSrv.user.pipe(
map((user: UserModel) => request.clone(
{setParams: {'access-token': user.accessToken}}
)),
mergeMap(request => next.handle(request))
);
Now I would like to do a request by initialising my app. The problem is, that the user instance is empty and the application throws an error. Is there a way to do something like await -> so that the user instance is set?
Example:
this.transmissionSrv.restoreQueue().then((projects: ProjectModel[]) => {
this.transmissionSrv.transmitProjects(projects, true).subscribe(console.log);
});
Currently, I use the setTimeout-method, but that isn't the way I should do it, right? In addition, sorry for not being consistent by using Observer; Ionic often uses Promises(?)
You should try adding a filter before your map. Using the filter, your map wont get call until the user is set.
return this.userSrv.user.pipe(
filter(Boolean),
map((user: UserModel) => request.clone(
{setParams: {'access-token': user.accessToken}}
)),
mergeMap(request => next.handle(request))
);
There are a couple of ways you could solve this.
Synchronously: Use an Angular APP_INITIALIZER (see here) to make the backend call and ensure the user object is present when the app bootstraps.
Asynchronously: Modify your existing application to store the user instance in an RxJs BehaviorSubject in a service somewhere and have components that depend on it subscribe to that BehaviorSubject wherever the user instance is needed. When the service constructs, have it make the backend call and stick the completed user instance inside the BehaviorSubject (userSubject.next(user)) when it's complete.
I just started playing with angular 2 and i've ran into a small problem, that i ve searched for in various forms and also angulars documentation.
I've managed to make a service that makes a call and then i want in a component when i press a button to load another component with dynamicload component and have access to the ajax result.
The problem is that I can t figure out how to do that..
The question is how can I make the result accesible in other components using Observables or Promises method.
If I understood correctly your question, you are looking a way to insert a data from request to another nested component.
I hope this image will clarify for you the data flow for this case.
Your Root component is calling a service method which returns for you promise object.
Then you map needed data from response to the component model inside Root Component constructor.
And your Child component should be subscribed for the model which you was preparing in previous step.
ngOnInit() {
this.dataService.getSomeData()
.subscribe((data: IData) => {
this.data = data;
});
}
Just a short example above how to set model in the root component from the promise object to the local model.
New research:
There is another way to fill your components by data from api's. You can use EventEmitter to emit event from service, and then, you can subscribe for this event inside you created components, so they will get a data, each time there will be called the service. Here is nice example of this strategy in the first answer. Service Events
Hope it will help you, let me know if you will need additional info!
Just create a service, then inject the service where you want.
Here it's an example how to share a service ajax data across many components without making the request twice :
https://stackoverflow.com/a/36413003/2681823
the Service:
#Injectable()
export class DataService {
constructor(private http: Http) { }
private _dataObs = new ReplaySubject<request>(1);
getData(forceRefresh?: boolean) {
// On Error the Subject will be Stoped and Unsubscribed, if so, create another one
this._dataObs = this._dataObs.isUnsubscribed ? new ReplaySubject(1) : this._dataObs;
// If the Subject was NOT subscribed before OR if forceRefresh is requested
if (!this._dataObs.observers.length || forceRefresh) {
this.http.get('http://jsonplaceholder.typicode.com/posts/2')
.subscribe(
requestData => {
this._dataObs.next(requestData);
},
error => this._dataObs.error(error));
}
return this._dataObs;
}
}
the Component:
#Component({
selector: 'child',
template : `<button (click)="makeRequest()" class="btn">Click me!</button>`
})
export class Child {
constructor(private _dataService: DataService) { }
makeRequest() {
this._dataService.getData().subscribe(
requestData => {
console.log('ChildComponent', requestData);
}
}
}
A full working example/plunker can be found here : http://plnkr.co/edit/TR7cAqNATuygDAfj4wno?p=preview