/
angular-modal-service.js
164 lines (135 loc) · 5.88 KB
/
angular-modal-service.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
// angularModalService.js
//
// Service for showing modal dialogs.
/***** JSLint Config *****/
/*global angular */
(function() {
'use strict';
var module = angular.module('angularModalService', []);
module.factory('ModalService', ['$document', '$compile', '$controller', '$http', '$rootScope', '$q', '$templateCache',
function($document, $compile, $controller, $http, $rootScope, $q, $templateCache) {
// Get the body of the document, we'll add the modal to this.
var body = $document.find('body');
function ModalService() {
var self = this;
// Returns a promise which gets the template, either
// from the template parameter or via a request to the
// template url parameter.
var getTemplate = function(template, templateUrl) {
var deferred = $q.defer();
if(template) {
deferred.resolve(template);
} else if(templateUrl) {
// check to see if the template has already been loaded
var cachedTemplate = $templateCache.get(templateUrl);
if(cachedTemplate !== undefined) {
deferred.resolve(cachedTemplate);
}
// if not, let's grab the template for the first time
else {
$http({method: 'GET', url: templateUrl, cache: true})
.then(function(result) {
// save template into the cache and return the template
$templateCache.put(templateUrl, result.data);
deferred.resolve(result.data);
})
.catch(function(error) {
deferred.reject(error);
});
}
} else {
deferred.reject("No template or templateUrl has been specified.");
}
return deferred.promise;
};
self.showModal = function(options) {
// Create a deferred we'll resolve when the modal is ready.
var deferred = $q.defer();
// Validate the input parameters.
var controllerName = options.controller;
if(!controllerName) {
deferred.reject("No controller has been specified.");
return deferred.promise;
}
// If a 'controllerAs' option has been provided, we change the controller
// name to use 'as' syntax. $controller will automatically handle this.
if(options.controllerAs) {
controllerName = controllerName + " as " + options.controllerAs;
}
// Get the actual html of the template.
getTemplate(options.template, options.templateUrl)
.then(function(template) {
// Create a new scope for the modal.
var modalScope = $rootScope.$new();
// Create the inputs object to the controller - this will include
// the scope, as well as all inputs provided.
// We will also create a deferred that is resolved with a provided
// close function. The controller can then call 'close(result)'.
// The controller can also provide a delay for closing - this is
// helpful if there are closing animations which must finish first.
var closeDeferred = $q.defer();
var inputs = {
$scope: modalScope,
close: function(result, delay) {
if(delay === undefined || delay === null) delay = 0;
window.setTimeout(function() {
// Resolve the 'close' promise.
closeDeferred.resolve(result);
// We can now clean up the scope and remove the element from the DOM.
modalScope.$destroy();
modalElement.remove();
// Unless we null out all of these objects we seem to suffer
// from memory leaks, if anyone can explain why then I'd
// be very interested to know.
inputs.close = null;
deferred = null;
closeDeferred = null;
modal = null;
inputs = null;
modalElement = null;
modalScope = null;
}, delay);
}
};
// If we have provided any inputs, pass them to the controller.
if(options.inputs) {
for(var inputName in options.inputs) {
inputs[inputName] = options.inputs[inputName];
}
}
// Parse the modal HTML into a DOM element (in template form).
var modalElementTemplate = angular.element(template);
// Compile then link the template element, building the actual element.
// Set the $element on the inputs so that it can be injected if required.
var linkFn = $compile(modalElementTemplate);
var modalElement = linkFn(modalScope);
inputs.$element = modalElement;
// Create the controller, explicitly specifying the scope to use.
var modalController = $controller(controllerName, inputs);
// Finally, append the modal to the dom.
if (options.appendElement) {
// append to custom append element
options.appendElement.append(modalElement);
} else {
// append to body when no custom append element is specified
body.append(modalElement);
}
// We now have a modal object...
var modal = {
controller: modalController,
scope: modalScope,
element: modalElement,
close: closeDeferred.promise
};
// ...which is passed to the caller via the promise.
deferred.resolve(modal);
})
.catch(function(error) {
deferred.reject(error);
});
return deferred.promise;
};
}
return new ModalService();
}]);
}());