Angular Bootstrap Modal: Unknown provider: $modalInstanceProvider - angularjs

I am trying to use the Angular Bootstrap Modal directive (http://angular-ui.github.io/bootstrap/) as follows, in my controller to open the modal:
function customerSearch() {
var modalInstance = $modal.open({
templateUrl: 'app/customer/customers.modal.html',
controller: 'customers.modal'
});
modalInstance.result.then(function(selectedCustomer) {
console.log(selectedCustomer);
});
}
In the modal controller:
var controllerId = 'customers.modal';
angular.module('app').controller(controllerId,
['$modalInstance', customersModal]);
function customersModal($modalInstance) {
// Modal controller stuff
}
But when I do, I get the following error:
Unknown provider: $modalInstanceProvider <- $modalInstance
If I take out $modalInstance, it works but I obviously have no reference to the modal in the calling controller..
Edit
I don't know if it is worth noting, but I am using the Controller As syntax:
<div class="container-fluid" data-ng-controller="customers.modal as vm">
Application dependencies:
var app = angular.module('app', [
// Angular modules
'ngAnimate', // animations
'ngRoute', // routing
'ngSanitize', // sanitizes html bindings (ex: sidebar.js)
// Custom modules
'common', // common functions, logger, spinner
'common.bootstrap', // bootstrap dialog wrapper functions
// 3rd Party Modules
'ui.bootstrap', // ui-bootstrap (ex: carousel, pagination, dialog)
'breeze.directives', // breeze validation directive (zValidate)
]);
I've created a plunker which is showing the problem here: http://plnkr.co/edit/u8MSSegOnUQgsA36SMhg?p=preview

The problem was that you were specifying a controller in 2 places - when opening a modal and inside a template - this is not needed. Remove ng-controller from a template and things will work as expected:
http://plnkr.co/edit/khySg1gJjqz1Qv4g4cS5?p=preview

try this syntax first
angular.module('app').controller('customers.modal',
['$modalInstance', function($modalInstance){
// Modal controller stuff
}]);
I think it get messed up if you use bracket notation and declare the controller outside.
$modalInstance is the modalInstance you create there
var modalInstance = $modal.open({
templateUrl: 'app/customer/customers.modal.html',
controller: 'customers.modal'
});
it's really the same object. It get injected back in the controller but it's not a service/factory. So it doesn't have a Provider.
This is a tricky part in the lib. Feel free to ask to the original authors of ui-bootstrap. They have been helpful in explaining that.

Related

Angular.js 1.2 separating Controllers and Directives using an IIFE

I seem to come across an error when I try to define a controller within a directive that is wrapped in an IIFE. Although I could fixed this by adding ng-controller on the div in tableHelper.html. I was wondering the code below returns tableHelperCtrl as undefined.
Using angular.js 1.2.29
app.module.js
(function () {
'use strict';
angular.module('app', [
]);
})();
tableHelper.controller.js
(function () {
'use strict';
angular
.module('app')
.controller('tableHelperCtrl', tableHelperCtrl);
function tableHelperCtrl() {
var vm = this;
vm.data = 'some data'
}
})();
tableHelper.directive.js
(function () {
'use strict';
angular
.module('app')
.directive('tableHelper', tableHelper);
function tableHelper() {
var directive = {
restrict: 'A',
templateUrl: './src/app/tableHelper/tableHelper.html',
link: link,
controller: tableHelperCtrl,
controllerAs: 'vm'
};
return directive;
}
}
})();
tableHelper.html
<div>
<p>Table Helpers Directive</p>
<table>
<thead></thead>
<td>{{vm}}</td>
</table>
</div>
You should not assign them the same controller. Give them a controller each and make them communicate through scope (using isolate scopes too if needed) or through a service.
There are a couple of issues with your directive code. Suresh's comment about wrapping the name of your controller in quotes seems to be one issue, although I've seen it work without them, I couldn't get it.
You've also got an extra closing curly brace, an you didn't define link although I guess we could assume that you've got it somewhere but left it out.
One more item is since you've defined your controller as 'vm', you want to use vm.data in your html instead of just vm.
Here's a plunker that shows it working with these changes.

Angular UI Bootstrap Modal: [$injector:unpr] Unknown provider: $uibModalInstanceProvider

