620
Views
4
Comments
Solved
Screen Events and Data Actions
Application Type
Mobile

Hi there Outsystems community.

I have a question regarding mobile screen events and data actions. I have a certain screen flow where a user inputs information and can move on to other screens. One of those screens is merely informative, and does not allow the user to input any other information. As such, the user can return to the previous screen to continue inputting data.

The problem with this flow, is that when the user returns to the previous screen, the data fetch actions which are set to be fetched at start will run again, effectively overwriting the information the user had previously inserted.

The easiest solution to this would be to just simply add a screen variable to control whether the onAfterFetch action should do its job, or skip. This of course is not optimal, since it actually performs an unnecessary server call.

The other solution would be to set the data action to fetch data on demand and verify if the data needs to be fetched on one of the screen events. Looking at the documentation, the onInitialize event would be a great fit for this, however, explicit data fetching is not asynchronous, and will block the entire screen loading flow. The boilerplate comment on the onInitialize even states that a developer should not use this event to perform server calls. Granted, this would only happen on the first screen load (which already shows a loading widget which blocks the user's view anyway), but it still does not seem very optimal.

So, my question is: how can I, following best practices, optimize this loading process, and maybe also apply this pattern to other screens to avoid unnecessary data fetching when the user navigates to previous screens?

2019-07-08 11-04-35
Leonardo Fernandes
 
MVP
Solution

Hi Ricardo.

Having the data fetch actions set to fetch at start is almost the same as having them on demand and refreshing them during OnReady. The only difference is, if you have multiple data actions fetching at start, they would execute in parallel, and if you refresh all of them during OnReady, they should execute in sequence (although there are ways to execute them in parallel using JavaScript).


With that in mind, I suggest the following approach:

1. Make sure there's a single data action in the screen.

2. Set it to execute on demand.

3. At the start of the OnReady, execute the JavaScript function wasCurrentViewRestoredFromCache(), see https://success.outsystems.com/documentation/11/reference/outsystems_apis/javascript_api/view/#wascurrentviewrestoredfromcache to detect whether the screen/block has its values restored from the cache.

4. If that's the case, you can skip the refresh of the data action. Otherwise, you need to refresh the data action to load the initial values of the screen.


I don't remember now if refreshing data actions/aggregates in OnReady generates a warning, but even if it does you can ignore it because it won't cause any performance problems to the screen.


Note that the function navigatedFromHistory(), see https://success.outsystems.com/documentation/11/reference/outsystems_apis/javascript_api/navigation/#navigatedfromhistory might achieve similar results. However we found that there are some edge cases where the wasCurrentViewRestoredFromCache() is more accurate. For example, if a user in module A navigates to a screen belonging to module B, and then navigates back to A using the browser back button, this navigation will be made from the history, but the screen will not be restored from the cache. The reason is that the cache information is maintained in JavaScript, and navigating between modules reloads the JavaScript runtime (i.e. it's not an SPA navigation). Another edge case is if the user presses the reload button of the browser, or if the application upgrades to a new version during the navigation. In all of these cases, the JavaScript runtime is reloaded, and while the user can still do history navigations, they will not be restored from the cache.

There were more edge cases not related to reloading the JavaScript runtime, but related to allowing the user to show/hide blocks. For example, imagine screen A has 3 tabs X, Y, Z, and each tab has a block inside of an if such that it loads only when the user selects that tab. Then if a user selects tab X, then navigates to screen B and back to screen A, the tab X will be selected and restored from cache, so far so good. But if the user selects tab Y, and the block on tab Y checks navigatedFromHistory() to bypass the data fetch, it will return true because the screen A was navigated from history. But the data in tab Y was not restored during that navigation, so it's incorrect to skip the data fetch. If the block on tab Y checks wasCurrentViewRestoredFromCache() then it will return false.


One last point, being the smart person which I'm sure you are, you will try to refactor the wasCurrentViewRestoredFromCache() call into a client action, to be reused in many screens/blocks. Don't do that as it will not work. The wasCurrentViewRestoredFromCache() function needs to be called directly in the context of the screen/block for it to work, otherwise it will always return false.

I believe that's the only JavaScript API with that behaviour though. So if you don't care about the edge cases I mentioned previously, then you could use navigatedFromHistory() as a solution that will have the same effect, and this JavaScript API can be refactored into a client action without problems.

UserImage.jpg
Ricardo Mata

Hi Leonardo.

This seems to be the best approach. I guess I misinterpreted the warning on the onReady autogenerated code, which warns against using data actions, since I automatically assumed that it would be a bad idea to also refresh data actions. Clearly reading it again it is just warning against trying to access data which might not yet be accessible.

As for the NavigatedFromHistory, luckily the navigation on this flow is contained within a single module, so that edge case wouldn't apply.

I will be sure to give it a try. Thanks.

2020-08-05 08-52-58
Ruben Goncalves

Hi @Ricardo Mata,

Allow me to start with some contextual questions! (bear with me)

  1. In the follow, the user is having to click on the "back" button of the browser?
    (As in, fill up data in screen1, navigate to screen2, then hit back to screen1?)
  2. How is the user navigating between screens?
  3. It is not possible to auto-save the data in the screen1?

I know that I'm not giving any answer, but this context will help to better understand the scenario, and with that share additional thoughts.

Cheers,

RG

UserImage.jpg
Ricardo Mata

Hi Ruben

  1. The follow-up screen has a button to allow the user to return to the previous screen. The user can also return to the previous screen using the device's own "back" button. This navigation is achieved using the "(Previous Screen)" option in the destination widget.
  2. In this particular case the user navigates to the new screen via clicking a link.
  3. It is, but that would require server calls, which is exactly what i'm trying to minimize. 

Cheers

2019-07-08 11-04-35
Leonardo Fernandes
 
MVP
Solution

Hi Ricardo.

Having the data fetch actions set to fetch at start is almost the same as having them on demand and refreshing them during OnReady. The only difference is, if you have multiple data actions fetching at start, they would execute in parallel, and if you refresh all of them during OnReady, they should execute in sequence (although there are ways to execute them in parallel using JavaScript).


With that in mind, I suggest the following approach:

1. Make sure there's a single data action in the screen.

2. Set it to execute on demand.

3. At the start of the OnReady, execute the JavaScript function wasCurrentViewRestoredFromCache(), see https://success.outsystems.com/documentation/11/reference/outsystems_apis/javascript_api/view/#wascurrentviewrestoredfromcache to detect whether the screen/block has its values restored from the cache.

4. If that's the case, you can skip the refresh of the data action. Otherwise, you need to refresh the data action to load the initial values of the screen.


I don't remember now if refreshing data actions/aggregates in OnReady generates a warning, but even if it does you can ignore it because it won't cause any performance problems to the screen.


Note that the function navigatedFromHistory(), see https://success.outsystems.com/documentation/11/reference/outsystems_apis/javascript_api/navigation/#navigatedfromhistory might achieve similar results. However we found that there are some edge cases where the wasCurrentViewRestoredFromCache() is more accurate. For example, if a user in module A navigates to a screen belonging to module B, and then navigates back to A using the browser back button, this navigation will be made from the history, but the screen will not be restored from the cache. The reason is that the cache information is maintained in JavaScript, and navigating between modules reloads the JavaScript runtime (i.e. it's not an SPA navigation). Another edge case is if the user presses the reload button of the browser, or if the application upgrades to a new version during the navigation. In all of these cases, the JavaScript runtime is reloaded, and while the user can still do history navigations, they will not be restored from the cache.

There were more edge cases not related to reloading the JavaScript runtime, but related to allowing the user to show/hide blocks. For example, imagine screen A has 3 tabs X, Y, Z, and each tab has a block inside of an if such that it loads only when the user selects that tab. Then if a user selects tab X, then navigates to screen B and back to screen A, the tab X will be selected and restored from cache, so far so good. But if the user selects tab Y, and the block on tab Y checks navigatedFromHistory() to bypass the data fetch, it will return true because the screen A was navigated from history. But the data in tab Y was not restored during that navigation, so it's incorrect to skip the data fetch. If the block on tab Y checks wasCurrentViewRestoredFromCache() then it will return false.


One last point, being the smart person which I'm sure you are, you will try to refactor the wasCurrentViewRestoredFromCache() call into a client action, to be reused in many screens/blocks. Don't do that as it will not work. The wasCurrentViewRestoredFromCache() function needs to be called directly in the context of the screen/block for it to work, otherwise it will always return false.

I believe that's the only JavaScript API with that behaviour though. So if you don't care about the edge cases I mentioned previously, then you could use navigatedFromHistory() as a solution that will have the same effect, and this JavaScript API can be refactored into a client action without problems.

UserImage.jpg
Ricardo Mata

Hi Leonardo.

This seems to be the best approach. I guess I misinterpreted the warning on the onReady autogenerated code, which warns against using data actions, since I automatically assumed that it would be a bad idea to also refresh data actions. Clearly reading it again it is just warning against trying to access data which might not yet be accessible.

As for the NavigatedFromHistory, luckily the navigation on this flow is contained within a single module, so that edge case wouldn't apply.

I will be sure to give it a try. Thanks.

Community GuidelinesBe kind and respectful, give credit to the original source of content, and search for duplicates before posting.