From 99af5f8a369b13b018ec1e592d82a1a140cbb6bb Mon Sep 17 00:00:00 2001 From: Owais Zahid Date: Fri, 28 Nov 2014 15:44:04 +0800 Subject: [PATCH] fix(Modal): Use attribute observe and add a render promise. --- src/modal/docs/readme.md | 1 + src/modal/modal.js | 50 ++++++++++++++++++++++++------ src/modal/test/modalWindow.spec.js | 8 +++-- template/modal/window.html | 4 +-- 4 files changed, 49 insertions(+), 14 deletions(-) diff --git a/src/modal/docs/readme.md b/src/modal/docs/readme.md index 89b115b109..f0d10dd54d 100644 --- a/src/modal/docs/readme.md +++ b/src/modal/docs/readme.md @@ -22,6 +22,7 @@ The `open` method returns a modal instance, an object with the following propert * `dismiss(reason)` - a method that can be used to dismiss a modal, passing a reason * `result` - a promise that is resolved when a modal is closed and rejected when a modal is dismissed * `opened` - a promise that is resolved when a modal gets opened after downloading content's template and resolving all variables +* 'rendered' - a promise that is resolved when a modal is rendered. In addition the scope associated with modal's content is augmented with 2 methods: diff --git a/src/modal/modal.js b/src/modal/modal.js index 662765d357..16db4ab5f7 100644 --- a/src/modal/modal.js +++ b/src/modal/modal.js @@ -75,7 +75,7 @@ angular.module('ui.bootstrap.modal', ['ui.bootstrap.transition']) }; }]) - .directive('modalWindow', ['$modalStack', '$timeout', function ($modalStack, $timeout) { + .directive('modalWindow', ['$modalStack', '$q', function ($modalStack, $q) { return { restrict: 'EA', scope: { @@ -91,7 +91,31 @@ angular.module('ui.bootstrap.modal', ['ui.bootstrap.transition']) element.addClass(attrs.windowClass || ''); scope.size = attrs.size; - $timeout(function () { + scope.close = function (evt) { + var modal = $modalStack.getTop(); + if (modal && modal.value.backdrop && modal.value.backdrop != 'static' && (evt.target === evt.currentTarget)) { + evt.preventDefault(); + evt.stopPropagation(); + $modalStack.dismiss(modal.key, 'backdrop click'); + } + }; + + // This property is only added to the scope for the purpose of detecting when this directive is rendered. + // We can detect that by using this property in the template associated with this directive and then use + // {@link Attribute#$observe} on it. For more details please see {@link TableColumnResize}. + scope.$isRendered = true; + + // Deferred object that will be resolved when this modal is render. + var modalRenderDeferObj = $q.defer(); + // Observe function will be called on next digest cycle after compilation, ensuring that the DOM is ready. + // In order to use this way of finding whether DOM is ready, we need to observe a scope property used in modal's template. + attrs.$observe('modalRender', function (value) { + if (value == 'true') { + modalRenderDeferObj.resolve(); + } + }); + + modalRenderDeferObj.promise.then(function () { // trigger CSS transitions scope.animate = true; @@ -106,16 +130,13 @@ angular.module('ui.bootstrap.modal', ['ui.bootstrap.transition']) if (!element[0].querySelectorAll('[autofocus]').length) { element[0].focus(); } - }); - scope.close = function (evt) { + // Notify {@link $modalStack} that modal is rendered. var modal = $modalStack.getTop(); - if (modal && modal.value.backdrop && modal.value.backdrop != 'static' && (evt.target === evt.currentTarget)) { - evt.preventDefault(); - evt.stopPropagation(); - $modalStack.dismiss(modal.key, 'backdrop click'); + if (modal) { + $modalStack.modalRendered(modal.key); } - }; + }); } }; }]) @@ -236,6 +257,7 @@ angular.module('ui.bootstrap.modal', ['ui.bootstrap.transition']) openedWindows.add(modalInstance, { deferred: modal.deferred, + renderDeferred: modal.renderDeferred, modalScope: modal.scope, backdrop: modal.backdrop, keyboard: modal.keyboard @@ -296,6 +318,13 @@ angular.module('ui.bootstrap.modal', ['ui.bootstrap.transition']) return openedWindows.top(); }; + $modalStack.modalRendered = function (modalInstance) { + var modalWindow = openedWindows.get(modalInstance); + if (modalWindow) { + modalWindow.value.renderDeferred.resolve(); + } + }; + return $modalStack; }]) @@ -333,11 +362,13 @@ angular.module('ui.bootstrap.modal', ['ui.bootstrap.transition']) var modalResultDeferred = $q.defer(); var modalOpenedDeferred = $q.defer(); + var modalRenderDeferred = $q.defer(); //prepare an instance of a modal to be injected into controllers and returned to a caller var modalInstance = { result: modalResultDeferred.promise, opened: modalOpenedDeferred.promise, + rendered: modalRenderDeferred.promise, close: function (result) { $modalStack.close(modalInstance, result); }, @@ -385,6 +416,7 @@ angular.module('ui.bootstrap.modal', ['ui.bootstrap.transition']) $modalStack.open(modalInstance, { scope: modalScope, deferred: modalResultDeferred, + renderDeferred: modalRenderDeferred, content: tplAndVars[0], backdrop: modalOptions.backdrop, keyboard: modalOptions.keyboard, diff --git a/src/modal/test/modalWindow.spec.js b/src/modal/test/modalWindow.spec.js index 6d33e5a0d3..30156aaade 100644 --- a/src/modal/test/modalWindow.spec.js +++ b/src/modal/test/modalWindow.spec.js @@ -10,14 +10,16 @@ describe('modal window', function () { })); it('should not use transclusion scope for modals content - issue 2110', function () { - $compile('
')($rootScope); + $rootScope.animate = false; + $compile('
')($rootScope); $rootScope.$digest(); expect($rootScope.foo).toBeTruthy(); }); it('should support custom CSS classes as string', function () { - var windowEl = $compile('
content
')($rootScope); + $rootScope.animate = false; + var windowEl = $compile('
content
')($rootScope); $rootScope.$digest(); expect(windowEl).toHaveClass('test'); @@ -33,4 +35,4 @@ describe('modal window', function () { expect(windowEl).toHaveClass('mywindow'); expect(windowEl).toHaveClass('test'); })); -}); \ No newline at end of file +}); diff --git a/template/modal/window.html b/template/modal/window.html index 81ca197ebb..a27c905f65 100644 --- a/template/modal/window.html +++ b/template/modal/window.html @@ -1,3 +1,3 @@ -