20

New to AngularJS and can't seem to find out what this error means. I've found a few others with the same error but it seems their issues don't correlate to mine.

Unknown provider: $modalProvider <- $modal error with AngularJS (Seems I've got the latest ui-bootstrap version)

And all of the others seem to be having scope issues with a modal, yet I can't seem to get the modal to begin with so I'm thinking these aren't related. Please tell me if I'm wrong and how that's the case:

Scope issue in AngularJS using AngularUI Bootstrap Modal

Scope issues with Angular UI modal

I grabbed the ui-bootstrap-tpls-0.6.0.min.js script from here: https://github.com/angular-ui/bootstrap/tree/gh-pages#build-files and I even tried adding the ui-bootstrap-0.6.0.min.js script as well thinking it was possibly needed. Though if I read it properly, it seems if I chose the ui-bootstrap-0.6.0.min.js script I'd need to also grab all of the templates here https://github.com/angular-ui/bootstrap/tree/master/template Which seems to be the case if I use only that script based on the errors:

Error: Failed to load template: template/modal/window.html
Error: Failed to load template: template/modal/backdrop.html

I've created a plunker with everything for simplicity of explaining structure etc and pasting in all the code here.

http://plnkr.co/edit/yg3G8uKsaHNnfj4yNnJs?p=preview

The error (which you can see by testing the code on plunker with the console open) is the following:

Error: Unknown provider: $modalInstanceProvider <- $modalInstance
     at Error (<anonymous>)
     at http://run.plnkr.co/8OIh0YtLn1dg9OvR/angular.min.js:30:24
     at Object.c [as get] (http://run.plnkr.co/8OIh0YtLn1dg9OvR/angular.min.js:27:310)
     at http://run.plnkr.co/8OIh0YtLn1dg9OvR/angular.min.js:30:109
     at c (http://run.plnkr.co/8OIh0YtLn1dg9OvR/angular.min.js:27:310)
     at d (http://run.plnkr.co/8OIh0YtLn1dg9OvR/angular.min.js:27:444)
     at Object.instantiate (http://run.plnkr.co/8OIh0YtLn1dg9OvR/angular.min.js:29:80)
     at http://run.plnkr.co/8OIh0YtLn1dg9OvR/angular.min.js:53:80
     at http://run.plnkr.co/8OIh0YtLn1dg9OvR/angular.min.js:44:136
     at m (http://run.plnkr.co/8OIh0YtLn1dg9OvR/angular.min.js:6:494)

If anyone can give any insight as to what I may be doing wrong here. It doesn't seem like a scope issue. More like a setup issue or possibly the way I'm manually bootstrapping the app?

Community
  • 1
  • 1
edward.louis
  • 357
  • 2
  • 5
  • 14
  • 5
    In ui.bootstrap 0.10.0 it seems that the dialog inherits `$scope.$close();` and `$scope.$dismiss(reason)` without needing to inject `modalInstance`. – Mwayi Mar 30 '14 at 03:05
  • So too is anything on scope parent. No need to inject `resolve: {items: function(){return $scope.items}}` as it would already be inherited – Mwayi Mar 30 '14 at 03:22

4 Answers4

34

It seems you are not injecting the $modal service as a dependency.

How to "inject a service"?

Consider the function you are trying to use the service... you should declare it like this:

['$modal', function($modal) {
    // $modal has been injected here, you can use it
}]


Edit:

I've studied you Plunk now... it is overcomplicating simple things, and reveals some miscomprehensions about AngularJS concepts (controller, scope, etc.)

Also, it was using Bootstrap's 3 CSS - which is not compatible with AngularJS Bootstrap currently. I've changed the CSS link to Bootstrap 2.

See how it can be much much more simple and effective: http://plnkr.co/edit/YFuAA5B65NZggd6fxjCh?p=preview

I'd recommend studying this document carefully, from start to finish: http://docs.angularjs.org/guide/concepts

This video is also very very good, but it does not provide deeper insight into the concepts: http://weblogs.asp.net/dwahlin/archive/2013/04/12/video-tutorial-angularjs-fundamentals-in-60-ish-minutes.aspx


Basically, the error message was telling that you were trying to inject a service into something (the "ModalController", in your case) - but this service was not found.

"How I was trying to inject?" - you may ask. The answer is: every parameter you require in a Controller function is a "dependency" to be "injected" - and AngularJS "injector" service performs this task. This is how "$scope" parameter magically receives a "scope" - it is the injector working behind the scenes.

In you ModalController, the injector was trying to satisfy both the "$modalInstance" and the "items" dependencies (remove the "$modalInstance" parameter - the error message will change to "itemsProvider not found")...

If you want to receive dependencies like this, through the "magical" work of the injector, you need to create/declare services with these names... (or properly use the "resolve" attribute as you were trying to do) ...

...but this is not needed in this case at all. You just want access to "items", and return a selected item. You were also trying to close/dismiss the modal programatically.

You could resolve dependencies through the "resolve" attribute, but why complicate so much what can be achieved with simplicity? Just use the "scope" attribute instead, and provide the scope to the modal - it will have access to its properties. The modal also automatically adds "$close" and "$dismiss" functions to the scope, so you can easily use these functions.

You were trying to pass attributes from the main scope to the modal scope by injecting them as services into the modal controller! :-) You were trying to inject the own modal instance into its controller!!!

So, your main issue is related to the $injector - it worths studying what is this inject thing all about - it is well explained in the documented I linked above.

"Inject a service" is not as simple as "passing a variable to a function". You were almost there through the "resolve" attribute, but as I've said - really not needed for this simple case.

I've created another Plunker without using "scope", and keeping the "resolve"... it is not possible to inject the "modalInstance" as we do with "items":

'$modalInstance': function() { return modalInstance; }

...because it is still undefined at this moment... we could workaround by just calling $scope.$close in the ModalController (and not injecting the modal)...

...or, like I did, injecting it through a function... very crazy, but it works:

http://plnkr.co/edit/9AhH6YFBUmhYoUDhvnhQ?p=preview

...I would never do like this... it is just for learning purposes!


At last but not least: by adding ng-controller in the template file, you are requiring the ModalController twice... you already stated it in the modal configuration. But through the modal configuration, you can have the dependency injection through the resolver - while through the template you don't have the "resolve" thing applied.

Update:

As pointed in the comments by Mahery, $modalInstance is made available for injection in the controller by AngularUI Bootstrap implementation. So, we don't need any effort ro "resolve" or make it available somehow.

Here is the updated Plunker: http://plnkr.co/edit/BMfaFAWibHphDlZAkZex?p=preview

Indeed, the error was happening mainly due to the "ng-controller" atttribute in the template. The controller created through this way does not receive the "AngularUI treatment". Only the controller specified in the modal options receives the "treatment" where $modalInstance is provided...

J. Bruni
  • 20,322
  • 12
  • 75
  • 92
  • Excellent explanation. Can you tell me why you say the second plunker is "crazy, but it works"? Seems to me that the second method passes the scope items to the modal, where the other accesses the items on scope directly. I'm just trying to understand scope further I guess; or rather, proper usage of it. I'll definitely be giving all of that concepts link a more thorough read. Is there an advantage to not using resolve? Or perhaps a better question may be, when is a good time to use and when is it not beneficial? Thanks again for the in depth explanation. – edward.louis Oct 06 '13 at 08:09
  • 1
    "Crazy, but it works" - because it is too much complication to perform such a simple task. That implementation only makes sense for study - to play and see how things work - but never for real in a **simple case** like this. "Resolve" is available as a way to inject stuff in the controller, when it is instantiated. Use it when you **need** it. There is no "advantage" in using an advanced resource without needing it. The only "advantage" is that, when you **need** to inject something into the controller, "resolve" offers a way to do it. I am using plenty of modals and I have not needed it yet. – J. Bruni Oct 06 '13 at 23:03
  • 1
    The only usage of "resolve" I've done by now was not for modals, but for [route configuration](http://docs.angularjs.org/api/ngRoute.$routeProvider#when). I've read [this article about dynamically loading controllers](http://weblogs.asp.net/dwahlin/archive/2013/05/22/dynamically-loading-controllers-and-views-with-angularjs-and-requirejs.aspx), took some time to study it in depth, and adapted its solution to my own needs. In this case, I can't imagine how to perform the task without "resolve" - in other words, we really **need** it to solve the problem. – J. Bruni Oct 06 '13 at 23:09
  • 1
    So, in short, "when is a good time to use?" - when you need it. And if the question is "when you need it?" - I can't answer because I haven't needed it for modals yet - despite using them a lot (but mostly for simple confirmation dialogs). – J. Bruni Oct 06 '13 at 23:12
  • Well put again. For study is exactly why the question was raised. I'm just trying to use very simple and most likely reusable cases like opening modals, but with the idea that each modal may have different data it would be handling (i.e. one for login, another for registering) so I might create one controller that handles all of my modals for separation of code. I find this (organization of code and how to pass data from one object to another) and scope in tandem, to be my largest barriers in angular so far. – edward.louis Oct 07 '13 at 00:51
  • I've already run into another issue that's more scope-specific that has explained some things and yet brought more questions to mind that I believe I'll be posting another question for soon. Keep an eye out. ;) lol – edward.louis Oct 07 '13 at 00:52
  • $modalInstance is actually the modalInstance you create in the "father" Controller. When you call open() it passes this instance into the "child" Controller. – mahery rafara Oct 07 '13 at 11:14
  • Prototypical inheritance happens for scopes, not controllers. – J. Bruni Oct 07 '13 at 12:08
  • you're reasoning is right if you follow the official Angularjs concept. However they decided to implement ui.bootstrap.modal differently. Source: the source code. I've already asked on github why it was done this way. $modalInstance don't need to be injected it's already there in open(). – mahery rafara Oct 08 '13 at 12:59
  • Indeed, @maheryrafara - it is automatically provided to the injector as `$modalInstance` - no need to make it available. Have you seen it, @edward.louis? I will update my answer. Source: https://github.com/angular-ui/bootstrap/blob/master/src/modal/modal.js#L280 – J. Bruni Oct 09 '13 at 03:34
  • 1
    I think it looks like more to the problem in this post: http://stackoverflow.com/questions/21020715/angular-bootstrap-modal-unknown-provider-modalinstanceprovider – Panais Dec 16 '14 at 15:34
3

I've adapted J. Bruni's Plunker to work with Bootstrap3. You can find it here:

http://plnkr.co/edit/sgT87KEubgcWkrEfhTRz?p=preview

Please note that beginning with ui-bootstrap 0.7.0 bootstrap3 support is going to be native and any issues regarding modals (and other directives, ex. progress bar) should be fixed.

Kulbi
  • 961
  • 1
  • 10
  • 16
3
  1. If you are providing the controller name while opening and also in your template then you will get same error.

  2. Remove the ng-controller from your template

Happy coding

Ali Adravi
  • 21,707
  • 9
  • 87
  • 85
1

I got the same error. By removing ng-controller from the template while providing your controller through $modal you can fix this problem.

$modal.open({
            templateUrl: 'MyView.html',
            controller: 'MyViewController'

        });