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

Commit 16d854c

Browse files
aroopwesleycho
authored andcommitted
feat(modal): add appendTo support
- Add support for appending to specific element through `appendTo` option Closes #4599
1 parent 8ffdaeb commit 16d854c

File tree

3 files changed

+59
-9
lines changed

3 files changed

+59
-9
lines changed

src/modal/docs/readme.md

+1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ The `$uibModal` service has only one method: `open(options)` where available opt
1919
* `windowTemplateUrl` - a path to a template overriding modal's window template
2020
* `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`
2121
* `openedClass` - class added to the `body` element when the modal is opened. Defaults to `modal-open`
22+
* `appendTo` - Appends the modal to a specific element. appendTo must be an `angular.element`. Defaults to `body` element. Example: `appendTo: $document.find('aside')`.
2223

2324
Global defaults may be set for `$uibModal` via `$uibModalProvider.options`.
2425

src/modal/modal.js

+16-9
Original file line numberDiff line numberDiff line change
@@ -280,16 +280,16 @@ angular.module('ui.bootstrap.modal', ['ui.bootstrap.stackedMap'])
280280
});
281281

282282
function removeModalWindow(modalInstance, elementToReceiveFocus) {
283-
var body = $document.find('body').eq(0);
284283
var modalWindow = openedWindows.get(modalInstance).value;
284+
var appendToElement = $document.find(modalWindow.appendTo).eq(0);
285285

286286
//clean up the stack
287287
openedWindows.remove(modalInstance);
288288

289289
removeAfterAnimate(modalWindow.modalDomEl, modalWindow.modalScope, function() {
290290
var modalBodyClass = modalWindow.openedClass || OPENED_MODAL_CLASS;
291291
openedClasses.remove(modalBodyClass, modalInstance);
292-
body.toggleClass(modalBodyClass, openedClasses.hasKey(modalBodyClass));
292+
appendToElement.toggleClass(modalBodyClass, openedClasses.hasKey(modalBodyClass));
293293
toggleTopWindowClass(true);
294294
});
295295
checkRemoveBackdrop();
@@ -298,7 +298,7 @@ angular.module('ui.bootstrap.modal', ['ui.bootstrap.stackedMap'])
298298
if (elementToReceiveFocus && elementToReceiveFocus.focus) {
299299
elementToReceiveFocus.focus();
300300
} else {
301-
body.focus();
301+
appendToElement.focus();
302302
}
303303
}
304304

@@ -418,14 +418,19 @@ angular.module('ui.bootstrap.modal', ['ui.bootstrap.stackedMap'])
418418
keyboard: modal.keyboard,
419419
openedClass: modal.openedClass,
420420
windowTopClass: modal.windowTopClass,
421-
animation: modal.animation
421+
animation: modal.animation,
422+
appendTo: modal.appendTo
422423
});
423424

424425
openedClasses.put(modalBodyClass, modalInstance);
425426

426-
var body = $document.find('body').eq(0),
427+
var appendToElement = $document.find(modal.appendTo).eq(0),
427428
currBackdropIndex = backdropIndex();
428429

430+
if (!appendToElement.length) {
431+
throw new Error('appendTo element not found. Make sure that the element passed is in DOM.');
432+
}
433+
429434
if (currBackdropIndex >= 0 && !backdropDomEl) {
430435
backdropScope = $rootScope.$new(true);
431436
backdropScope.modalOptions = modal;
@@ -436,7 +441,7 @@ angular.module('ui.bootstrap.modal', ['ui.bootstrap.stackedMap'])
436441
angularBackgroundDomEl.attr('modal-animation', 'true');
437442
}
438443
backdropDomEl = $compile(angularBackgroundDomEl)(backdropScope);
439-
body.append(backdropDomEl);
444+
appendToElement.append(backdropDomEl);
440445
}
441446

442447
var angularDomEl = angular.element('<div uib-modal-window="modal-window"></div>');
@@ -455,8 +460,8 @@ angular.module('ui.bootstrap.modal', ['ui.bootstrap.stackedMap'])
455460
var modalDomEl = $compile(angularDomEl)(modal.scope);
456461
openedWindows.top().value.modalDomEl = modalDomEl;
457462
openedWindows.top().value.modalOpener = modalOpener;
458-
body.append(modalDomEl);
459-
body.addClass(modalBodyClass);
463+
appendToElement.append(modalDomEl);
464+
appendToElement.addClass(modalBodyClass);
460465

461466
$modalStack.clearFocusListCache();
462467
};
@@ -609,6 +614,7 @@ angular.module('ui.bootstrap.modal', ['ui.bootstrap.stackedMap'])
609614
//merge and clean up options
610615
modalOptions = angular.extend({}, $modalProvider.options, modalOptions);
611616
modalOptions.resolve = modalOptions.resolve || {};
617+
modalOptions.appendTo = modalOptions.appendTo || 'body';
612618

613619
//verify options
614620
if (!modalOptions.template && !modalOptions.templateUrl) {
@@ -675,7 +681,8 @@ angular.module('ui.bootstrap.modal', ['ui.bootstrap.stackedMap'])
675681
windowClass: modalOptions.windowClass,
676682
windowTemplateUrl: modalOptions.windowTemplateUrl,
677683
size: modalOptions.size,
678-
openedClass: modalOptions.openedClass
684+
openedClass: modalOptions.openedClass,
685+
appendTo: modalOptions.appendTo
679686
});
680687
modalOpenedDeferred.resolve(true);
681688

src/modal/test/modal.spec.js

+42
Original file line numberDiff line numberDiff line change
@@ -833,6 +833,48 @@ describe('$uibModal', function () {
833833
expect($document.find('.modal-backdrop')).not.toHaveClass('fade');
834834
});
835835
});
836+
837+
describe('appendTo', function() {
838+
it('should be added to body by default', function() {
839+
var modal = open({template: '<div>Content</div>'});
840+
841+
expect($document).toHaveModalsOpen(1);
842+
expect($document).toHaveModalOpenWithContent('Content', 'div');
843+
});
844+
845+
it('should not be added to body if appendTo is passed', function() {
846+
var element = angular.element('<section>Some content</section>');
847+
angular.element(document.body).append(element);
848+
849+
var modal = open({template: '<div>Content</div>', appendTo: element});
850+
851+
expect($document).not.toHaveModalOpenWithContent('Content', 'div');
852+
});
853+
854+
it('should be added to appendTo element if appendTo is passed', function() {
855+
var element = angular.element('<section>Some content</section>');
856+
angular.element(document.body).append(element);
857+
858+
expect($document.find('section').children('div.modal').length).toBe(0);
859+
open({template: '<div>Content</div>', appendTo: element});
860+
expect($document.find('section').children('div.modal').length).toBe(1);
861+
});
862+
863+
it('should throw error if appendTo element is not found', function() {
864+
expect(function(){
865+
open({template: '<div>Content</div>', appendTo: $document.find('aside')});
866+
}).toThrow(new Error('appendTo element not found. Make sure that the element passed is in DOM.'));
867+
});
868+
869+
it('should be removed from appendTo element when dismissed', function() {
870+
var modal = open({template: '<div>Content</div>'});
871+
872+
expect($document).toHaveModalsOpen(1);
873+
874+
dismiss(modal);
875+
expect($document).toHaveModalsOpen(0);
876+
});
877+
});
836878

837879
describe('openedClass', function() {
838880
var body;

0 commit comments

Comments
 (0)