Search code examples
angularjspromiseangularjs-service

Angular JS: Fetching the data from server before the view has been shown to the user


My config looks like the following:

app.config(function ($routeProvider) {
    $routeProvider
        ...
        .when('/project/:slug', {
            templateUrl: 'partials/plaintasks-part.php',
            controller: 'ProjectCtrl',
            resolve : {
                projectDetail : ProjectCtrl.loadProject
            }
        })
        ...
});

And loadProject is as follows:

// Project controller
var ProjectCtrl = app.controller('ProjectCtrl', ...);

ProjectCtrl.loadProject = function( $q, Tasks, $route ){

    var defer = $q.defer();

    var slug = $route.current.params.slug;
    // Tasks.getProjectBySlug() is where I have implemented the http request to get the data from server
    var project = Tasks.getProjectBySlug( slug );
    var tasks = Tasks.getProjectTasks( project.id );

    defer.resolve({ 
        tasks : tasks,
        project : project
    });

    return defer.promise;
}

Now the problem is, it doesn't wait for the projects and tasks to be fetched from the server and simply shows the view which I don't want unless the data is fetched from the server. Can anyone please tell me, what am I doing wrong here? Why doesn't it wait for the data to be fetched although I have implemented the resolve in my route?


Solution

  • There are two things wrong in your code:

    • You have an excess deferred (no need for that $q.defer() there, it's the deferred anti pattern.
    • You are not waiting for the promises to resolve when you resolve that deferred.

    Since getProjectTasks is not an HTTP call according to the question, it should not return a promise unless there is potential for it to make an HTTP request. If it makes an HTTP request - please write a method on your backend that gets a slug and returns the project and its tasks as the overhead of making two round trips is really expensive.

    Then, you can reduce loadProject to:

    ProjectCtrl.loadProject = function( $q, Tasks, $route ){
         return Task.getProjectBySlug($route.current.params.slug).then(function(project){
              return {tasks:Tasks.getProjectTasks(project.id), project: project};
         });
    };
    

    If you absolutely must make two calls, you can still do:

    ProjectCtrl.loadProject = function( $q, Tasks, $route ){  
        var getProject = Task.getProjectBySlug($route.current.params.slug);
        var getTasks = p.then(function(project){ return Tasks.getProjectTasks(project.id);});
        return $q.all([getTasks,getProject]).then(function(project,tasks){
             return {tasks:tasks,project:project};
        });
    }
    

    Which still avoids the nesting.