angular-modal-service.js 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164
  1. // angularModalService.js
  2. //
  3. // Service for showing modal dialogs.
  4. /***** JSLint Config *****/
  5. /*global angular */
  6. (function() {
  7. 'use strict';
  8. var module = angular.module('angularModalService', []);
  9. module.factory('ModalService', ['$animate', '$document', '$compile', '$controller', '$http', '$rootScope', '$q', '$templateRequest', '$timeout',
  10. function($animate, $document, $compile, $controller, $http, $rootScope, $q, $templateRequest, $timeout) {
  11. // Get the body of the document, we'll add the modal to this.
  12. var body = angular.element($document[0].body);
  13. function ModalService() {
  14. var self = this;
  15. // Returns a promise which gets the template, either
  16. // from the template parameter or via a request to the
  17. // template url parameter.
  18. var getTemplate = function(template, templateUrl) {
  19. var deferred = $q.defer();
  20. if (template) {
  21. deferred.resolve(template);
  22. } else if (templateUrl) {
  23. $templateRequest(templateUrl, true)
  24. .then(function(template) {
  25. deferred.resolve(template);
  26. }, function(error) {
  27. deferred.reject(error);
  28. });
  29. } else {
  30. deferred.reject("No template or templateUrl has been specified.");
  31. }
  32. return deferred.promise;
  33. };
  34. // Adds an element to the DOM as the last child of its container
  35. // like append, but uses $animate to handle animations. Returns a
  36. // promise that is resolved once all animation is complete.
  37. var appendChild = function(parent, child) {
  38. var children = parent.children();
  39. if (children.length > 0) {
  40. return $animate.enter(child, parent, children[children.length - 1]);
  41. }
  42. return $animate.enter(child, parent);
  43. };
  44. self.showModal = function(options) {
  45. // Create a deferred we'll resolve when the modal is ready.
  46. var deferred = $q.defer();
  47. // Validate the input parameters.
  48. var controllerName = options.controller;
  49. if (!controllerName) {
  50. deferred.reject("No controller has been specified.");
  51. return deferred.promise;
  52. }
  53. // Get the actual html of the template.
  54. getTemplate(options.template, options.templateUrl)
  55. .then(function(template) {
  56. // Create a new scope for the modal.
  57. var modalScope = (options.scope || $rootScope).$new();
  58. // Create the inputs object to the controller - this will include
  59. // the scope, as well as all inputs provided.
  60. // We will also create a deferred that is resolved with a provided
  61. // close function. The controller can then call 'close(result)'.
  62. // The controller can also provide a delay for closing - this is
  63. // helpful if there are closing animations which must finish first.
  64. var closeDeferred = $q.defer();
  65. var closedDeferred = $q.defer();
  66. var inputs = {
  67. $scope: modalScope,
  68. close: function(result, delay) {
  69. if (delay === undefined || delay === null) delay = 0;
  70. $timeout(function() {
  71. // Resolve the 'close' promise.
  72. closeDeferred.resolve(result);
  73. // Let angular remove the element and wait for animations to finish.
  74. $animate.leave(modalElement)
  75. .then(function () {
  76. // Resolve the 'closed' promise.
  77. closedDeferred.resolve(result);
  78. // We can now clean up the scope
  79. modalScope.$destroy();
  80. // Unless we null out all of these objects we seem to suffer
  81. // from memory leaks, if anyone can explain why then I'd
  82. // be very interested to know.
  83. inputs.close = null;
  84. deferred = null;
  85. closeDeferred = null;
  86. modal = null;
  87. inputs = null;
  88. modalElement = null;
  89. modalScope = null;
  90. });
  91. }, delay);
  92. }
  93. };
  94. // If we have provided any inputs, pass them to the controller.
  95. if (options.inputs) angular.extend(inputs, options.inputs);
  96. // Compile then link the template element, building the actual element.
  97. // Set the $element on the inputs so that it can be injected if required.
  98. var linkFn = $compile(template);
  99. var modalElement = linkFn(modalScope);
  100. inputs.$element = modalElement;
  101. // Create the controller, explicitly specifying the scope to use.
  102. var controllerObjBefore = modalScope[options.controllerAs];
  103. var modalController = $controller(options.controller, inputs, false, options.controllerAs);
  104. if (options.controllerAs && controllerObjBefore) {
  105. angular.extend(modalController, controllerObjBefore);
  106. }
  107. // Finally, append the modal to the dom.
  108. if (options.appendElement) {
  109. // append to custom append element
  110. appendChild(options.appendElement, modalElement);
  111. } else {
  112. // append to body when no custom append element is specified
  113. appendChild(body, modalElement);
  114. }
  115. // We now have a modal object...
  116. var modal = {
  117. controller: modalController,
  118. scope: modalScope,
  119. element: modalElement,
  120. close: closeDeferred.promise,
  121. closed: closedDeferred.promise
  122. };
  123. // ...which is passed to the caller via the promise.
  124. deferred.resolve(modal);
  125. })
  126. .then(null, function(error) { // 'catch' doesn't work in IE8.
  127. deferred.reject(error);
  128. });
  129. return deferred.promise;
  130. };
  131. }
  132. return new ModalService();
  133. }]);
  134. }());