I am using Modal (ui.bootstrap.modal) and I have seen 2 different implementations for this:
1. Declare the controller modal instance in the modal declaration
/* 1 file ModalDemoCtrl.js*/
angular.module('ui.bootstrap.demo')
.controller('ModalDemoCtrl', function ($scope, $uibModal, $log) {
var modalInstance = $uibModal.open({
animation: $scope.animationsEnabled,
templateUrl: 'myModalContent.html',
controller: function(){
// stuff
},
size: size,
resolve: {
items: function () {
return $scope.items;
}
}
});
});
2. Declare the controller modal instance separate from the parent controller
/* first file ModalDemoCtrl.js*/
angular.module('ui.bootstrap.demo')
.controller('ModalDemoCtrl', function ($scope, $uibModal, $log) {
var modalInstance = $uibModal.open({
animation: $scope.animationsEnabled,
templateUrl: 'myModalContent.html',
controller:'ModalInstanceCtrl',
size: size,
resolve: {
items: function () {
return $scope.items;
}
}
});
});
/* second file ModalInstanceCtrl.js*/
angular.module('ui.bootstrap.demo')
.controller('ModalInstanceCtrl', function ($scope, $uibModalInstance, items) {
//stuff
});
Questions
a) Which implementation is suitable for a large angular web application ?
b) With implementation 2 will I have dependency issues later on, because of the members that will be resolved and passed to the controller as locals (resolve (Type: Object)) ?
c) With implementation 1 will I increase the number of code lines in my js file ( the controller) and will be harder to understand the logic ?
Related
I have mainly to open an angular popup window based on a template.
But I need to obtain the data from an ajax call.
The Module sample explains how to use a template, but I am confused where to place an ajax call to obtain the items, like follows (the snippet is not working, but is the same as the sample):
angular.module('ui.bootstrap.demo').controller('ModalDemoCtrl',
function($scope, $uibModal, $log) {
//
// need to obtain these items from an AJAX/GET call
//
$scope.items = ['item1', 'item2', 'item3'];
$scope.open = function(size) {
var modalInstance = $uibModal.open({
templateUrl: 'myModalContent.html',
controller: 'ModalInstanceCtrl',
size: size,
resolve: {
items: function() {
return $scope.items;
}
}
});
<div ng-controller="ModalDemoCtrl">
<script type="text/ng-template" id="myModalContent.html">
<div class="modal-header">...</div>
<div class="modal-body">
<ul>
<li ng-repeat="item in items">...</li>
</ul>
</div>
<div class="modal-footer">...</div>
</script>
<button type="button" ng-click="open()">Open me!</button>
</div>
I have a link that return JSON data, I'd modify the function like this:
$scope.open = function(size, jsonLink) {
var modalInstance = $uibModal.open({
// ?????
templateJsonUrl: jsonLink,
// ?????
templateUrl: 'myModalContent.html',
controller: 'ModalInstanceCtrl',
size: size,
resolve: {
items: function() {
return $scope.items;
}
}
});
Just make a request with a http service, and assign result to $scope.items when it will be resolved. A digest cycle will make bindings. Don't forget to inject http service itself.
angular.module('ui.bootstrap.demo').controller('ModalDemoCtrl',
function($scope, $http, $uibModal, $log) {
$http
.post('/your_pass_to_json.json', data)
.then(function(data) {
$scope.items = data.items;
});
$scope.open = function(size) {
var modalInstance = $uibModal.open({
templateUrl: 'myModalContent.html',
controller: 'ModalInstanceCtrl',
size: size,
resolve: {
items: function() {
return $scope.items;
}
}
});
Here is plunker, but I've assigned $scope.items in the promise rejection because could not mock the XHR request.
UPDATE
Or you can put modal opening in the xhr callback:
$scope.open = function(size) {
$http
.post('/your_pass_to_json.json', data)
.then(function(data) {
var modalInstance = $uibModal.open({
templateUrl: 'myModalContent.html',
controller: 'ModalInstanceCtrl',
size: size,
resolve: {
items: function() {
return data.tiems;
}
}
});
});
}
In this case, the modal will always be opened with a delay. I would suggest you to make an xhr request once the controller was initialized, and make a verification on the opening event: if xhr was already fired before, take items from $scope, if it wasn't, cancel previous request and make a new one.
#Lazarev's answer is correct, but additionally in Angular, you have to make ajax calls from angular services and directly inject to controllers.
like this;
AngularJS Ajax Call in Service
I have 2 routes:
app.config(['$routeProvider', '$locationProvider', function(
$routeProvider, $locationProvider) {
$routeProvider.
when("/list/:class", {
controller: "listController",
templateUrl: "DatabaseObject",
reloadOnSearch: true
}).
when("/edit/:class/:id?", {
templateUrl: "editortemplate"
}).
otherwise("/custombrowsingstartpage");
}]);
They both work fine!
What I would like is to be able to render the "editortemplate" of one of the routes within a modal window from the "/list/:class" route.
Within my "listController" I have the modal opening function:
$scope.showEditorPanel = function (size, a, b) {
//console.log($scope);
var modalInstance = $modal.open({
animation: true,
//templateUrl: '#/edit/'+b+'/'+a,
templateUrl: 'editortemplate',
controller: 'editorController',
size: size,
backdrop: true,
scope: $scope
});
The template renders well but I don't know how to pass it the class and id variables the template requires(as shown in its route).
I tried defining route with variables(class== var b, id== var a) instead of template url but no luck:
//templateUrl: '#/edit/'+b+'/'+a,
If you add a 'resolve' object you can send what you want, I believe this is the "Angular" way to do this with $modal :
$scope.showEditorPanel = function (size, a, b) {
var modalInstance = $modal.open({
animation: true,
templateUrl: 'editortemplate',
controller: 'editorController',
size: size,
backdrop: true,
resolve: {
scope: function () {
$scope.properties.class = a;
$scope.properties.id = b;
return $scope.properties;
}
}
});
To pass data to the modal window, you need to use the resolve method of the modal instance. It works just like the same property in the router:
$scope.showEditorPanel = function (size, a, b) {
var modalInstance = $modal.open({
...
resolve: {
class_id: function () { return $scope.class_id }
}
});
Then you need to require that in the the modal window's controller:
function editorController ($modalInstance, $scope, class_id) {
// use the class_id ...
$scope.class_id = class_id;
}
This fixes the 'class_id' in the explicit requirements of the modal controller and will help with troubleshooting down the road. You could have passed it in "secretly" via the $scope, but this is not a good idea (I categorically avoid the $scope property in $modal!). Explicit code is good code!
I just had to define the expected variables as $routParams:
$scope.showEditorPanel = function (size, a, b) {
//console.log($scope);
$routeParams.class=b;
$routeParams.id=a;
var modalInstance = $modal.open({
animation: true,
//templateUrl: '#/edit/TissueSample/'+a,
templateUrl: 'editortemplate',
controller: 'editorController',
size: size,
backdrop: true,
scope: $scope
});
I am trying to hook up an Angular Bootstrap modal (pop-up) but it's giving me either "templateUrl" not found error, or this error: Error: [ng:areq] http://errors.angularjs.org/1.3.15/ng/areq?p0=ModalInstanceCtrl&p1=not%20a%20function%2C%20got%20undefined
I just want some theoretical suggestions on why that would be the case. I have tried a lot of things: it's all properly hooked up, all the spelling matches, etc, etc.
$modal is being used in the controller with other functions as well, so it's all being injected.
Angular Documents provide a suggestion that when you get this error it is possible that you have a controller being injected into another controller. Which is exactly what was happening with my attempt to add a modal as their box solution:
angular.module('ui.bootstrap.demo').controller('ModalDemoCtrl', function ($scope, $modal, $log) {
$scope.items = ['item1', 'item2', 'item3'];
$scope.animationsEnabled = true;
$scope.open = function (size) {
var modalInstance = $modal.open({
animation: $scope.animationsEnabled,
templateUrl: 'myModalContent.html',
controller: 'ModalInstanceCtrl',
size: size,
resolve: {
items: function () {
return $scope.items;
}
}
});
modalInstance.result.then(function (selectedItem) {
$scope.selected = selectedItem;
}, function () {
$log.info('Modal dismissed at: ' + new Date());
});
};
$scope.toggleAnimation = function () {
$scope.animationsEnabled = !$scope.animationsEnabled;
};
});
controller: 'ModalInstanceCtrl' MUST BE removed from your modal function if you're adding it to an existing controller, otherwise it will all go to ... you know where.
Can be as simple as the controller not existing on your module.
I forgot to add the code for the modal controller :( when I first tried it in my project
This controller is about 3 controllers deep in my project (make sure your using "controller AS cntlr" style) so embedding wasn't a problem for me.
angular.module('ui.bootstrap.demo').controller('ModalInstanceCtrl', function ($scope, $uibModalInstance, items) {
$scope.items = items;
$scope.selected = {
item: $scope.items[0]
};
$scope.ok = function () {
$uibModalInstance.close($scope.selected.item);
};
$scope.cancel = function () {
$uibModalInstance.dismiss('cancel');
};
});
I'm trying to pass an object into a modal dialog in a bootstrap/angularJS using the code below. I did this in the style of the answer given at AngularJS UI Bootstrap modal is unable to perform functions from scope. When the modal form is supposed to open from a call to editGroup(), I get the following error:
Error: [$injector:unpr] Unknown provider: selGroupProvider <- selGroup
var EditGroupModalController = function ($scope, $modalInstance, selGroup) {
$scope.user = $sessionStorage.user;
$scope.selGroup = selGroup;
$scope.closeModal = function () {
$modalInstance.close();
};
};
$scope.editGroup = function (selGroup) { // "selGroup" receives the current Group object from $scope.groupList[]
$modal.open({
templateUrl: 'app/views/administration/advanced/editgroup.html',
controller: ['$scope', '$modalInstance','$modal','$sessionStorage','advancedService','selGroup', EditGroupModalController],
size: 'lg',
windowTemplateUrl:'app/views/partials/modaltemplatedraggable.html',
backdrop:'static',
resolve: {
item: function () {
return selGroup;
}
}
});
};
The official description of this error is here; however, I do not understand why I am receiving this error. Any help with this would be greatly appreciated.
Your controller's dependencies list doesn't match controller's function definition: in $modal.open you've listed six dependencies, while in function only three are present.
Dependencies are injected by resolve keys - in your case the key is item.
Necessary changes to your code in order to make it work:
Replace (1)
var EditGroupModalController = function ($scope, $modalInstance, selGroup)
with
var EditGroupModalController = function ($scope, $modalInstance, $modal, $sessionStorage, advancedService, selGroup)
And replace (2)
resolve: {
item: function () {
With
resolve: {
selGroup: function () {
You should name the variable in the resolve as 'selGroup' instead of 'item' so that it can be correctly injected.
Also, the names of the declared dependencies should match the function definition. I put together this simple demo.
angular.module('test', ['ui.bootstrap']).controller('TestController', function($scope, $modal) {
var sel = {name: "A group"};
var EditGroupModalController = function($scope, $modalInstance, selGroup) {
$scope.selGroup = selGroup;
$scope.closeModal = function() {
$modalInstance.close();
};
};
$scope.editGroup = function(selGroup) { // "selGroup" receives the current Group object from $scope.groupList[]
$modal.open({
template: '<div>Test {{selGroup.name}}</div>',
controller: ['$scope', '$modalInstance', 'selGroup', EditGroupModalController],
size: 'lg',
backdrop: 'static',
resolve: {
selGroup: function() {
return sel;
}
}
});
};
});
http://plnkr.co/edit/ec1PjkDZtf4yqINONnX5?p=preview
How can I access variables within another function?
myApp.controller "RegistrationsController", ($scope, $location, $http, $modal)->
$scope.open = () ->
# move to global
modalInstance = $modal.open(
templateUrl: "/assets/angular/templates/registrations/_signup.html",
controller: "ModalController"
)
$scope.cancel = ->
modalInstance.dismiss('cancel')
$scope.saveUser = () ->
$scope.cancel()
From the code above I'm trying to dismiss the modalInstance (modal opened) via cancel function.
However, I guess I need to make modalInstance a global variable first?
declare modalInstance outside of the functions. This will scope it to the whole controller.
myApp.controller "RegistrationsController", ($scope, $location, $http, $modal)->
var modalInstance;
$scope.open = () ->
# move to global
modalInstance = $modal.open(
templateUrl: "/assets/angular/templates/registrations/_signup.html",
controller: "ModalController"
)
$scope.cancel = ->
modalInstance.dismiss('cancel')
$scope.saveUser = () ->
$scope.cancel()
If you're trying to cancel from the ModalController then you can add inject the $modalInstance dependency like ...
.controller('ModalController', function($scope, $modalInstance) {
$scope.cancel = function() {
$modalInstance.dismiss('cancel');
};
});
If you're trying to cancel it from anywhere else then you can share you're controller among other directives using require? Like
.directive('otherDirective', function() {
return {
require: 'RegistrationController',
link: function(scope, el, attr, ctrl) {
}
}
});
Or you could use a service as mediator between other controllers, services, directive, what ever.
.service('mediator', function() {
var modalInstance;
this.modalInstance = function(instance) {
if (instance) {
modalInstance = instance;
}
return modalInstance;
};
});
and then in your directives and controllers and what not, do something like...
.controller('SomeController', function(mediator) {
this.cancel = function() {
mediator.modalInstance().dismiss('cancel');
});
});
You can amend for CoffeeScript ;)