This is a bit strange. When I search this issue online I see many pages of Google results and SO solutions... but none seem to work!
In a nutshell, I am trying to implement AngularUI Bootstrap Modal. I keep getting the following error:
Error: [$injector:unpr] Unknown provider: $uibModalInstanceProvider <- $uibModalInstance <- addEntryCtrl
Here is my HTML:
<nav class="navbar navbar-default">
<div class="container">
<span class="nav-col" ng-controller="navCtrl" style="text-align:right">
<a class="btn pill" ng-click="open()" aria-hidden="true">Add New</a>
</span>
</div>
</nav>
Here is my controller:
var app = angular.module('nav', ['ui.bootstrap']);
app.controller('navCtrl', ['$scope', '$uibModal', function($scope, $uibModal) {
$scope.open = function() {
var uibModalInstance = $uibModal.open({
animation: true,
templateUrl: 'addEntry/addEntry.html',
controller: 'addEntryCtrl',
});
};
}]);
And finally, here is my modal code:
var app = angular.module('addEntry', ['firebase', 'ui.bootstrap']);
app.controller('addEntryCtrl', ['$scope', '$firebaseObject', '$state', '$uibModalInstance', function($scope, $firebaseObject, $state, $uibModalInstance) {
$scope.cancel = function() {
$uibModalInstance.dismiss('cancel');
};
$uibModalInstance.close();
}]);
Solutions I've tried:
updated both Angular Bootstrap (Version: 0.14.3) and Angular (v1.4.8)
changed uibModalInstance to modalInstance
changed $uibModalInstance to modalInstance
put my addEntryCtrl inside my ModalInstance
Any thoughts? This has been driving me up the wall for almost 2 days now.
* EDIT *
I should note two things:
1) when I remove $uibModalInstance as a dependency from addEntry, my HTML form submit buttons work just fine and the form looks perfect. Even the redirect occurs correctly (upon submission). The problem remains: the modal still stays on the screen and an error is thrown that $uibModalInstance is undefined. This makes sense since I removed it as a dependency but I obviously still need the modal is close upon submission.
2) Also, I have almost identical code working in another part of my app. The only difference there is that it's working via a factory. Otherwise, the code is identical. Thus, I am confident my dependencies are all there and versions are correct. So. Freaking. Strange.
Thanks!
Answer Found! After hacking away with my friend, we discovered the answer. I wanted to post it here just in case someone else reads this.
It turns out that we had an ng-controller in our modal window that was in a div tag that wrapped the entire html form that was in the modal. Previously, this worked fine when our form was NOT in a modal (it had a separate URL) but for some reason it breaks when it is in a modal. The ng-controller was referencing the addEntryCtrl. Immediately after removing it, the form worked!
The problem was that you were specifying a (or double) controller(s) in 2 places- when opening a modal and inside a template - this is not needed. Remove ng-controller from a template and things will work as expected.Trust me,it will work.
It turns out that if you specify the controller inside the html template (with ng-controller="...") it will not resolve the $uibModalInstance. Specifying the controller from the call to $uibModal.open({controller="...", ...}) will allow it to resolve correctly.
Since you only need the dismiss() and close() methods, you can get them from $scope (named $dismiss and $close) instead, since that will resolve correctly in both ways of instantiating the controller.
var app = angular.module('addEntry', ['ui.bootstrap']);
app.controller('addEntryCtrl', ['$scope', function($scope) {
$scope.cancel = function() {
$scope.$dismiss('cancel');
};
$scope.$close();
}]);
You are trying to reference a controller that is part of a separate module. In order for this to work, you need to inject your secondary module (addEntry) into your main module (nav):
var app = angular.module('nav', ['ui.bootstrap', 'addEntry']);
As you use $uibModal.open() (see lower) and specify explicitly the controller name, you shouldn't put the directive ng-controller in the template.
That cause the error. No ng-controller in the View !
var uibModalInstance = $uibModal.open({
animation: true,
templateUrl: 'addEntry/addEntry.html',
controller: 'addEntryCtrl',
});

AngularJS + RequireJS + angular-ui-grid

I am trying to use angular-ui-grid with AngularJS and RequireJS. See plunker here.
My index31.html has grid and indexController.js defines the gridOptions object. indexController is injected when needed.
When browser loads indexController.js before index31.html, it works fine (i.e. grid is displayed) but when it is the other way round, I get error: $scope.uiGrid is undefined.
How do I specify (in $stateProvider config or elsewhere) to always load indexController.js before index31.html. Or, how do I make all controllers load before the html?
The reason for this is that you require the actual code of the controller asynchronously, i.e. with an inline require:
// app.js
define(function () {
...
app.controller('IndexController', ['$scope', '$injector', function ($scope, $injector) {
// HERE!!!
require(['indexController'], function (controller) {
$injector.invoke(controller, this, { '$scope': $scope });
});
}]);
});
There is no guarantee for the order of loading with this pattern.
What can you do: Require the 'indexController' at the top:
define(['indexController'], function (indexController) {
...
app.controller('IndexController', indexController);
});
It even removes the (horrible IMO) usage of $injector!
(Sidenote: Doing this, the plunk complained about the $scope.$apply() in the last line of indexController.js; I commented it out, it really seems redundant.)
Plunk: http://plnkr.co/edit/fsyljR8FEeZdvXB3SRJP?p=preview

Why does an Angular controller that is a function work, but a controller in a package does not?

