Prevent controller from reloading/rebuilding on state change

I’m fighting with this half of my day and I’ve just given up. Maybe some of you, my colleagues, did this in the past and could share experience? The situation is this.

I’ve got an app build upon the sidemenu template. One of the views is a screen with a big list created with ng-repeat. It takes some time for its controller to build it so imagine my frustration when it is rebuilt each time I switch to another view/state in my app. It is rebuilt though nothing has changed in the data. What a waste of time for users and cpu. It is enough to switch to another view, then come back again and everything is lost and all calculations are starting over; data entered in form is lost (yes, I know I can remember that, but why should I?), scroll is lost, whole time spent on building the list is lost.

I think there must be some way to preserve the contents of the screen/view/state and not waste time for building it over and over again. Please help me with this. I’m out of ideas and all tricks I’ve found on stackoverflow don’t work or I can’t follow them.

Thank you very much in advance. This is the last thing that keeps me out of releasing my first app :slight_smile:

Rafal

P.S. Please don’t advice collection-repeat or infinite-scroll. They’re not suitable in my case.

1 Like

There could be a number of ways to speed this up. Is your long list of data items being re-fetched every time that view is presented? If so, you might offload that data handling into a service, which will fetch (and cache) your data for you.
If the data is expected to remain static once loaded, have considered using something like bind-once?
Is there any particular reason why infinite-scroll is not suitable for your case? All other things being equal, loading fewer items into your list at one time is always going to be faster than loading the entire list.

That’s why I’ve added post scriptum :slight_smile:

  • rendering is optimized with different approaches (bindonce,
    ng-show, etc. tried iscroll, datagrid, angular-once, infinite,
    virtual, etc) - nothing else faster than it is now is possible
    on the target devices

  • yes, I utilize services for data

I just need to stop the controller reloading everything each time which takes ages to render while it should be possible and enough to just render it once and that would be enough. If this is impossible then there’s no way for me to stay with hybrid apps.

Since I’ve started the journey with hybrid, cordova, ionic and angular I spend my time on nothing else than optimizing over and over things that should be in the package while I could focus on usability and features. I’ve compared my optimized solution to a native one. My app loads in 3 secs on a device and this is even without populating the ng-repeat list what I do only when a state is changed for the first time. Otherwise no one would wait for so long. Similar app, native one, loads in 1 sec on the same device with the whole list populated at once. The idea with stopping controller from reloading content that is not changed at all and doesn’t require reloading is the last chance. Then I give up and I go and check xamarin or something else. Nothing can be more pain probably… :wink:

Regards
Rafal

If you want a controller to stay loaded, you would have to keep the template it’s controlling loaded (rendered). You might be able to accomplish your need using nested states, where the main state (rendering your intensive template/controller) stays loaded. But that may not work for your UI. Hard to make specific suggestions without seeing the code. Maybe if you post a codepen someone will have an idea for you.

The code is not much different than the base sidemenu template. One of the views is static, one is a ng-repeat list, there’s also a service. The route hasn’t been changed and is pretty much the same as in the sidemenu template.

Hello. I am solving same problem as @rafaellop
I am new to Ionic, so i still learn and was thinking that i will find solution.

Reason why i need this is simple. I have inifinite list (image Twitter app), where you scroll to next posts. It loads 10times new data and add them to list when scroll. Then you click to show detail of one post you like and when go back BOOOM you are lost!

@mbuster can you make Codepen for your suggested solution? Imagine simple app with List and Detail.

My solution to this is to just use a full-screen modal for the “Detail” view. That way the underlying list is undisturbed and when you close the modal, you’re right back where you were.

@mhartington describes here: Ionic modal fullscreen mode how to force the modal to always be full-screen.

I had the same issues with reloads on switch from overview to detail page and back. Additionally my app has different “channels” which when browsing would load over and over again to. Solved it with a combination of the following:

  • All HTTP calls are cached with angulars regular cacher & only calling 10 results at the time (SQL Offset/Limit).
  • Using ionic infinite list & collection-repeat (fixed height is a pain but a major performance boost).
  • The templates references a different scope element than the the one HTTP calls are interacting with, e.g. $scope.cachedMessages instead of directly $scope.messages.
  • Before switching UI states, the current $scope.cachedMessages is dumped into an “cache-array” inside a service.
  • When switching UI states the app checkes: Do we have a cached messages for this view? if so, load that instead of http calls.

Anyways, you´ll get the picture. With all this in place i´m getting great performance and keeping http API interactions to a minimum.

i have cached the long list data and whenever user navigates aways from the list and going back, it can remember the scroll position instead of scrolling to the top, in my Android version only… but NOT in iOS, in iOS, it just scrolls to top whenever going back to the list. Both versions have the same piece of codes at that part…

I found solution to stop list reloading on state change. I have infinite scroll, but it can work for normal list. Normally you define empty list then you fill it.

// ProductsCtrl
$scope.products=[]; 
$scope.loadMore=function() {  MyService.loadProducts().then(products){   
    products.forEach(function(product){
        $scope.products.push(product);
    });
}}

Solution is to put list in $rootScope and define it in global AppCtrl:

// AppCtrl
$rootScope.products=[]; 

And

// ProductsCtrl
$rootScope.loadMore=function() {  MyService.loadProducts().then(products){   
    products.forEach(function(product){
        $rootScope.products.push(product);
    });
}}
// if you have normal list you call:
// if($rootScope.products.length==0) $rootScope.loadMore();
// if you have infinite list function will be called automatically

When you enter some other view and go back (state changes), all stuff in ProductsCtrl is executed again, but if $rootScope.products is allready filled with data, loadMore() will not be triggered.