1

I am new to angular, looked up relevant questions but could not find anything useful. I hope someone can offer some insight as I spent an entire day on this to no avail: I am trying to use resolve to get data using $resource and ui-router for my applciation. The problem is that the call to the service method never gets made (the call is defined int eh resolve property') although the state seems to load fine. (The state actually has a ui-grid so currently the grid appears empty). The application is using a json-server to provide data for now , the server does not show any http request. Please find some relevant code below: The state provider config:

angular.module('carsApp', ['ui.router', 'ngResource', 'ui.bootstrap', 'ui.grid'])
  .config(function($stateProvider, $urlRouterProvider) {
    $stateProvider
      // abstract state for 'car' state. Creates view template with two sub tabs.

      .state('app.main.car', {
        url: "",
        abstract: true,
        views: {
          // the child template (Absolutely named)
          'contentbar@app.main': {
            templateUrl: 'views/crid.html',
            controller: 'GridController'
          },

          'subTabs@app.main': {
            templateUrl: 'views/cars.html',
            controller: 'CarController'

          }
        },
        resolve: {
          // create an Object property called "cars"
          // which will later be used for Dependency Injection
          // inside our Controller. Inject any Services required to resolve the data
          cars: ['gridFactory', function(gridFactory) {
            // Return Service call, that returns a Promise
            return gridFactory.getCars();
          }],
          dealer: function() {
            return {};
          }
        }
      })

      .state('app.main.car.all', {
        url: "car/summary",
        views: {

          'subTabView@app.main.car': {
            templateUrl: 'views/summary.html'

          }
        },
        resolve: {
          cars: ['gridFactory', function(gridFactory) {
            // Return Service call, that returns a Promise
            return gridFactory.getCars();
          }],
          dealer: function() {
            return {};
          }
        }
      });

    $urlRouterProvider.otherwise('/');
  });

the service:

.service('gridFactory', ['$resource', 'baseURL', function($resource, baseURL) {

        this.getCars = function() {
          return $resource(baseURL + "cars", null, {
            'query': {
              method: 'GET',
              isArray: true,
              // im setting authroization header here so using this version
              // of query to be able to add headers

            }
          });
        }


      }

the controller:

.controller('GridController', ['$scope', '$stateParams', '$state', 'gridFactory', 'cars', 'dealer', function($scope, $stateParams, $state, gridFactory, cars, dealer) {

        $scope.cars = cars; //i am not sure if this is the correct way to assign. Looking up at 
        //all the blog posts i could, this is what i understood
        $scope.dealer = dealer;

        console.log('scope objects ' + JSON.stringify($scope.cars)); //$scope.cars is undefined here

        //grid related code follows. Not shown here to keep things simple.

        // the grid is apparently working as i was able to get data without using resolve.
        // some context on why i want to use resolve:
        // I want the grid to refresh whenever an entry on the grid is clicked, the corresponding details
        // for that entry are fetched from gridFActory using resource and displayed in the grid again.
        // However, the grid was not being refreshed ( i was using a child controller for a the .all state,
        // i tried every trick on stackoverflow to make it work but giving 'resolve' a go as a final attempt)

      }

I hope someone can offer some insight as to what I am missing. Many thanks for your time!

fah
  • 13
  • 3
  • Do you have a state called `app.main` and `app` in your application ([Reference](https://github.com/angular-ui/ui-router/wiki/Nested-States-and-Nested-Views#parent-must-exist))? – Alon Eitan Apr 22 '17 at 20:15
  • thanks a lot for your response! yes, i do. this is only part of the code, wanted to keep it simple. Also just discovered 'resolve' works differently for nested states so looking into that now. – fah Apr 22 '17 at 20:33

2 Answers2

1

Need to call the query() method and return the $resource promise instead. By default $resource methods return an empty object or array that gets populated after the request completes

resolve: {          
      cars: ['gridFactory', function(gridFactory) {
        // Return Service call, that returns a Promise
        return gridFactory.getCars().query().$promise;
      }],
      dealer: ....

You could also move some of this call into the service method and have it return the query().$promise

DEMO

charlietfl
  • 170,828
  • 13
  • 121
  • 150
  • Thanks a lot for your response! right! I will try this and get back. – fah Apr 22 '17 at 20:36
  • I also had to inject $promise as follows. Is this the standard way or should I not have done this? cars_parent: ['gridFactory','$promise', function (gridFactory, $promise) { // Return Service call, that returns a Promise return gridFactory.getCars().query().$promise; }], – fah Apr 22 '17 at 21:39
  • No...here's a basic working version. Not abstract state though, just a basic state. http://plnkr.co/edit/5LaXWy2LqCUUwr1pM03r?p=preview – charlietfl Apr 22 '17 at 21:48
  • ok...note that a resolve in parent state is already available in child state. You don't need to resolve same thing again – charlietfl Apr 22 '17 at 22:00
  • Thanks! right! I keep getting this error (that's why i tried to 'inject' the 'promise' but even then it didnt go away) Error: [$injector:unpr] Unknown provider: carsProvider <- cars <- GridController – fah Apr 22 '17 at 22:16
  • can you add another state to my demo to reproduce error? – charlietfl Apr 22 '17 at 22:24
  • Resolved the issue by using 'query' and also by removing 'resolve' from the child states based on your comment about not needing to resolve the same thing in child! Thank you so much!! My resolve is now able to communicate with the server using resource. P.S :the unkown provider was an incorrect dependency injection issue, resolved it finally! (Tried to add a state to the plunkr btw but couldnt get it to work as new to plunkr, sorry about that) – fah Apr 22 '17 at 23:24
  • hey...glad it worked out. Angular itself has steep learning curve and ui-router doubles that – charlietfl Apr 22 '17 at 23:26
-1

I suppose problem in your factory get cars method:

Try following:

 .service('gridFactory', ['$resource', 'baseURL', function($resource, baseURL) {

    this.getCars = function() {
      var cars =  $resource(baseURL + "cars", null, {
        'query': {
          method: 'GET',
          isArray: true,
          // im setting authroization header here so using this version
          // of query to be able to add headers

        }

       return new Promise(function(resolve, reject){

          var result = [];

          cars.query(function (response) {
             angular.forEach(response, function (item) {
               result.push(item);
             }
          }

          resolve(result);

        })

       })

      });
    }


  }

I think this question is related to yours

Community
  • 1
  • 1
Leguest
  • 2,097
  • 2
  • 17
  • 20
  • this is an anti pattern using a new promise when `$resource` already has promise properties – charlietfl Apr 22 '17 at 20:21
  • Thanks for the link! looking into that! – fah Apr 22 '17 at 20:34
  • @fah that linked answer is full of misleading information – charlietfl Apr 22 '17 at 20:37
  • @charlietfl look closely at my answer, I wrapped in promise some logic on data that $resource returned, e.g. `var result`. That purpose for the services, there is no pattern violation. Bad pattern is resolving something in routing system. IMHO – Leguest Apr 22 '17 at 20:40
  • huh? resolving in router is extremely common and very practical. And why wouldn't you use the `$resource` built in promise? There is no need to iterate through the response array just to pass it to another array either – charlietfl Apr 22 '17 at 20:42
  • I just provided an example, so, his has a `getCars` method, in that method he maybe can have multiple $resource for whatever reason, so promise construction helps to arrange data from these 2 resources. Its ok to have that logic in *service*. Its bad to manage data somewhere else in routing or controllers for example. Controllers and routing should only receive proper data from service. Resolving is common I agree with that. But my answer also provides example of how he can manipulate with data in his getCars method. – Leguest Apr 22 '17 at 20:55
  • $resource also has built in transform methods ...have you ever even used $resource? – charlietfl Apr 22 '17 at 21:23
  • fyi - some links about the ant-pattern https://github.com/petkaantonov/bluebird/wiki/Promise-anti-patterns#the-deferred-anti-pattern http://stackoverflow.com/a/23803744/1175966 Can see my demo in answer working using the built in promise. Using that promise can also add an additional `then()` inside service to also do data transforms and return the transformed data to next `then` which is called internally in resolve – charlietfl Apr 22 '17 at 22:32