Yes, yes, you’ve probably seen posts regarding creating a modal dialog service before. I’ve been using one for a bit and wanted to share my spin on this concept.
Angular’s ui-bootstrap directives provide a lot of functionality out of the box. Having the $modal providers really make it easy to manage dialogs. While it is easy to create your own service that can create simple OK/Cancel requests, it is also pretty straight forward to create modals that let you pass data back and forth to your calling controller.
I generally have the aforementioned use cases. As such, I generally define a reusable dialogService with two methods for those two use cases: simple dialog and complex dialog that, potentially, has a more complex controller and template/form, and takes, effectively a DTO object from the calling controller.
The dialogService itself is pretty simple thanks to the ui-modal functionality. We basically $inject a $modalInstance service with every dialog, and that way the dialog, and caller, can control when the dialog is closed. A simple dialogService could look like this (not the injection/module definition isn’t called out specifically):
var dialogService = function ($sce, $modal) { // Generic wrapper around modal opening function openDialog(template, controller, size) { return $modal.open({ templateUrl: template, controller: controller, size: size }); } function openDialogWithModel(templateUrl, controller, model, windowClass, isInlineHtml, size) { if (!size) { size = 'sm'; } if (isInlineHtml) { return $modal.open({ controller: controller, template: templateUrl, resolve: { dialogModel: function () { return model; } }, windowClass: windowClass, size: size }); } else { return $modal.open({ controller: controller, templateUrl: templateUrl, resolve: { dialogModel: function () { return model; } }, windowClass: windowClass, size: size }); } }; return { openDialog: openDialog, openDialogWithModel: openDialogWithModel }; };
After the service is defined, we need a primary controller, into which the service will be injected, some templates to display, and other miscellaneous wire-ups. Most of this is boiler plate, but the interesting part of the dialog service is calling it with a controller and model defined:
var myController = function ($log, ds) { var vm = this; vm.modalFormModel = { firstname: 'original', lastname: 'original' }; vm.openFormModal = function () { ds.openDialogWithModel('modalForm.html', 'modalFormCtrl as mf', vm.modalFormModel, '', false, 'lg'); } };
Passing in a DTO is possible since $modal.open() method takes a resolve parameter. If there is a resolver handler, then the model’s controller is configured to be injected with the named resolved parameters. The modalFormCtrl would look like the below snippet. Also note that I take advantage of angular.copy() on the DTO, and then perform a simple key/value copy to map submitted values back to the DTO if the user clicks ‘Submit.’ This allows us to discard the changes from the user if they don’t want to commit them without modifying the original DTO:
var modalFormController = function ($log, $modalInstance, dialogModel) { var vm = this; // Clone so we can cancel our edit vm.dialogModel = angular.copy(dialogModel); vm.ok = function () { for (var p in vm.dialogModel) { if (dialogModel.hasOwnProperty(p)) { dialogModel[p] = vm.dialogModel[p]; } } $modalInstance.close(); $log.log("User clicked submit - saving changes"); }, vm.cancel = function () { $modalInstance.close(); $log.log("User clicked cancel."); }; }; // Note the injection of dialogModel DTO modalFormController.$inject = ['$log', '$modalInstance', 'dialogModel']; angular.module('myApp.controllers') .controller('modalFormCtrl', modalFormController);
Here are links to Github and Plunker:
And here’s a working jsfiddle for demonstration: