Using resolve in $routeProvider causes 'Unknown provider ...'
Asked Answered
C

3

20

I am trying to do an asynchronous http request to load some data before my app loads and so I am using a resolve in $routeProvider which is an http request in my MainController. For some reason, I keep getting Error: [$injector:unpr] Unknown provider: appDataProvider <- appData where appData is where I do my http request. I am using AngularJS v 1.2.5.

Here is the code and two methods that I tried that both give the same error:

Method #1

MainController.js

var MainController = ['$scope','$location','appData',
    function($scope, $location, appData){
       console.log(appData.data);
    }
];

MainController.loadData = {
    appData: function($http, $location, MainFactory){
        var aid = MainFactory.extractAid($location);
        return $http({method: 'GET', url: URL_CONST + aid});
    }
};

app.js

var app = angular.module('HAY', ['ngRoute']);

app.config(function($routeProvider) {
  $routeProvider
    .when('/', {
      redirectTo: '/pages/alerts'
    })
    .when('/pages/:pageName', {
        templateUrl: function(params) {
            return 'views/pages/' + params.pageName + '.html';
        },
        controller: MainController,
        resolve: MainController.loadData
    })
    .otherwise({
        redirectTo: '/pages/alerts'
    });
});

I tried changing the name in case it was a conflicting system reserved keyword but with no luck. For some reason, appData is never recognized

Method #2 I also tried changing it around like so:

app.js

var app = angular.module('HEY', ['ngRoute']);

app.config(function($routeProvider) {
  $routeProvider
    .when('/', {
      redirectTo: '/pages/alerts'
    })
    .when('/pages/:pageName', {
        templateUrl: function(params) {
            return 'views/pages/' + params.pageName + '.html';
        },
        controller: MainController,
        resolve: {
                appData: ['$http', '$location','MainFactory', function($http, $location, MainFactory) {
                    var aid = MainFactory.extractAid($location);
                    return $http({method: 'GET', url: URL_CONST + aid});
                }]
        }
    })
    .otherwise({
        redirectTo: '/pages/alerts'
    });
});

MainController.js

var MainController = ['$scope','$location','appData',
    function($scope, $location, appData){
        console.log(resolvedData);
    }
];

However, the result was exactly the same. Does this have something to do with angular 1.2.5 ?

Here is a working version from someone else

http://mhevery.github.io/angular-phonecat/app/#/phones

And here is the code:

function PhoneListCtrl($scope, phones) {
  $scope.phones = phones;
  $scope.orderProp = 'age';
}

PhoneListCtrl.resolve = {
  phones: function(Phone) {
    return Phone.query();
  },
  delay: function($q, $defer) {
    var delay = $q.defer();
    $defer(delay.resolve, 1000);
    return delay.promise;
  }
}

angular.module('phonecat', ['phonecatFilters', 'phonecatServices', 'phonecatDirectives']).
  config(['$routeProvider', function($routeProvider) {
    $routeProvider.
        when('/phones', {templateUrl: 'partials/phone-list.html', controller: PhoneListCtrl, resolve: PhoneListCtrl.resolve}).
        otherwise({redirectTo: '/phones'});
  }]);
Clatter answered 18/12, 2013 at 14:50 Comment(14)
appData is not a module. You don't need to inject it.Donnadonnamarie
hmm quite the contrary. I need to inject it, otherwise is not recognized. I've used it before.Clatter
You can inject MainFactory but not a function.Donnadonnamarie
I disagree. You can certainly inject it. Take a look at the example I provided right after my code.Clatter
I agree, I inject my resolved parameters into my controller, works just fine.Lathi
@GeorgiAngelov I'm using Angular 1.1.5 in the application I'm working on.Lathi
@GeorgiAngelov is the $http call returning what you expected? Try using Chrome's developer tools and look at the Network tab and see what that call is returning. Maybe something is going awry there and messing up the expected value of the promise.Lathi
@m.e.conroy, the solution was simply ridiculous. Please see the solution I posted. I hope it helps someone else who may have the same problem.Clatter
@GeorgiAngelov the second thing that you point out has helped me solve the problem. I completely forgot that I have set ng-controller to the main body as well :) ThanksCoppinger
@JackVo, I am glad that it helped! It seems like something that shouldn't be causing so much problems ;)Clatter
Solution point 2 helped me out using angularjs v1.3.2Discomfit
@SamVloeberghs, I am really glad it helpedClatter
You should answer your own question with the above findings / details! For me it turned out to be a reference to the controller in a ng-include. Exactly as you mention above, referring to the controller causes the "Unknown provider" error, apparently also for partials / includes. The solution was to give the include its own controller, then every thing works.Insistence
@davidkonrad, I am glad it helped! I just posted my own answer so I hope that's a bit more clear to people looking at this question. Thanks!Clatter
C
1

The problem was NOT due to previously using different version of AngularJS.

Here are the fixes using the code that I have above.

In app.js, you need to declare the controller as controller: 'MainController' and NOT as controller: MainController even though you have var MainController = app.controller('MainController', ....).

