Using this angular modal service:
app.service('modalService', ['$modal',
function ($modal) {
var modalDefaults = {
backdrop: true,
keyboard: true,
modalFade: true,
templateUrl: '/templates/modal.html'
};
var modalOptions = {
closeButtonText: 'Close',
actionButtonText: 'OK',
headerText: 'Proceed?',
bodyText: 'Perform this action?'
};
this.showModal = function (customModalDefaults, customModalOptions) {
if (!customModalDefaults) customModalDefaults = {};
customModalDefaults.backdrop = 'static';
return this.show(customModalDefaults, customModalOptions);
};
this.show = function (customModalDefaults, customModalOptions) {
//Create temp objects to work with since we're in a singleton service
var tempModalDefaults = {};
var tempModalOptions = {};
//Map angular-ui modal custom defaults to modal defaults defined in service
angular.extend(tempModalDefaults, modalDefaults, customModalDefaults);
//Map modal.html $scope custom properties to defaults defined in service
angular.extend(tempModalOptions, modalOptions, customModalOptions);
if (!tempModalDefaults.controller) {
tempModalDefaults.controller = function ($scope, $modalInstance) {
$scope.modalOptions = tempModalOptions;
$scope.modalOptions.ok = function (result) {
$modalInstance.close(result);
};
$scope.modalOptions.close = function () {
$modalInstance.dismiss('cancel');
};
}
}
return $modal.open(tempModalDefaults).result;
};
}]);
I'm having trouble understanding how to pass values from the modal (which has an input) to the controller.
This is my modal:
<input type="text" class="form-control" id="{{modalOptions.inputName}}" name="{{modalOptions.inputName}}" data-ng-model="modalOptions.inputVal" data-ng-if="modalOptions.inputName" />
<button type="button" class="btn"
data-ng-click="modalOptions.close()">{{modalOptions.closeButtonText}}</button>
<button class="btn btn-primary"
data-ng-click="modalOptions.ok();">{{modalOptions.actionButtonText}}</button>
Controller:
$scope.addTopic = function () {
var modalOptions = {
closeButtonText: 'Cancel',
actionButtonText: 'Create Topic',
inputName: 'topicName'
};
modalService.showModal({}, modalOptions).then(function (result) {
// I tried...
var input = $scope.inputName; // and...
input = result;
$log.log("Adding topic '" + input + "' to publication no " + $scope.publication.id);
});
}
So the input is an option in modalOptions but when the user enters a value and clicks ok, nothing is sent to the controller. $scope.inputName returns undefined and so does result.
Ideally, I want to end up with an object like so { inputs : {name: 'inputName' , value: 'abcde'} }.
Try the resolve method in Angular UI Bootstrap
var modalOptions = {
resolve: {
myvar: function () {
return $scope.myvar;
}
}
};
modalService.showModal(modalOptions);
Related
Using angularjs here:
I have looked at the following forum for my query but not able to resolve this :returning data from angularjs modal dialog service
I have the following modal html:
<div class="confirm-modal-header">
<h4 class="modal-title">{{modalOptions.headerText}}</h4>
</div>
<div class="modal-body">
<p>{{modalOptions.bodyText}}</p>
</div>
<div class="modal-footer">
<button type="button" class="btn"
data-ng-click="modalOptions.close()">
{{modalOptions.closeButtonText}}
</button>
<button type="button" class="btn btn-danger"
data-ng-click="modalOptions.ok();">
{{modalOptions.actionButtonText}}
</button>
</div>
I also have the service for this:
CTApp.factory("dialogModalService", ["$q", "$timeout", "$state", "$modal",
function ($q, $timeout, $state, $modal) {
var service = {}
var modalDefaults = {
backdrop: true,
keyboard: true,
modalFade: true,
templateUrl: '/app/modal.html'
};
var modalOptions = {
closeButtonText: 'Close',
actionButtonText: 'OK',
headerText: 'Proceed?',
bodyText: 'Perform this action?'
};
service.showModal = function (customModalDefaults, customModalOptions) {
if (!customModalDefaults) customModalDefaults = {};
customModalDefaults.backdrop = 'static';
return this.show(customModalDefaults, customModalOptions);
};
service.show = function (customModalDefaults, customModalOptions) {
//Create temp objects to work with since we're in a singleton service
var tempModalDefaults = {};
var tempModalOptions = {};
//Map angular-ui modal custom defaults to modal defaults defined in service
angular.extend(tempModalDefaults, modalDefaults, customModalDefaults);
//Map modal.html $scope custom properties to defaults defined in service
angular.extend(tempModalOptions, modalOptions, customModalOptions);
if (!tempModalDefaults.controller) {
tempModalDefaults.controller = function ($scope, $modalInstance) {
$scope.modalOptions = tempModalOptions;
$scope.modalOptions.ok = function (result) {
$modalInstance.close(result);
};
$scope.modalOptions.close = function (result) {
$modalInstance.dismiss('cancel');
};
}
}
return $modal.open(tempModalDefaults).result;
};
return service;
}
]);
And the above modal is being called from my controller as:
dialogModalService.showModal({}, modalOptions).then(function (result)
{
//check for result returned
}
Now in my controller I want to check whether the user pressed the cancel button or not. How can I check that?
I tried to pass the cancel along with
modalOptions.close('cancel')
But it never enters my controller loop of the dialogModalService.showModal.
In my browser also I tried to put a breakpoint inside the call above but again its never hit.
What am I doing wrong here?
In your Modal service try changing:
$modalInstance.dismiss('cancel');
to
$modalInstance.close('cancel');
I am trying to get a angular modal form working but I am always getting an unknown provider error. I think I have included all the necessary files?
Here is my code for calling the service:
function deleteConfirm() {
var modalOptions = {
closeButtonText: 'Cancel',
actionButtonText: 'Delete Supplier',
headerText: 'Delete ' + supplierName + '?',
bodyText: 'Are you sure you want to delete this supplier?'
};
modalService.showModal({}, modalOptions).then(function(result) {
if (result === 'ok') {
alert("ok");
}
}, function(error) {
alert("Error deleting");
});
}
And here is the code for the service:
(function() {
'use strict';
modalService.$inject = '$uibModal';
angular.module('plunker').factory('modalService', modalService);
function modalService($uibModal) {
var injectParams = ['$uibModal'];
//var modalService = function($uibModal) {
var modalDefaults = {
backdrop: true,
keyboard: true,
modalFade: true,
templateUrl: 'modal.html'
};
var modalOptions = {
closeButtonText: 'Close',
actionButtonText: 'OK',
headerText: 'Proceed?',
bodyText: 'Perform this action?'
};
this.showModal = function(customModalDefaults, customModalOptions) {
if (!customModalDefaults) customModalDefaults = {};
customModalDefaults.backdrop = 'static';
return this.show(customModalDefaults, customModalOptions);
};
this.show = function(customModalDefaults, customModalOptions) {
//Create temp objects to work with since we're in a singleton service
var tempModalDefaults = {};
var tempModalOptions = {};
//Map angular-ui modal custom defaults to modal defaults defined in this service
angular.extend(tempModalDefaults, modalDefaults, customModalDefaults);
//Map modal.html $scope custom properties to defaults defined in this service
angular.extend(tempModalOptions, modalOptions, customModalOptions);
if (!tempModalDefaults.controller) {
tempModalDefaults.controller = function($scope, $uibModalInstance) {
$scope.modalOptions = tempModalOptions;
$scope.modalOptions.ok = function(result) {
$uibModalInstance.close('ok');
};
$scope.modalOptions.close = function(result) {
$uibModalInstance.close('cancel');
};
};
tempModalDefaults.controller.$inject = ['$scope', '$uibModalInstance'];
}
return $uibModal.open(tempModalDefaults).result;
};
}
}());
http://plnkr.co/edit/xNpbI42UJm8acODSOimR
Thanks for any help
angular.module('plunker').factory('modalService', modalService);
modalService.$inject = ['$uibModal'];
Try this!
Add to main app.js dependencies ['ui.bootstrap']
var app = angular.module('plunker', ['ui.bootstrap']);
You forgot to include "ui.bootstrap" into your app.
Simply bootstrap your app like this to correct the issue :
var app = angular.module('plunker', ["ui.bootstrap"]);
I have an Angular-UI modal with a form in it. When the user triggers the dismiss event I want to implement a confirmation based on $dirty. I have searched through numerous sources to find notions on Promise and can succesfully get e.g. an alert during the closing event. However, I can't find anywhere how to actually stop the modal from closing.
EDIT:
With the current code the confirmation alert often (surprisingly not always) pops up after the modal has already been dismissed.
var editResourceModalController = function($scope, $uibModalInstance) {
$uibModalInstance.result.catch(function() {
if ($scope.editForm.$dirty) {
window.confirm("close modal?");
}
$uibModalInstance.dismiss('cancel');
});
}
var uibModalInstance;
$scope.openEditModal = function() {
uibModalInstance = $uibModal.open({
animation: true,
templateUrl: "edit.html",
controller: editResourceModalController
});
}
Add the $scope.ok method and hook it to the editForm's submit button's ng-click
var editResourceModalController = function($scope, editItem, hierarchy, selectedFolder) {
$scope.form = {};
$scope.editItem = editItem;
$scope.editListItems = [];
$scope.listItems = 0;
$scope.getNumber = function(n) {
return new Array(n);
}
$scope.hierarchy = hierarchy;
$scope.selectedFolder = selectedFolder;
$scope.editModel = {
name: $scope.editItem.name,
description: $scope.editItem.description,
hierarchyId: $scope.selectedFolder
}
$scope.ok = function () {
editItem.close($scope.editForm.$dirty);
};
}
Inject the $scope.edeitForm.$dirty as isDirty and use the injected value as you like
$scope.openEditModal = function(editItem, hierarchy, selectedFolder) {
$scope.modalInstance = $uibModal.open({
animation: true,
templateUrl: "edit.html",
controller: ["$scope", "editItem", "hierarchy", "selectedFolder", editResourceModalController],
resolve: {
editItem: function() {
return editItem;
},
hierarchy: function() {
return hierarchy;
},
selectedFolder: function() {
return selectedFolder;
}
}
});
$scope.modalInstance.result.catch(function(isDirty) {
if (isDirty) {
// confirmation code here
}else{
// other logic
}
// dismiss the modal
editItem.dismiss('cancel');
});
}
Hope this helped you :D
I fixed it using $scope.$on, extensive example here
var editResourceModalController = function($scope, $uibModalInstance) {
$scope.close = function() {
$uibModalInstance.close();
}
$scope.$on('modal.closing', function(event) {
if ($scope.editForm.$dirty) {
if (!confirm("U sure bwah?")) {
event.preventDefault();
}
}
});
}
var uibModalInstance;
$scope.openEditModal = function(editItem, hierarchy, selectedFolder) {
uibModalInstance = $uibModal.open({
animation: true,
templateUrl: "edit.html",
controller: editResourceModalController
});
}
This solution works for me.
Esc, X button on top and Close button at the bottom.
function cancel() {
if (vm.modalForm.$dirty) {
var response = DevExpress.ui.dialog.confirm("You have unsaved changes. Would you like to discard them?");
response.done(function (result) {
if (result)
vm.dismiss({ $value: 'cancel' });
});
}
else
vm.dismiss({ $value: 'cancel' });
}
$scope.$on('modal.closing', function (event, reason) {
if (reason === 'escape key press') {
var message;
if (vm.modalForm.$dirty) {
message = "You have unsaved changes. Would you like to discard them?";
if (!confirm(message)) {
event.preventDefault();
}
}
}
});
I'm having problems trying to write a jasmine unit test for an Angular-Bootstrap $modal. The exact error is
Expected spy open to have been called with [ { templateUrl : '/n/views/consent.html', controller : 'W2ConsentModal as w2modal', resolve : { employee : Function }, size : 'lg' } ] but actual calls were [ { templateUrl : '/n/views/consent.html', controller : 'W2ConsentModal as w2modal', resolve : { employee : Function }, size : 'lg' } ]
The expected and actual modal options object are the same. What is going on?
Controller
(function () {
'use strict';
angular
.module('app')
.controller('W2History', W2History);
W2History.$inject = ['$scope', '$modal', 'w2Service'];
function W2History($scope, $modal, w2Service) {
/* jshint validthis:true */
var vm = this;
vm.showModal = showModal;
function showModal(employee) {
var modalInstance = $modal.open({
templateUrl: '/n/views/consent.html',
controller: 'W2ConsentModal as w2modal',
resolve: {
employee: function () {
return employee;
}
},
size: 'lg'
});
modalInstance.result.then(function (didConsent) {
// code omitted
});
}
}
})();
Test
describe('W2History controller', function () {
var controller, scope, modal;
var fakeModal = {
result: {
then: function (confirmCallback, cancelCallback) {
//Store the callbacks for later when the user clicks on the OK or Cancel button of the dialog
this.confirmCallBack = confirmCallback;
this.cancelCallback = cancelCallback;
}
},
close: function (item) {
//The user clicked OK on the modal dialog, call the stored confirm callback with the selected item
this.result.confirmCallBack(item);
},
dismiss: function (type) {
//The user clicked cancel on the modal dialog, call the stored cancel callback
this.result.cancelCallback(type);
}
};
var modalOptions = {
templateUrl: '/n/views/consent.html',
controller: 'W2ConsentModal as w2modal',
resolve: {
employee: function () {
return employee;
}
},
size: 'lg'
};
beforeEach(function () {
module('app');
inject(function (_$controller_, _$rootScope_, _$modal_) {
scope = _$rootScope_.$new();
modal = _$modal_;
spyOn(modal, 'open').and.returnValue(fakeModal);
controller = _$controller_('W2History', {
$scope: scope,
$modal: modal,
w2Service: w2Srvc
});
});
});
it('Should correctly show the W2 consent modal', function () {
var employee = terminatedaccessMocks.getCurrentUserInfo();
controller.showModal(employee);
expect(modal.open).toHaveBeenCalledWith(modalOptions);
});
});
Try this:
describe('W2History controller', function () {
var controller, scope, modal;
var fakeModal = {
result: {
then: function (confirmCallback, cancelCallback) {
//Store the callbacks for later when the user clicks on the OK or Cancel button of the dialog
this.confirmCallBack = confirmCallback;
this.cancelCallback = cancelCallback;
}
},
close: function (item) {
//The user clicked OK on the modal dialog, call the stored confirm callback with the selected item
this.result.confirmCallBack(item);
},
dismiss: function (type) {
//The user clicked cancel on the modal dialog, call the stored cancel callback
this.result.cancelCallback(type);
}
};
var modalOptions = {
templateUrl: '/n/views/consent.html',
controller: 'W2ConsentModal as w2modal',
resolve: {
employee: jasmine.any(Function)
},
size: 'lg'
};
var actualOptions;
beforeEach(function () {
module('plunker');
inject(function (_$controller_, _$rootScope_, _$modal_) {
scope = _$rootScope_.$new();
modal = _$modal_;
spyOn(modal, 'open').and.callFake(function(options){
actualOptions = options;
return fakeModal;
});
controller = _$controller_('W2History', {
$scope: scope,
$modal: modal
});
});
});
it('Should correctly show the W2 consent modal', function () {
var employee = { name : "test"};
controller.showModal(employee);
expect(modal.open).toHaveBeenCalledWith(modalOptions);
expect(actualOptions.resolve.employee()).toEqual(employee);
});
});
PLUNK
Explanation:
We should not expect the actual resolve.employee to be the same with the fake resolve.employee because resolve.employee is a function which returns an employee (in this case the employee is captured in closure). The function could be the same but at runtime the returned objects could be different.
The reason your test is failing is the way javascript compares functions. Take a look at this fiddle. Anyway, I don't care about this because we should not expect function implementations. What we do care about in this case is the resolve.employee returns the same object as we pass in:
expect(actualOptions.resolve.employee()).toEqual(employee);
So the solution here is:
We expect everything except for the resolve.employee:
var modalOptions = {
templateUrl: '/n/views/consent.html',
controller: 'W2ConsentModal as w2modal',
resolve: {
employee: jasmine.any(Function) //don't care about the function as we check it separately.
},
size: 'lg'
};
expect(modal.open).toHaveBeenCalledWith(modalOptions);
Check the resolve.employee separately by capturing it first:
var actualOptions;
spyOn(modal, 'open').and.callFake(function(options){
actualOptions = options; //capture the actual options
return fakeModal;
});
expect(actualOptions.resolve.employee()).toEqual(employee); //Check the returned employee is actually the one we pass in.
This is a pass by reference vs pass by value issue. The resolve.employee anonymous function used in $modal.open:
var modalInstance = $modal.open({
templateUrl: '/n/views/consent.html',
controller: 'W2ConsentModal as w2modal',
resolve: {
employee: function () {
return employee;
}
},
size: 'lg'
});
is not the same (by reference) as the resolve.employee anonymous function in your test:
var modalOptions = {
templateUrl: '/n/views/consent.html',
controller: 'W2ConsentModal as w2modal',
resolve: {
employee: function () {
return employee;
}
},
size: 'lg'
};
Your test should be:
resolve: {
employee: jasmine.any(Function)
}
If it's essential that the resolve function be tested, you should expose it somewhere where you can get a reference to the same function in your tests.
I am not sure if this will help you now, but when you spy on something you can get the argument that is passed to the $uibModal.open spy, you can then call that function to test that it returns what is in the resolve method.
it('expect resolve to be have metadataid that will return 9999', () => {
spyOn($uibModal, 'open');
//add test code here that will call the $uibModal.open
var spy = <jasmine.Spy>$uibModal.open;
var args = spy.calls.argsFor(0);
expect(args[0].resolve.metadataId()).toEqual(9999);
});
***** my code is using typescript, but this works for me.**
I have come across the same scenario. I have come across the problem with the below given solution
//Function to open export modal
scope.openExportModal();
expect( uibModal.open ).toHaveBeenCalledWith(options);
expect( uibModal.open.calls.mostRecent().args[0].resolve.modalData() ).toEqual(modalData);
Hope this may help if you want a quick fix.
I have created a directive below:
html:
<div image-upload></div>
directive:
angular.module('app.directives.imageTools', [
"angularFileUpload"
])
.directive('imageUpload', function () {
// Directive used to display a badge.
return {
restrict: 'A',
replace: true,
templateUrl: "/static/html/partials/directives/imageToolsUpload.html",
controller: function ($scope) {
var resetScope = function () {
$scope.imageUpload = {};
$scope.imageUpload.error = false;
$scope.imageUpload['image_file'] = undefined;
$scope.$parent.imageUpload = $scope.imageUpload
};
$scope.onImageSelect = function ($files) {
resetScope();
$scope.imageUpload.image_file = $files[0];
var safe_file_types = ['image/jpeg', 'image/jpg']
if (safe_file_types.indexOf($scope.imageUpload.image_file.type) >= 0) {
$scope.$parent.imageUpload = $scope.imageUpload
}
else {
$scope.imageUpload.error = true
}
};
// Init function.
$scope.init = function () {
resetScope();
};
$scope.init();
}
}
});
This directive works fine and in my controller I access $scope.imageUpload as I required.
Next, I tried to pass into the directive a current image but when I do this $scope.imageUpload is undefined and things get weird...
html:
<div image-upload current="project.thumbnail_small"></div>
This is the updated code that gives the error, note the new current.
angular.module('app.directives.imageTools', [
"angularFileUpload"
])
.directive('imageUpload', function () {
// Directive used to display a badge.
return {
restrict: 'A',
replace: true,
scope: {
current: '='
},
templateUrl: "/static/html/partials/directives/imageToolsUpload.html",
controller: function ($scope) {
var resetScope = function () {
$scope.imageUpload = {};
$scope.imageUpload.error = false;
$scope.imageUpload['image_file'] = undefined;
$scope.$parent.imageUpload = $scope.imageUpload
if ($scope.current != undefined){
$scope.hasCurrentImage = true;
}
else {
$scope.hasCurrentImage = true;
}
};
$scope.onImageSelect = function ($files) {
resetScope();
$scope.imageUpload.image_file = $files[0];
var safe_file_types = ['image/jpeg', 'image/jpg']
if (safe_file_types.indexOf($scope.imageUpload.image_file.type) >= 0) {
$scope.$parent.imageUpload = $scope.imageUpload
}
else {
$scope.imageUpload.error = true
}
};
// Init function.
$scope.init = function () {
resetScope();
};
$scope.init();
}
}
});
What is going on here?
scope: {
current: '='
},
Everything works again but I don't get access to the current value.
Maybe I'm not using scope: { correctly.
in your updated code you use an isolated scope by defining scope: {current: '=' } so the controller in the directive will only see the isolated scope and not the original scope.
you can read more about this here: http://www.ng-newsletter.com/posts/directives.html in the scope section