navParam caching issue / ionViewLoaded db re-load issue?

I’m having trouble with an Ionic 2 App I am working on - It appears as though it’s a caching issue, but I can’t figure out why it is occurring. I think perhaps the navParam data I am pushing to a page is persisting when I pop the page off again? Below is a detailed explanation of what I’m experiencing - I would appreciate any input / advice / code critiques.

GroceryApp
The GroceryApp consists of the following components:

  • HomePage - Lists Grocery Stores. Enables a user to delete existing stores.
  • DetailsPage - Acts as the “Create Grocery Store” and “Edit Grocery Store” page, depending on how the user navigated to the page. Enables a user to Enter / Modify Grocery Store Information, and also to View / Delete from a list of store products (each store can have zero, one or more products).
  • ProductModal - Acts as the “Create Grocery Store Product” and “Edit Grocery Store Product” modal, depending on how the user navigated to the modal. Enables the user to Enter / Modify Grocy Store Product Information.
  • GroceryDataProvider - Contains all required details for syncing PouchDB to Cloudant. I based this on the provider found in this tutorial, except it uses cloudant instead of CouchDB.

Sample Screenshots

Database Structure
I’m using a very basic JSON document structure:
{
“_id”: “471B10E6-2F4E-3663-8765-44176EAD5343”,
“_rev”: “1-7fcd259cb670ef3457278ec1b42c5574”,
“g_store_name”: “Store A”,
“g_store_description”: “A very nice store”,
“g_store_products”: [
{
“product_id”: “0”,
“product_name”: “Apple”,
“product_description”: “Crispy”,
“product_price”: “100”
},
{
“product_id”: “1”,
“product_name”: “Banana”,
“product_description”: “Ripe”,
“product_price”: “150”
},
{
“product_id”: “2”,
“product_name”: “Pear”,
“product_description”: “Rotten”,
“product_price”: “50”
}
]
}

The Issue
I’m having an issue maintaining and displaying the GroceryStoreProduct Array. When a user is on the DetailsPage, I want them to be able to add / edit / delete as many GroceryProducts as they wish, however I don’t want anything to be saved to the database until they hit the SUMIT button. Also, if the user hits the CANCEL button then I want the GroceryStoreProduct Array to return to its original state before the user entered the DetailsPage, i.e. a reflection of what is written in the database.

Everything works fine on SUBMIT. All new product are added to the database and any removed products are removed.

However, when the user hits CANCEL, I’m having no such luck. For example, if I Edit “Store A” and delete the “Banana” product, then click CANCEL, the HomePage is displayed however it lists “Store A” as having “Apple” & “Pear” products, but no “Banana”. Opening the DetailsPage will reflect the same results, whereby only “Apple” & “Pear” products are listed. However checking the database will reveal that the “Banana” product still exists in the “Store A” g_store_products array.

I have been searching the Internet for clues as to what the problem may be, but no such luck yet. I thought maybe I needed to re-load the DB values using the ionViewLoaded function on the DetailsPage, but I can’t work out how (or if I need to). Any input would be greatly appreciated.

The GroceryApp project can be found on GitHub here. You’ll need to add your own cloudant connection information to the grocery-data.ts file to get it to work.

I’d probably try changing:

  ionViewLoaded(){
    this.groceryDataService.getGroceryData().then((data) => {
        this.groceryStoreDataSet = data;
    });
  }

to:

  ionViewDidEnter(){
    this.groceryDataService.getGroceryData().then((data) => {
        this.groceryStoreDataSet = data;
    });
  }

As according to the documentation (NavController docs):

ionViewDidEnter
"Runs when the page has fully entered and is now the active page. This event will fire, whether it was the first load or a cached page."

With the reason being that your HomePage is likely cached (as by default pages are cached, and so popping the StoreDetailsPage would simply navigate to the cached page) and ionViewDidEnter only runs upon initialization of the page (and not when loading a cached version of it).

1 Like

In truth, I had tried this option but it yielded the same results, therefore I reverted to ionViewLoaded as that was the method used in the tutorial I was following. However, given your suggestion I decided to try again and upon further inspection I noticed that the getGroceryData() function (inside my grocery-data.ts provider) had the following line of code at the very beginning:

if (this.data) {
      return Promise.resolve(this.data);
}

This line is essentially saying that if cached data exists, then display the cached data! Removing this line forces the data to be refreshed every time the getGroceryData() function is called, therefore removing this line AND implementing your suggestion of using ionViewDidEnter() (instead of ionViewLoaded()) means that the home page refreshes its data each time it is loaded, thus solving the problem!

It’d be great if I could implement some functionality so the Home page only refreshed its data when it knew it needed to, so I might look into that now. Thanks again for your response!

Hi @SigmundFroyd, unfortunately my “fix” has caused some undesirable results with my deleteStore function on my HomePage. I’m currently forcing the data to be loaded again whenever a user submits or cancels the DetailsPage, which results in the correct data being displayed on the HomePage. However, if I then choose to delete a Grocery Store (which occurs in a function on the HomePage) then often deleting a single item will result in several stores “disappearing” from view. Checking the database shows that only the one store has been deleted (which is the expected behaviour), therefore I’m not sure why the other values are disappearing. The only way to resolve this issue is to navigate away from the page, then back to it.

Ideally I’d like for this not to happen but I can’t really work out how to prevent it. Any ideas?

Alrighty! First, apologies for my delayed response, I was in no-internet land over the weekend.

I’ve looked over the code once again, as well as explored your GroceryDataClass, and I think the issue is that every time getGroceryData() is called you do set up a this.db.changes handler. Which was fine when you were caching data, as I believe the issue now is that you wind up having multiple handlers. (Resulting in your data array removing multiple items)

Rather than messing with all of that, I’m wondering if the best solution is to simply revert (i.e. add back in caching and wot) to your original setup and instead modify HomePage, specifically the update function.

So change this:

  navGroceryStoreUpdate(groceryData): void {

    this.navCtrl.push(GroceryStoreDetailsPage, {
      groceryParamPageAction: 'Edit',
      groceryParamDB_id: groceryData._id,
      groceryParamDB_rev: groceryData._rev,
      groceryParamStoreDateCreated: groceryData.g_store_date_created,
      groceryParamStoreName: groceryData.g_store_name,
      groceryParamStoreDescription: groceryData.g_store_description,
      groceryParamStoreProducts: groceryData.g_store_products
    });
  }

to this:

  navGroceryStoreUpdate(groceryData): void {

    this.navCtrl.push(GroceryStoreDetailsPage, {
      groceryParamPageAction: 'Edit',
      groceryParamDB_id: groceryData._id,
      groceryParamDB_rev: groceryData._rev,
      groceryParamStoreDateCreated: groceryData.g_store_date_created,
      groceryParamStoreName: groceryData.g_store_name,
      groceryParamStoreDescription: groceryData.g_store_description,
      groceryParamStoreProducts: groceryData.g_store_products.slice() //This is the important bit
    });
  }

So basically, when you edit a grocery store and you modify the products, you’ll be modifying a copy of the products and not the products themselves. Which would result in everything reverting fine when a user hits cancel, but when a user hits submit it’ll update fine as all of your DB watcher logic will kick-in.

1 Like

Brilliant - this has worked a treat :grin:

Thanks so much for your assistance. I had actually tried something similar on the DetailsPage by trying to create another array and making its contents the same as the g_store_products array, however it wasn’t working as desired because (I think) the two-way binding seemed to be applied to my new array also, so it just behaved the same as the g_store_products array.

Thanks again for your help, this has saved me some headache!!! :+1: