AngularJS $uibModal as a service with different controllers - javascript

I'm sitting with this question for a while.
I'm wondering how you can add different kind of controllers and different templateUrl from angularjs to $uibModal. So that You don't have that much code that actually does the same thing. Only on different controller and different template.
This is what I have done so far:
vm.addDialogue = function () {
vm.formData = {};
var modalInstance = $uibModal.open({
animation: true,
templateUrl: 'views/modals/add.dialogue.html',
controller: 'addDialogueController',
controllerAs: 'vm',
backdrop: 'static',
resolve: {
formData: function() {
return vm.formData;
},
selectedTab: 1,
editTime: false
}
});
};
vm.addProject = function(){
vm.formData = {};
var modalInstance = $uibModal.open({
animation: true,
templateUrl: 'views/modals/add.project.html',
controller: 'addProjectController',
controllerAs: 'vm',
backdrop: 'static',
resolve: {
formData: function () {
return vm.formData;
}
}
});
modalInstance.result.then(function (newProject) {
vm.errorMessage = null;
vm.projects.unshift(savedProject);
}, function () {
console.info('Modal dismissed at: ' + new Date());
});
}
This works but I have five more of these Modals, I'm trying to find something that these seven fit in one function and not seven different functions.
I have also tried putting this all in once controller instead of the controllers they belonged in. That worked for a moment but then it didn't showed some buttons that were supposed to be on a page.

Well, you could abstract your code by using a service and parse your params into. This service will handle the uibModal instance. In that way you could avoid your duplicate code.
AngularJS example application & service:
var myApp = angular.module('myApp', []);
//simple controller
myApp.controller('MyCtrl', function (myModalService) {
//open modal
myModalService.openModal('views/modals/add.project.html', 'addProjectController');
//close on click
$scope.close = function () {
myModalService.modalInstance.close();
}
});
// my simple modal service
myApp.service('myModalService', function() {
this.modalInstance = null;
this.openModal = function (templateUrl, controller) {
this.modalInstance = $uibModal.open({
animation: true,
templateUrl: templateUrl,
controller: controller,
controllerAs: 'vm',
backdrop: 'static',
resolve: {
formData: function() {
return vm.formData;
}
}
});
return this.modalInstance;
}
return this;
});

Related

Angular controller ctrlreg error

I'm getting a ctrlreg error when I try to open a modal dialog. I don't think it's a typo and I do think the controller is being registered, but when I attach to it in the debugger, I don't see the template file.
Here is the controller:
(function () {
angular.module('myAppName.controllers').controller('ConfirmationModalController', function ($scope, $uibModalInstance) {
$scope.del = function () {
$uibModalInstance.close(true);
};
$scope.keep = function () {
$uibModalInstance.close(false);
};
});
})();
That gets hit when I reload the page thus I'm guessing it's being registered.
Now here is how I attempt to create the dialog:
function deleteObject(obj) {
var modalInstance = $uibModal.open({
animation: true,
ariaLabelledBy: 'modal-title',
ariaDescribedBy: 'modal-body',
templateUrl: 'js/views/confirm_modal.tpl',
controller: 'ConfirmationModalController',
controllerAs: '$ctrl',
size: 'lg',
resolve: {
data: function () {
return $ctrl.data;
}
}
});
modalInstance.result.then(
function(obj)
{
$ctrl.data.objects = _.without($ctrl.data.objects, obj);
DataService.save($ctrl.data);
ObjectService.remove(object.id);
},
function () {
console.log("------------ERROR----------");
}
);
}
Edit:
Here is the error message I'm getting:
Error: [$controller:ctrlreg] http://errors.angularjs.org/1.6.2/$controller/ctrlreg?p0=ConfirmationModalController
at http://localhost:8080/webjars/angularjs/1.6.2/angular.min.js:6:425
at $get (http://localhost:8080/webjars/angularjs/1.6.2/angular.min.js:92:395)
at http://localhost:8080/webjars/angular-ui-bootstrap/2.2.0/ui-bootstrap-tpls.min.js:8:29619
at http://localhost:8080/webjars/angularjs/1.6.2/angular.min.js:134:167
at m.$get.m.$eval (http://localhost:8080/webjars/angularjs/1.6.2/angular.min.js:148:47)
at m.$get.m.$digest (http://localhost:8080/webjars/angularjs/1.6.2/angular.min.js:145:92)
at m.$get.m.$apply (http://localhost:8080/webjars/angularjs/1.6.2/angular.min.js:148:341)
at l (http://localhost:8080/webjars/angularjs/1.6.2/angular.min.js:101:89)
at XMLHttpRequest.t.onload (http://localhost:8080/webjars/angularjs/1.6.2/angular.min.js:106:489) Possibly unhandled rejection: {}

AngularJS: pass variable to modal

I use https://github.com/simpulton/angularjs-wizard for my project, it works,
(I modified it a little, replaced var app to $scope)
but I need to pass a variable to open function:
$scope.open = function (image)
{
$scope.image = image;
var modalInstance = $uibModal.open({
templateUrl: 'wizard.html',
controllerAs: 'modal',
size: 'lg',
controller: 'ModalCtrl',
resolve: {
image: function () {
return image;
}
}
});
modalInstance.result
.then(function (data) {
$scope.closeAlert();
$scope.summary = data;
}, function (reason) {
$scope.reason = reason;
});
};
and in html:
ng-click="open(image)"
but image is undefined in my template
it works if I only just the modal window, with the example from https://angular-ui.github.io/bootstrap/#/modal,
but not with this wizard example
update:
https://jsfiddle.net/Ginstay/znz64sk3/2/
yes, ajax is completed at the moment, when I open the modal window
and if I add a breakpoint to return image; image is there
Try this one
angular.module('plunker', ['ui.bootstrap']);
var ModalDemoCtrl = function ($scope, $modal) {
var modalInstance = $modal.open({
templateUrl: 'myModalContent.html',
controller: ModalInstanceCtrl,
resolve: {
test: function () {
return 'test variable';
}
}
});
};
var ModalInstanceCtrl = function ($scope, $modalInstance, test) {
$scope.test = test;
};
I figured it, I needed to add
$scope.image = image;
to ModalCtrl

Get controller by path location in AngularJS

I'm developing a simple modal window with ui.bootstrap. This modal is showed when we click in a certain button binding to a controller and fires up, but the modal and its content is binding to another controller so when we click is necessary to know where the controller is which it'll be in another folder of the project.
For example, imaging the structure as follows:
component1
..... template1.html
..... controller1.js
component2
..... template2.html
..... controller2.js
The controller1.js is in charge of load the modal view which renders and is binding with template2.html and controller2.js respectively. So, in controller1.js we have this:
$scope.open = function (size) {
var modalInstance = $uibModal.open({
templateUrl: 'components/component2/template2.html',
controller: 'components/component2/controller2.js',
size: size,
resolve: {
items: function () {
return $scope.items;
}
}
});
modalInstance.result.then(function (selectedItem) {
$scope.selected = selectedItem;
$log.debug(selectedItem);
}, function () {
$log.info('Modal dismissed at: ' + new Date());
});
};
Which obviously does not work for controller2.js. As we do we templateUrl, there is any way to load a controller passing its path as parameter in the $uibModal.open?
I have not tested it, but do something like:
var modalInstance = $uibModal.open({
templateUrl: 'components/component2/template2.html',
controller: 'ModalController',
size: size,
resolve: {
items: function () {
return $scope.items;
}
}
});
app.controller('ModalController', function ($scope, $modalInstance) {
// do some things
});

How to unit test the 'resolve' property on an Angular-UI Bootstrap Modal component

I'm trying to write a unit test that asserts the correct variable is being sent to the resolve property of ui.bootstrap.modal from the Angular-UI Bootstrap components. Here is what I have so far:
// Controller
angular.module('app')
.controller('WorkflowListCtrl', function ($scope, $modal) {
// Setup the edit callback to open a modal
$scope.edit = function(name) {
var modalInstance = $modal.open({
templateUrl: 'partials/editWorkflowModal.html',
controller: 'WorkflowEditCtrl',
scope: $scope,
resolve: {
name: function() { return name; }
}
});
};
});
It's worth noting that the resolve.name property must be a function for the Angular-UI component to work correctly - previously I had tried resolve: { name: name } but this didn't work.
// Unit Test
describe('Controller: WorkflowListCtrl', function () {
// load the controller's module
beforeEach(module('app'));
var workflowListCtrl,
scope,
modal;
// Initialize the controller and a mock scope
beforeEach(inject(function ($controller, $rootScope) {
scope = $rootScope.$new();
modal = {
open: jasmine.createSpy()
};
workflowListCtrl = $controller('WorkflowListCtrl', {
$scope: scope,
$modal: modal
});
it('should allow a workflow to be edited', function() {
// Edit workflow happens in a modal.
scope.edit('Barney Rubble');
expect(modal.open).toHaveBeenCalledWith({
templateUrl: 'partials/editWorkflowModal.html',
controller: 'WorkflowEditCtrl',
scope: scope,
resolve: {
name: jasmine.any(Function)
}
});
});
}));
});
At the moment, this is just checking that the resolve.name property is a function, but what I'd really like to do is assert the resolve.name function returns Barney Rubble. This syntax obviously doesn't work:
expect(modal.open).toHaveBeenCalledWith({
templateUrl: 'partials/editWorkflowModal.html',
controller: 'WorkflowEditCtrl',
scope: scope,
resolve: {
name: function() { return 'Barney Rubble'; }
}
});
It seems like I somehow want to spy on the resolve.name function to check it was called with Barney Rubble but I can't figure out a way to do that. Any ideas?
So I have figured out a way to do this.
Define a 'private' function on $scope:
$scope._resolve = function(item) {
return function() {
return item;
};
};
Modify the original $scope function to call this 'private' method:
$scope.edit = function(name) {
var modalInstance = $modal.open({
templateUrl: 'partials/modal.html',
controller: 'ModalCtrl',
scope: $scope,
resolve: {
name: $scope._resolve(name)
}
});
};
Update your tests to mock this function and return the original value, then you can test it was passed in correctly.
it('should allow a workflow to be edited', function() {
// Mock out the resolve fn and return our item
spyOn($scope, '_resolve').and.callFake(function(item) {
return item;
});
// Edit workflow happens in a modal.
scope.edit('Barney Rubble');
expect(modal.open).toHaveBeenCalledWith({
templateUrl: 'partials/modal.html',
controller: 'ModalCtrl',
scope: scope,
resolve: {
name: 'Barney Rubble'
}
});
});

Angular-ui modal issue

I'm trying to include an angular-ui modal in my web application but am having issues with getting everything set up.
The documentation indicate that you can use $modalInstance to inject the child controller into the parent controller but I don't quite understand how to go about doing so.
Here is the current code (it is straight from the modal demo from the documentation):
angular.module('myApp.controllers', []).
controller('addContent', function ($scope, $http, $modal, $log){
//modaltest
$scope.items = ['item1', 'item2', 'item3'];
$scope.addTerm = function () {
var newTerm = $modal.open({
templateUrl: 'newTermModal.jade',
controller: newTerms,
resolve: {
items: function () {
return $scope.items;
}
}
});
newTerm.result.then(function (selectedItem) {
$scope.selected = selectedItem;
}, function () {
$log.info('Modal dismissed at: ' + new Date());
});
};
}).
controller("newTerms",function($scope, $modalInstance, items){
$scope.items = items;
$scope.selected = {
item: $scope.items[0]
};
$scope.ok = function () {
$modalInstance.close($scope.selected.item);
};
$scope.cancel = function () {
$modalInstance.dismiss('cancel');
};
});
When I run the app like it is now and click the button to open the modal (addTerm function) the app crashes with the error "ReferenceError: newTerms is not defined."
As I mentioned above, the angular-ui site indicates you can inject a controller with $modalInstance but I have not been able to figure out how.
a
Any help would be greatly appreciated!
EDIT
After adding the quotation marks on the pathname as suggested by Chandermani, it seems the modal is loading in the current page rather than the specified template.
I've changed the path to the following: templateUrl:
$scope.addTerm = function () {
var newTerm = $modal.open({
templateUrl: './views/partials/newTermModal.jade',
controller: 'newTerms',
resolve: {
items: function () {
return $scope.items;
}
}
});
A screenshot of the issue follows:
Any idea what could be causing this?
Well you can pass the controller as a string value. I took the default demo sample for modal and changed it to pass controller name instead of controller itself.
See my plunker http://plnkr.co/edit/jpJX4WvHw0SSYm3pAAzq?p=preview
So something like this
controller: 'newTerms',
should work.
I got the same problem, the modal loads main HTML file but not template.
My previous configuration was:
opens dialogs but dialog content is main HTML (like on your pic)
$scope.opts = {
backdrop: true,
backdropClick: true,
dialogFade: false,
keyboard: true,
templateUrl : 'app/reports/modalContent.html',
controller : 'ModalInstanceCtrl',
resolve: {}
};
works as expected
$scope.opts = {
backdrop: true,
backdropClick: true,
dialogFade: false,
keyboard: true,
templateUrl : '/app/reports/modalContent.html',
controller : 'ModalInstanceCtrl',
resolve: {}
};
Sounds like if you put wrong templateUrl, it by default uses main page HTML.
Be sure that you have right path for templateUrl
Hope it will help,
Have you tried to declare a dependency of 'ui.bootstrap' module? Like this:
angular.module('myApp.controllers', ['ui.bootstrap'])
Happened to me today, too. The templateUrl in the controller must match the id for the modal in the html file.
You need to define the newTerms controller before your other controller. Or you can change the code and just create a function inside your main controller with the name newTerms and remove the quotation marks for the name of your controller in your modal.
$scope.addTerm = function () {
var newTerm = $modal.open({
templateUrl: './views/partials/newTermModal.jade',
controller: newTerms,
resolve: {
items: function () {
return $scope.items;
}
}
});
var newTerms = function($scope, $modalInstance, items){
$scope.items = items;
$scope.selected = {
item: $scope.items[0]
};
$scope.ok = function () {
$modalInstance.close($scope.selected.item);
};
$scope.cancel = function () {
$modalInstance.dismiss('cancel');
};
}

Categories

Resources