I am writing a ionic2 firebase application. I have a provider in which constructor will make async call and resolves it. Since it is an async call my component is getting initialized and trying to contact the provider even before its completion. How to make the component wait untill async operation gets resolved and completed in the provider constructor?
I generally think this is the wrong question. Instead I would ask myself “how can I make this component appear responsive to the user while indicating that it is waiting on something in order to reach a more interactable state?”. Loading spinners are the easiest way, there are also interface mocks that look like fuzzed-out content.
I would make the provider expose this operation as an Observable
(which can be created in its constructor if you wish). That way your component can simply subscribe to that Observable
upon its creation. That is a much better approach than trying to stall the component until concrete data is ready, because it gives you further updates for free.
It will be more clear if I explain my actual scenario:
I have collection of carts out of which one will be current cart at any given point of time and remaining will be the one’s that were already checked out. So my cart provider has to identify the current cart from list of available carts in the collection and has to hold the reference to that current cart document. Now my cart component will ask the cart provider to give me the list of cart items available in current cart.
This has to be one time activity that’s why I am doing in this provider constructor. If I expose this as an Observable and let the component resolve then it will be called every time the component loads.
Since component gets loaded every time with page view, we will end up resolving the current cart every time.
Any suggestions?
The cart provider can internally carry currentCart
as a Subject
and expose it as an Observable
. Whatever process changes and/or fetches information needed for currentCart
to be usable can somehow cause next
to be called on the Subject
. The components that display stuff involving the current cart can subscribe to the publicly-visible Observable
version of currentCart
(which would presumably include a way to get access to the contents of that cart).
Too many "it"s and "this"s for me to be sure what you’re concerned about here.
As I understand it, there are two issues:
- what is the current cart?
- what’s in it?
The answer to the first question only changes when a different cart is selected, and as long as the current cart stays current, the subscription at component load time will be trivially fast and not something to worry about.
The answer to the second question is more complicated, and you could add additional caching mechanisms if your testing indicates that it’s warranted, but even in a worst-case scenario with no further caching, the only time you have to ask the second question is when the current cart changes, and once you’ve got an answer, further component loads will not trigger any further network activity.
I have understood the concept of Subject and Observables now. Using Subject is actually solving my problem. But I am not able to use its full capabilities.
Consider the below code:
class Provider {
...
...
getProducts(){
// returns an observable of products
return angularFireStore.collection('products');
}
}
class ProductsPage{
private products:any;
constructor(private myProv: Provider){
myProvider.getProducts().subscribe(
results => this.products = results;
)
}
}
Now I have subscribed to products collection. When I add a new product to the products collection, the above subscription will get triggered automatically and latest will be updated in the products page.
Even I got the latest data since component constructor will get called every time when I navigate to its page, I end up calling the firebase again to get latest data. I feel this is unnecessary since I have already got the latest data when I have done an add on products collection itself because of subscription.
Is there any life cycle method for a component that will be called only once and not for every view of that page. So that I will move my subscription code to that method and there will be no need to call getProducts() of provider.
@rapropos Any thoughts?
Save your firebase result in your service and return this copy to your component.
Then how do we get the latest data when something changes in the backend?
I use a function like this:
getProductUpdateListener() {
return this.productUpdated.asObservable();
}
In the component I do something like this in the ngOninit
this.productSub = this.productService
.getProductUpdateListener()
.subscribe((productData: { products: Product[]; postCount: number }) => {
this.products = productData.products;
});
and in the ngOnDestroy:
if (this.productSub) {
this.productSub.unsubscribe();
}
In the Service you can use:
this.productUpdated.next({
products: [...this.products],
postCount: productsData.maxProducts
});
to inform all subcribers about changes.
But perhaps there are some things specific to firebase…
Regarding your first reply, would you have a simple code snippet of the provider and the client component to illustrate this mechanic?
Is this close enough and/or useful to you?
Thank you very much. This is a beautifully crafted piece of code. Can’t wait to play with it and adapt it for my ionic 3/storage needs.