In a recent question, I was having an issue with a simple modal dialog implemented using Angular UI for Bootstrap.
I started with this fiddle, and the person who answered came up with this result.
However, one thing immediately caught my attention!
Old Controller Implementation
var controllers = angular.module('app.controllers', []);
controllers.controller('ModalController', ['$scope', '$modal', '$log',
function ($scope, $modal, $log) {
// Overarching controller code...
}
]);
controllers.controller('ModalInstanceController', ['$scope', '$modalInstance',
function ($scope, $modalInstance, params) {
// ...Modal Instance Code...
}
]);
This code does not work with the Angular UI for Bootstrap Modal, but for some reason, this code does:
var ModalController = function($scope, $modal, $log) {
// Overarching controller code...
};
var ModalInstanceController = function($scope, $modalInstance, params) {
// Modal Instance Code...
};
...The problem being, that AngularJS code is usually modularized like the first example to avoid cluttering the global namespace.
So far none of my experiments have been able to get a modularized setup to succeed in the first place. I attempted some simple substitutions, where I would make one controller or the other be a modularized controller, in hopes that it was only one controller preventing the params from being passed between controllers; this turned out not to be the case. Implementing $scope.params = []; before declaring the $scope.open() function, and populating $scope.params in the open function similarly had no effect.
Question: In the context of the AngularUI for Bootstrap system, why does the modularized approach fail? And more importantly, how can I make it work?
Here is your fixed plnkr (http://jsfiddle.net/pEmXt/4/), it had several problems:
You defined your modules in the wrong order.
You had the ui DI in the wrong place.
Your resolve syntax was wrong.
The DI in your modal instance controller was missing an item in the list of dependencies.
The resolve method is used like this:
resolve: {
objectName: function(){
return myObject;
}
}
OK...so I recently ran into same issue and was perplexed also. I just never bothered to dig into it. A quick trip to angular-ui github repo and I found out in the issue tracker.
Angular-Ui demos are passing a function reference as controller. For a modular controller it needs to be a string
var modalInstance = $modal.open({
templateUrl: 'myModalContent.html',
controller: 'ModalInstanceCtrl', /* use string for modular controller */
/* OR */
controller: ModalInstanceCtrl, /* use reference for controller as function*/
});
Issue tracker reference: https://github.com/angular-ui/bootstrap/issues/2330
Working demo from issue tracker: http://plnkr.co/edit/38vBcPalBBNMgYis4cZX?p=preview
In the first example, the module 'app.controllers' has to be added to the list of dependencies for the main app.
var app = angular.module('app', ['app.controllers']);
In the second instance the controllers are global functions and therefore are visible without being explicitly added as a dependency.

How to use the same controller for modal and non-modal form in Angular UI Bootstrap?

I've got a modal with a registration form. The same form should be displayed at the bottom of the landing page not in a modal.
Currently my controller that handles registration modal takes $modalInstance as one of its parameters along $scope etc. If I add ng-controller="SignUpCtrl" to an element in the landing page, it doesn't work, because the controller wasn't created via $modal.open method and so Angular complains about Unknown provider: $modalInstanceProvider <- $modalInstance.
I've got a service for registering users (authService.signUp(data).then/catch...), but the controller itself does a bit more - handles input, emits events (e.g. with translated error messages), sets cookies etc.
What's the best way to handle such case without duplicating almost whole controller code? Should I move the code from controller into yet another, higher-level service?
After struggling for a long while I found a easier trick to reuse our Controller for both modal and normal case.
I found that we can pass caller's scope to modal controller, so I pushed modalInstance into $scope and passed it to the modal controller.
Now you don't have unknown provider problem because $scope is a well known one.
Below is an example:
CallerController = function($rootScope, ...) {
var modalScope = $rootScope.$new();
modalScope.modalInstance = $modal.open({
templateUrl: tempUrl,
controller: ReusableModalController,
scope: modalScope // <- This is it!
});
modalScope.modalInstance.result.then(function (result) {
// Closed
}, function () {
// Dismissed
});
};
ReusableModalController = function($scope, ...){
var dataToSendBack = 'Hello World';
$scope.modalInstance.close(dataToSendBack);
};
Cheers!
If you are using ui-router you can easily use the resolve from ui-router to provide a $uibModalInstance (was $modalInstance before):
$stateProvider
.state('conductReview', {
url: '/review',
templateUrl: '/js/templates/review.html',
controller: 'yourController',
resolve: {
$uibModalInstance: function () { return null; } // <- workaround if you want to use $uibModalInstance in your controller.
}
})
That way you can use your modal controller like a normal controller. If you inject $uibModalInstance in your controller it will be null.
If you want to use same controller for both modal form as well as landing page form, make use of
modalInstance.result.then(function (selectedItem) {
$scope.selected = selectedItem;
}, function () {
$log.info('Modal dismissed at: ' + new Date());
});
You can get all the data from modal form in selectedItem and use this in landing page form.
Also, how do you open the modal. If it is through a button, bind that ng-model to open modal using $modal.open.Don't create separate controller for your modal.Use the same one as your landing page. This way you can use 1 controller to open modal as well as other function after the modal is closed.
PS: Code snippet given here is from angular ui's page. Check that page's documentation for selectedItem

Resources