Skip to content
This repository was archived by the owner on May 29, 2019. It is now read-only.

Commit 99af5f8

Browse files
zaknuceswesleycho
authored andcommitted
fix(Modal): Use attribute observe and add a render promise.
1 parent b5a80c0 commit 99af5f8

File tree

4 files changed

+49
-14
lines changed

4 files changed

+49
-14
lines changed

Diff for: src/modal/docs/readme.md

+1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ The `open` method returns a modal instance, an object with the following propert
2222
* `dismiss(reason)` - a method that can be used to dismiss a modal, passing a reason
2323
* `result` - a promise that is resolved when a modal is closed and rejected when a modal is dismissed
2424
* `opened` - a promise that is resolved when a modal gets opened after downloading content's template and resolving all variables
25+
* 'rendered' - a promise that is resolved when a modal is rendered.
2526

2627
In addition the scope associated with modal's content is augmented with 2 methods:
2728

Diff for: src/modal/modal.js

+41-9
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ angular.module('ui.bootstrap.modal', ['ui.bootstrap.transition'])
7575
};
7676
}])
7777

78-
.directive('modalWindow', ['$modalStack', '$timeout', function ($modalStack, $timeout) {
78+
.directive('modalWindow', ['$modalStack', '$q', function ($modalStack, $q) {
7979
return {
8080
restrict: 'EA',
8181
scope: {
@@ -91,7 +91,31 @@ angular.module('ui.bootstrap.modal', ['ui.bootstrap.transition'])
9191
element.addClass(attrs.windowClass || '');
9292
scope.size = attrs.size;
9393

94-
$timeout(function () {
94+
scope.close = function (evt) {
95+
var modal = $modalStack.getTop();
96+
if (modal && modal.value.backdrop && modal.value.backdrop != 'static' && (evt.target === evt.currentTarget)) {
97+
evt.preventDefault();
98+
evt.stopPropagation();
99+
$modalStack.dismiss(modal.key, 'backdrop click');
100+
}
101+
};
102+
103+
// This property is only added to the scope for the purpose of detecting when this directive is rendered.
104+
// We can detect that by using this property in the template associated with this directive and then use
105+
// {@link Attribute#$observe} on it. For more details please see {@link TableColumnResize}.
106+
scope.$isRendered = true;
107+
108+
// Deferred object that will be resolved when this modal is render.
109+
var modalRenderDeferObj = $q.defer();
110+
// Observe function will be called on next digest cycle after compilation, ensuring that the DOM is ready.
111+
// In order to use this way of finding whether DOM is ready, we need to observe a scope property used in modal's template.
112+
attrs.$observe('modalRender', function (value) {
113+
if (value == 'true') {
114+
modalRenderDeferObj.resolve();
115+
}
116+
});
117+
118+
modalRenderDeferObj.promise.then(function () {
95119
// trigger CSS transitions
96120
scope.animate = true;
97121

@@ -106,16 +130,13 @@ angular.module('ui.bootstrap.modal', ['ui.bootstrap.transition'])
106130
if (!element[0].querySelectorAll('[autofocus]').length) {
107131
element[0].focus();
108132
}
109-
});
110133

111-
scope.close = function (evt) {
134+
// Notify {@link $modalStack} that modal is rendered.
112135
var modal = $modalStack.getTop();
113-
if (modal && modal.value.backdrop && modal.value.backdrop != 'static' && (evt.target === evt.currentTarget)) {
114-
evt.preventDefault();
115-
evt.stopPropagation();
116-
$modalStack.dismiss(modal.key, 'backdrop click');
136+
if (modal) {
137+
$modalStack.modalRendered(modal.key);
117138
}
118-
};
139+
});
119140
}
120141
};
121142
}])
@@ -236,6 +257,7 @@ angular.module('ui.bootstrap.modal', ['ui.bootstrap.transition'])
236257

237258
openedWindows.add(modalInstance, {
238259
deferred: modal.deferred,
260+
renderDeferred: modal.renderDeferred,
239261
modalScope: modal.scope,
240262
backdrop: modal.backdrop,
241263
keyboard: modal.keyboard
@@ -296,6 +318,13 @@ angular.module('ui.bootstrap.modal', ['ui.bootstrap.transition'])
296318
return openedWindows.top();
297319
};
298320

321+
$modalStack.modalRendered = function (modalInstance) {
322+
var modalWindow = openedWindows.get(modalInstance);
323+
if (modalWindow) {
324+
modalWindow.value.renderDeferred.resolve();
325+
}
326+
};
327+
299328
return $modalStack;
300329
}])
301330

@@ -333,11 +362,13 @@ angular.module('ui.bootstrap.modal', ['ui.bootstrap.transition'])
333362

334363
var modalResultDeferred = $q.defer();
335364
var modalOpenedDeferred = $q.defer();
365+
var modalRenderDeferred = $q.defer();
336366

337367
//prepare an instance of a modal to be injected into controllers and returned to a caller
338368
var modalInstance = {
339369
result: modalResultDeferred.promise,
340370
opened: modalOpenedDeferred.promise,
371+
rendered: modalRenderDeferred.promise,
341372
close: function (result) {
342373
$modalStack.close(modalInstance, result);
343374
},
@@ -385,6 +416,7 @@ angular.module('ui.bootstrap.modal', ['ui.bootstrap.transition'])
385416
$modalStack.open(modalInstance, {
386417
scope: modalScope,
387418
deferred: modalResultDeferred,
419+
renderDeferred: modalRenderDeferred,
388420
content: tplAndVars[0],
389421
backdrop: modalOptions.backdrop,
390422
keyboard: modalOptions.keyboard,

Diff for: src/modal/test/modalWindow.spec.js

+5-3
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,16 @@ describe('modal window', function () {
1010
}));
1111

1212
it('should not use transclusion scope for modals content - issue 2110', function () {
13-
$compile('<div modal-window><span ng-init="foo=true"></span></div>')($rootScope);
13+
$rootScope.animate = false;
14+
$compile('<div modal-window animate="animate"><span ng-init="foo=true"></span></div>')($rootScope);
1415
$rootScope.$digest();
1516

1617
expect($rootScope.foo).toBeTruthy();
1718
});
1819

1920
it('should support custom CSS classes as string', function () {
20-
var windowEl = $compile('<div modal-window window-class="test foo">content</div>')($rootScope);
21+
$rootScope.animate = false;
22+
var windowEl = $compile('<div modal-window animate="animate" window-class="test foo">content</div>')($rootScope);
2123
$rootScope.$digest();
2224

2325
expect(windowEl).toHaveClass('test');
@@ -33,4 +35,4 @@ describe('modal window', function () {
3335
expect(windowEl).toHaveClass('mywindow');
3436
expect(windowEl).toHaveClass('test');
3537
}));
36-
});
38+
});

Diff for: template/modal/window.html

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
<div tabindex="-1" role="dialog" class="modal fade" ng-class="{in: animate}" ng-style="{'z-index': 1050 + index*10, display: 'block'}" ng-click="close($event)">
1+
<div modal-render="{{$isRendered}}" tabindex="-1" role="dialog" class="modal fade" ng-class="{in: animate}" ng-style="{'z-index': 1050 + index*10, display: 'block'}" ng-click="close($event)">
22
<div class="modal-dialog" ng-class="{'modal-sm': size == 'sm', 'modal-lg': size == 'lg'}"><div class="modal-content" modal-transclude></div></div>
3-
</div>
3+
</div>

0 commit comments

Comments
 (0)