Data initialization before rendering main view


#1

Hello!

I was trying to migrate my angular + phonegap app into ionic, and i’m kind of stuck on this.

My index.html of my old app looked like this

<div id="angular_main" class="container">
       <ng-include src="'partials/_header.html'"></ng-include>
       <ng-view data-ng-controller="InitController"></ng-view>
       <ng-include src="'partials/_footer.html'"></ng-include>
   </div>

where InitController is doing a bunch of stuff (specially ajax calls, and database read/write)

And now i’m trying to use the nav-bar feature but it doesn’t wait for the InitController to finish and then render the view.

I tried it several ways with no success at all, one of them:

<nav-bar type="bar-positive" back-button-type="button-icon" back-button-icon="ion-ios7-arrow-back"></nav-bar>
<nav-view data-ng-controller="InitController"></nav-view>

Thanks in advance.


#2

You need to consider using resolves in your app config. Basically, you give it a list of stuff that must be done before the app changes the route. So, all of those ajax calls get completed first.

Small Example :

$stateProvider
    .state('friendSummary', {
        url: '/friendSummary/:membersId',
        templateUrl: 'templates/friendSummaryPage.html',
        resolve : {
            memberDetails : function(FamilyService,$stateParams) {
                return FamilyService.getMember($stateParams.membersId);
            }
        },
        controller: 'FriendSummaryController'
    });


#3

LIke @Calendee mentioned, try the resolves stuff, but also consider that waiting to render a view based on data received from the server is usually not a great UX pattern. What would be better is to start the load after the view loads and your controller runs, and showing a loading indicator instead.


#4

Good points @max. In fact, a few days ago, I killed several resolves that were slowing my app down too much. It felt very laggy while trying to switch views.

Instead, I’m caching the data from the prior load and serving it right away. Then, after the state change, I reload the data and update the cache. It’s a lot more responsive this way.


#5

Thanks for your help.

The point is that this is only for starting the application, so it’s done only once. I need to know if the user has a valid authentication token, setting the databases, load some config files etc… So i don’t know if there is a better way of doing that.

I will try what mentioned above. Thanks a lot again.


#6

I would use the stateChangeStart to globally check if the user has a token stored locally:

    .run(function ($rootScope, $location, AccountService) {

        $rootScope.$on('$stateChangeStart', function (event, next, current) {

            // if route requires auth and user is not logged in
            if ( $location.url() !== '/intro' && !AccountService.hasToken()) {

                // redirect back to login
                return $location.path('/intro');
            }

        });

    })

The code above will have the AccountService check if a token is stored locally for all routes except ‘/intro’. You might have it does this for all routes except ‘/login’. If the token is not available, it will redirect to ‘/intro’ or ‘/login’.

I would NOT actually check the validity of their token until they do something via the API (GET, PUT, POST). However, that really depends on the sensitivity of the data and your security requirements. For example, you may not want them to see the existing content stored on the device if the token was revoked. However, once device is in hand, I think the cat is out of the bag.


#7

I haven’t think about this approach. I will give it a try, it could simplify a lot my code, and get rid of many not that nice functions.
I made it work anyway with this resolve instruction. Thanks a lot for your time.


#8

Do you have an example how to use the loading features in this scenario?


#9

How about this:


#10

I was able to set it up in a service and get it to work with $resource. Thanks for your help!


#11

@Calendee

I recently added a cachfactory to my resource for you reasons you stated. However, I am currently only updating on a manual pull to refresh. I like your idea here though of delivering the existing data from cache and updating the data in the background.

My question is, how do you keep the existing data displayed and update the cache without removing all the items in it?

I’m guessing I’m to a point where I need a model layer.


#12

You “could” watch the results of the cache reloading in the controller… However, I think that gets expensive. I’d say your best bet is to use a broadcast. When the service reloads from the API, it can broadcast “cache.reloaded”. Your controller can listen on that and then request the new data.


#13

Do you think its bad practice to actually send the updated data through the broadcast instead of having the controller request the updated data?

Thanks!


#14

I’m far from a “good” developer and certainly not a purist. If you are only going to get new data when a controller wants it, then let the controller manage the interaction with the service. If the service will be refreshing the data on a timer say, then I feel using a $scope.$on in the controller to listen for the service announcing new data is the way to go.


#15

Thanks man. You saved me.