Second and biggest thing was that in my index.html I declared my controller already like so:

index.html

body ng-app="HEY" controller="MainController" /body

This was causing the whole Unknown provider error Apparently angular wont tell you that you have already declared the controller that you are using to do the resolve it and that that will cause a weird error that have nothing to do with the resolve.

I hope this helps someone who may have the same problem.

Clatter answered 25/2, 2018 at 5:14 Comment(0)
L
2

Here's an example of the code I've used in the application I'm working on, not sure it will help much because its not much different than how you have it already.

Routing

.when('/view/proposal/:id',{
    controller : 'viewProposalCtrl',
    templateURL : 'tmpls/get/proposal/view',
    resolve : viewProposalCtrl.resolveViewProposal
})

Controller

var viewProposalCtrl = angular.module('proposal.controllers')
    .controller('viewProposalCtrl',['$scope','contacts','details','rationale',
        function($scope,contacts,details,rationale){
            $scope.contacts = contacts;
            $scope.details = details;
            $scope.rationale = rationale;

            // [ REST OF CONTROLLER CODE ]
        });


// proposalSrv is a factory service

viewProposalCtrl.resolveViewProposal = {
    contacts : ['$route','proposalSrv',function($route,proposalSrv){
        return proposalSrv.get('Contacts',$route.current.params.id)
           .then(function(data){
               return data.data.contacts;
           },function(){
               return [];
           });
    }],
    details : ['$route','proposalSrv',function($route,proposalSrv){
        return proposalSrv.get('Details',$route.current.params.id)
            .then(function(data){
                return data.data.details;
            },function(){
                return {};
            });
    }],
    rationale : ['$route','proposalSrv',function($route,proposalSrv){
        return proposalSrv.get('Rationale',$route.current.params.id)
            .then(function(data){
                return data.data.rationale;
            },function(){
                return {};
            ]
    }]
};

Now that I think about it, when I was developing my application I did have a problem and not sure why when I named my resolve function "resolve." This gave me a problem:

.when('/path',{
     // stuff here
     resolve : myCtrlr.resolve
})

but this did not:

.when('/path',{
     //stuff here
     resolve : myCtrlr.myResolveFn
})

Another Possibility

The only other thing I can think of, is that you're returning the promise from the $http call and then trying to use appData.data Try using the .then function or one of the other functions (.success,.error) to retrieve the information from the promise.

Lathi answered 18/12, 2013 at 15:57 Comment(5)
Thanks for this example. My problem is that I cannot even inject the functions to actually use the then() or .success,.error) . I still cannot figure out why. I am beginning to think that angularjs 1.2.5 is doing something differently than before..Clatter
That's a possibility, a lot has changed since 1.1.5. Perhaps you're missing a module or something, maybe something was factored out of the core code much like ngRouteLathi
I just posted the solution to this problem.Clatter
This design has a problem, if you do: var controller = angular.module().controller(), with two different controllers, both variables are going to be bind to the same object. Therefore, you can't have two controllers with resolveXX() methods called the same, or only the last of them will be executed on every page.Harp
I've never had a problem doing it the way I described, I have large scale applications written as such that work just fine.Lathi
C
1

The problem was NOT due to previously using different version of AngularJS.

Here are the fixes using the code that I have above.

In app.js, you need to declare the controller as controller: 'MainController' and NOT as controller: MainController even though you have var MainController = app.controller('MainController', ....).

Second and biggest thing was that in my index.html I declared my controller already like so:

index.html

body ng-app="HEY" controller="MainController" /body

This was causing the whole Unknown provider error Apparently angular wont tell you that you have already declared the controller that you are using to do the resolve it and that that will cause a weird error that have nothing to do with the resolve.

I hope this helps someone who may have the same problem.

Clatter answered 25/2, 2018 at 5:14 Comment(0)
M
0

One thing I noticed in angular 1x docs is that YOU DO NOT SPECIFY THE RESOLVED PARAMETER AS AN ANNOTATED DEPENDENCY

So this:

.when('/somewhere', {
  template: '<some-component></some-component>',
  resolve: {
    resolvedFromRouter: () => someService.fetch()
  }
})

export default [
  '$scope',
  'someService',
  'resolvedFromRouter'
  Controller
]

function Controller($scope, someService, resolvedFromRouter) {
  // <= unknown provider "resolvedFromRouter"
}

is wrong. You don't specify the resolved parameter as a dependency, in the docs:

For easier access to the resolved dependencies from the template, the resolve map will be available on the scope of the route, under $resolve (by default) or a custom name specified by the resolveAs property (see below). This can be particularly useful, when working with components as route templates.

So just do this instead:

.when('/somewhere', {
  template: '<some-component></some-component>',
  resolve: {
    resolvedFromRouter: () => someService.fetch()
  }
})

export default [
  '$scope',
  'someService',
  Controller
]

function Controller($scope, someService) {
  $scope.$resolve.resolvedFromRouter; // <= injected here
}
Meryl answered 8/2, 2017 at 18:16 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.