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'});
  }]);
Georgi Angelov
  • 4,338
  • 12
  • 67
  • 96
  • `appData` is not a module. You don't need to inject it. – zs2020 Dec 18 '13 at 14:54
  • hmm quite the contrary. I need to inject it, otherwise is not recognized. I've used it before. – Georgi Angelov Dec 18 '13 at 14:56
  • You can inject `MainFactory` but not a function. – zs2020 Dec 18 '13 at 14:58
  • I disagree. You can certainly inject it. Take a look at the example I provided right after my code. – Georgi Angelov Dec 18 '13 at 15:17
  • 1
    I agree, I inject my resolved parameters into my controller, works just fine. – m.e.conroy Dec 18 '13 at 15:32
  • @GeorgiAngelov I'm using Angular 1.1.5 in the application I'm working on. – m.e.conroy Dec 18 '13 at 15:38
  • @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. – m.e.conroy Dec 18 '13 at 16:18
  • 2
    @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. – Georgi Angelov Dec 18 '13 at 16:54
  • 1
    @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 :) Thanks – Jack Vo Oct 24 '14 at 05:18
  • 2
    @JackVo, I am glad that it helped! It seems like something that shouldn't be causing so much problems ;) – Georgi Angelov Oct 24 '14 at 14:43
  • 1
    Solution point 2 helped me out using angularjs v1.3.2 – Sam Vloeberghs Nov 14 '14 at 08:57
  • 1
    @SamVloeberghs, I am really glad it helped – Georgi Angelov Nov 15 '14 at 20:07
  • 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. – davidkonrad Feb 21 '18 at 15:36
  • @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! – Georgi Angelov Feb 25 '18 at 05:16

3 Answers3

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.

m.e.conroy
  • 3,508
  • 25
  • 27
  • 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.. – Georgi Angelov Dec 18 '13 at 16:10
  • 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 `ngRoute` – m.e.conroy Dec 18 '13 at 16:14
  • I just posted the solution to this problem. – Georgi Angelov Dec 18 '13 at 17:19
  • 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. – El pocho la pantera Dec 16 '16 at 13:38
  • I've never had a problem doing it the way I described, I have large scale applications written as such that work just fine. – m.e.conroy Dec 16 '16 at 16:10
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.

Georgi Angelov
  • 4,338
  • 12
  • 67
  • 96
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
}
Daniel Lizik
  • 3,058
  • 2
  • 20
  • 42