diff --git a/src/modal/docs/demo.js b/src/modal/docs/demo.js index 4e1823c72f..07fba73d75 100644 --- a/src/modal/docs/demo.js +++ b/src/modal/docs/demo.js @@ -1,4 +1,4 @@ -angular.module('ui.bootstrap.demo').controller('ModalDemoCtrl', function ($scope, $modal, $log) { +angular.module('ui.bootstrap.demo').controller('ModalDemoCtrl', function ($scope, $uibModal, $log) { $scope.items = ['item1', 'item2', 'item3']; @@ -6,7 +6,7 @@ angular.module('ui.bootstrap.demo').controller('ModalDemoCtrl', function ($scope $scope.open = function (size) { - var modalInstance = $modal.open({ + var modalInstance = $uibModal.open({ animation: $scope.animationsEnabled, templateUrl: 'myModalContent.html', controller: 'ModalInstanceCtrl', @@ -32,7 +32,7 @@ angular.module('ui.bootstrap.demo').controller('ModalDemoCtrl', function ($scope }); // Please note that $modalInstance represents a modal window (instance) dependency. -// It is not the same as the $modal service used above. +// It is not the same as the $uibModal service used above. angular.module('ui.bootstrap.demo').controller('ModalInstanceCtrl', function ($scope, $modalInstance, items) { diff --git a/src/modal/docs/readme.md b/src/modal/docs/readme.md index fc3b085bbf..1bf72279b5 100644 --- a/src/modal/docs/readme.md +++ b/src/modal/docs/readme.md @@ -1,11 +1,11 @@ -`$modal` is a service to quickly create AngularJS-powered modal windows. +`$uibModal` is a service to quickly create AngularJS-powered modal windows. Creating custom modals is straightforward: create a partial view, its controller and reference them when using the service. -The `$modal` service has only one method: `open(options)` where available options are like follows: +The `$uibModal` service has only one method: `open(options)` where available options are like follows: * `templateUrl` - a path to a template representing modal's content * `template` - inline template representing the modal's content -* `scope` - a scope instance to be used for the modal's content (actually the `$modal` service is going to create a child scope of a provided scope). Defaults to `$rootScope` +* `scope` - a scope instance to be used for the modal's content (actually the `$uibModal` service is going to create a child scope of a provided scope). Defaults to `$rootScope` * `controller` - a controller for a modal instance - it can initialize scope used by modal. Accepts the "controller-as" syntax in the form 'SomeCtrl as myctrl'; can be injected with `$modalInstance` * `controllerAs` - an alternative to the controller-as syntax, matching the API of directive definitions. Requires the `controller` option to be provided as well * `bindToController` - when used with `controllerAs` & set to `true`, it will bind the $scope properties onto the controller directly @@ -20,7 +20,7 @@ The `$modal` service has only one method: `open(options)` where available option * `size` - optional suffix of modal window class. The value used is appended to the `modal-` class, i.e. a value of `sm` gives `modal-sm` * `openedClass` - class added to the `body` element when the modal is opened. Defaults to `modal-open` -Global defaults may be set for `$modal` via `$modalProvider.options`. +Global defaults may be set for `$uibModal` via `$uibModalProvider.options`. The `open` method returns a modal instance, an object with the following properties: diff --git a/src/modal/modal.js b/src/modal/modal.js index 85d159dafd..b75b50294e 100644 --- a/src/modal/modal.js +++ b/src/modal/modal.js @@ -57,8 +57,8 @@ angular.module('ui.bootstrap.modal', ['ui.bootstrap.stackedMap']) /** * A helper directive for the $modal service. It creates a backdrop element. */ - .directive('modalBackdrop', [ - '$animate', '$injector', '$modalStack', + .directive('uibModalBackdrop', [ + '$animate', '$injector', '$uibModalStack', function($animate , $injector, $modalStack) { var $animateCss = null; @@ -100,8 +100,8 @@ angular.module('ui.bootstrap.modal', ['ui.bootstrap.stackedMap']) } }]) - .directive('modalWindow', [ - '$modalStack', '$q', '$animate', '$injector', + .directive('uibModalWindow', [ + '$uibModalStack', '$q', '$animate', '$injector', function($modalStack , $q , $animate, $injector) { var $animateCss = null; @@ -203,18 +203,18 @@ angular.module('ui.bootstrap.modal', ['ui.bootstrap.stackedMap']) }; }]) - .directive('modalAnimationClass', [ + .directive('uibModalAnimationClass', [ function () { return { compile: function(tElement, tAttrs) { if (tAttrs.modalAnimation) { - tElement.addClass(tAttrs.modalAnimationClass); + tElement.addClass(tAttrs.uibModalAnimationClass); } } }; }]) - .directive('modalTransclude', function() { + .directive('uibModalTransclude', function() { return { link: function($scope, $element, $attrs, controller, $transclude) { $transclude($scope.$parent, function(clone) { @@ -225,7 +225,7 @@ angular.module('ui.bootstrap.modal', ['ui.bootstrap.stackedMap']) }; }) - .factory('$modalStack', [ + .factory('$uibModalStack', [ '$animate', '$timeout', '$document', '$compile', '$rootScope', '$q', '$injector', @@ -424,7 +424,7 @@ angular.module('ui.bootstrap.modal', ['ui.bootstrap.stackedMap']) if (currBackdropIndex >= 0 && !backdropDomEl) { backdropScope = $rootScope.$new(true); backdropScope.index = currBackdropIndex; - var angularBackgroundDomEl = angular.element('
'); + var angularBackgroundDomEl = angular.element(''); angularBackgroundDomEl.attr('backdrop-class', modal.backdropClass); if (modal.animation) { angularBackgroundDomEl.attr('modal-animation', 'true'); @@ -433,7 +433,7 @@ angular.module('ui.bootstrap.modal', ['ui.bootstrap.stackedMap']) body.append(backdropDomEl); } - var angularDomEl = angular.element(''); + var angularDomEl = angular.element(''); angularDomEl.attr({ 'template-url': modal.windowTemplateUrl, 'window-class': modal.windowClass, @@ -547,14 +547,14 @@ angular.module('ui.bootstrap.modal', ['ui.bootstrap.stackedMap']) return $modalStack; }]) - .provider('$modal', function() { + .provider('$uibModal', function() { var $modalProvider = { options: { animation: true, backdrop: true, //can also be false or 'static' keyboard: true }, - $get: ['$injector', '$rootScope', '$q', '$templateRequest', '$controller', '$modalStack', + $get: ['$injector', '$rootScope', '$q', '$templateRequest', '$controller', '$uibModalStack', function ($injector, $rootScope, $q, $templateRequest, $controller, $modalStack) { var $modal = {}; @@ -689,3 +689,232 @@ angular.module('ui.bootstrap.modal', ['ui.bootstrap.stackedMap']) return $modalProvider; }); + +/* deprecated modal below */ + +angular.module('ui.bootstrap.modal') + + .value('$modalSuppressWarning', false) + + /** + * A helper directive for the $modal service. It creates a backdrop element. + */ + .directive('modalBackdrop', [ + '$animate', '$injector', '$modalStack', '$log', '$modalSuppressWarning', + function($animate , $injector, $modalStack, $log, $modalSuppressWarning) { + var $animateCss = null; + + if ($injector.has('$animateCss')) { + $animateCss = $injector.get('$animateCss'); + } + + return { + restrict: 'EA', + replace: true, + templateUrl: 'template/modal/backdrop.html', + compile: function(tElement, tAttrs) { + tElement.addClass(tAttrs.backdropClass); + return linkFn; + } + }; + + function linkFn(scope, element, attrs) { + if (!$modalSuppressWarning) { + $log.warn('modal-backdrop is now deprecated. Use uib-modal-backdrop instead.'); + } + if (attrs.modalInClass) { + if ($animateCss) { + $animateCss(element, { + addClass: attrs.modalInClass + }).start(); + } else { + $animate.addClass(element, attrs.modalInClass); + } + + scope.$on($modalStack.NOW_CLOSING_EVENT, function(e, setIsAsync) { + var done = setIsAsync(); + if ($animateCss) { + $animateCss(element, { + removeClass: attrs.modalInClass + }).start().then(done); + } else { + $animate.removeClass(element, attrs.modalInClass).then(done); + } + }); + } + } + }]) + + .directive('modalWindow', [ + '$modalStack', '$q', '$animate', '$injector', '$log', '$modalSuppressWarning', + function($modalStack , $q , $animate, $injector, $log, $modalSuppressWarning) { + var $animateCss = null; + + if ($injector.has('$animateCss')) { + $animateCss = $injector.get('$animateCss'); + } + + return { + restrict: 'EA', + scope: { + index: '@' + }, + replace: true, + transclude: true, + templateUrl: function(tElement, tAttrs) { + return tAttrs.templateUrl || 'template/modal/window.html'; + }, + link: function(scope, element, attrs) { + if (!$modalSuppressWarning) { + $log.warn('modal-window is now deprecated. Use uib-modal-window instead.'); + } + element.addClass(attrs.windowClass || ''); + element.addClass(attrs.windowTopClass || ''); + scope.size = attrs.size; + + 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'); + } + }; + + // moved from template to fix issue #2280 + element.on('click', scope.close); + + // 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() { + var animationPromise = null; + + if (attrs.modalInClass) { + if ($animateCss) { + animationPromise = $animateCss(element, { + addClass: attrs.modalInClass + }).start(); + } else { + animationPromise = $animate.addClass(element, attrs.modalInClass); + } + + scope.$on($modalStack.NOW_CLOSING_EVENT, function(e, setIsAsync) { + var done = setIsAsync(); + if ($animateCss) { + $animateCss(element, { + removeClass: attrs.modalInClass + }).start().then(done); + } else { + $animate.removeClass(element, attrs.modalInClass).then(done); + } + }); + } + + + $q.when(animationPromise).then(function() { + var inputsWithAutofocus = element[0].querySelectorAll('[autofocus]'); + /** + * Auto-focusing of a freshly-opened modal element causes any child elements + * with the autofocus attribute to lose focus. This is an issue on touch + * based devices which will show and then hide the onscreen keyboard. + * Attempts to refocus the autofocus element via JavaScript will not reopen + * the onscreen keyboard. Fixed by updated the focusing logic to only autofocus + * the modal element if the modal does not contain an autofocus element. + */ + if (inputsWithAutofocus.length) { + inputsWithAutofocus[0].focus(); + } else { + element[0].focus(); + } + }); + + // Notify {@link $modalStack} that modal is rendered. + var modal = $modalStack.getTop(); + if (modal) { + $modalStack.modalRendered(modal.key); + } + }); + } + }; + }]) + + .directive('modalAnimationClass', [ + '$log', '$modalSuppressWarning', + function ($log, $modalSuppressWarning) { + return { + compile: function(tElement, tAttrs) { + if (!$modalSuppressWarning) { + $log.warn('modal-animation-class is now deprecated. Use uib-modal-animation-class instead.'); + } + if (tAttrs.modalAnimation) { + tElement.addClass(tAttrs.modalAnimationClass); + } + } + }; + }]) + + .directive('modalTransclude', [ + '$log', '$modalSuppressWarning', + function ($log, $modalSuppressWarning) { + return { + link: function($scope, $element, $attrs, controller, $transclude) { + if (!$modalSuppressWarning) { + $log.warn('modal-transclude is now deprecated. Use uib-modal-transclude instead.'); + } + $transclude($scope.$parent, function(clone) { + $element.empty(); + $element.append(clone); + }); + } + }; + }]) + + .service('$modalStack', [ + '$animate', '$timeout', '$document', '$compile', '$rootScope', + '$q', + '$injector', + '$$multiMap', + '$$stackedMap', + '$uibModalStack', + '$log', + '$modalSuppressWarning', + function($animate , $timeout , $document , $compile , $rootScope , + $q, + $injector, + $$multiMap, + $$stackedMap, + $uibModalStack, + $log, + $modalSuppressWarning) { + if (!$modalSuppressWarning) { + $log.warn('$modalStack is now deprecated. Use $uibModalStack instead.'); + } + + angular.extend(this, $uibModalStack); + }]) + + .provider('$modal', ['$uibModalProvider', function($uibModalProvider) { + angular.extend(this, $uibModalProvider); + + this.$get = ['$injector', '$log', '$modalSuppressWarning', + function ($injector, $log, $modalSuppressWarning) { + if (!$modalSuppressWarning) { + $log.warn('$modal is now deprecated. Use $uibModal instead.'); + } + + return $injector.invoke($uibModalProvider.$get); + }]; + }]); diff --git a/src/modal/test/modal.spec.js b/src/modal/test/modal.spec.js index 38afd1c4e4..7b9206133e 100644 --- a/src/modal/test/modal.spec.js +++ b/src/modal/test/modal.spec.js @@ -1,17 +1,17 @@ -describe('$modal', function () { +describe('$uibModal', function () { var $animate, $controllerProvider, $rootScope, $document, $compile, $templateCache, $timeout, $q; - var $modal, $modalStack, $modalProvider; + var $uibModal, $uibModalStack, $uibModalProvider; beforeEach(module('ngAnimateMock')); beforeEach(module('ui.bootstrap.modal')); beforeEach(module('template/modal/backdrop.html')); beforeEach(module('template/modal/window.html')); - beforeEach(module(function(_$controllerProvider_, _$modalProvider_){ + beforeEach(module(function(_$controllerProvider_, _$uibModalProvider_){ $controllerProvider = _$controllerProvider_; - $modalProvider = _$modalProvider_; + $uibModalProvider = _$uibModalProvider_; })); - beforeEach(inject(function(_$animate_, _$rootScope_, _$document_, _$compile_, _$templateCache_, _$timeout_, _$q_, _$modal_, _$modalStack_) { + beforeEach(inject(function(_$animate_, _$rootScope_, _$document_, _$compile_, _$templateCache_, _$timeout_, _$q_, _$uibModal_, _$uibModalStack_) { $animate = _$animate_; $rootScope = _$rootScope_; $document = _$document_; @@ -19,8 +19,8 @@ describe('$modal', function () { $templateCache = _$templateCache_; $timeout = _$timeout_; $q = _$q_; - $modal = _$modal_; - $modalStack = _$modalStack_; + $uibModal = _$uibModal_; + $uibModalStack = _$uibModalStack_; })); beforeEach(function() { @@ -145,7 +145,7 @@ describe('$modal', function () { } function open(modalOptions) { - var modal = $modal.open(modalOptions); + var modal = $uibModal.open(modalOptions); $rootScope.$digest(); $timeout.flush(0); return modal; @@ -478,7 +478,7 @@ describe('$modal', function () { describe('default options can be changed in a provider', function() { it('should allow overriding default options in a provider', function() { - $modalProvider.options.backdrop = false; + $uibModalProvider.options.backdrop = false; var modal = open({template: '