Skip to content
This repository was archived by the owner on Sep 5, 2024. It is now read-only.

Commit 06e7e99

Browse files
devversionThomasBurleson
authored andcommitted
fix(input): add support for dynamically loaded ngMessage directives.
* Currently ngMessage directives inside of an input-container, which are loaded / transcluded dynamically, didn't get initialized properly. The animation was not working. As well as the auto-hide didn't work properly. Fixes #7477. Fixes #7596. Fixes #6810. Fixes #7823 Closes #8387
1 parent 77958a1 commit 06e7e99

File tree

2 files changed

+87
-7
lines changed

2 files changed

+87
-7
lines changed

src/components/input/input.js

Lines changed: 34 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -717,16 +717,43 @@ function ngMessageDirective($mdUtil) {
717717
priority: 100
718718
};
719719

720-
function compile(element) {
721-
var inputContainer = $mdUtil.getClosest(element, "md-input-container");
720+
function compile(tElement) {
721+
if (!isInsideInputContainer(tElement)) {
722+
723+
// When the current element is inside of a document fragment, then we need to check for an input-container
724+
// in the postLink, because the element will be later added to the DOM and is currently just in a temporary
725+
// fragment, which causes the input-container check to fail.
726+
if (isInsideFragment()) {
727+
return function (scope, element) {
728+
if (isInsideInputContainer(element)) {
729+
// Inside of the postLink function, a ngMessage directive will be a comment element, because it's
730+
// currently hidden. To access the shown element, we need to use the element from the compile function.
731+
initMessageElement(tElement);
732+
}
733+
};
734+
}
735+
} else {
736+
initMessageElement(tElement);
737+
}
722738

723-
// If we are not a child of an input container, don't do anything
724-
if (!inputContainer) return;
739+
function isInsideFragment() {
740+
var nextNode = tElement[0];
741+
while (nextNode = nextNode.parentNode) {
742+
if (nextNode.nodeType === Node.DOCUMENT_FRAGMENT_NODE) {
743+
return true;
744+
}
745+
}
746+
return false;
747+
}
725748

726-
// Add our animation class
727-
element.toggleClass('md-input-message-animation', true);
749+
function isInsideInputContainer(element) {
750+
return !!$mdUtil.getClosest(element, "md-input-container");
751+
}
728752

729-
return {};
753+
function initMessageElement(element) {
754+
// Add our animation class
755+
element.toggleClass('md-input-message-animation', true);
756+
}
730757
}
731758
}
732759

src/components/input/input.spec.js

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -436,6 +436,59 @@ describe('md-input-container directive', function() {
436436
expect(el[0].querySelector("[ng-messages]").classList.contains('md-auto-hide')).toBe(false);
437437
}));
438438

439+
it('should set the animation class on the ngMessage properly', inject(function() {
440+
var element = compile(
441+
'<md-input-container>' +
442+
'<input ng-model="inputVal">' +
443+
'<div ng-messages>' +
444+
'<ng-message id="requiredMessage" when="required">Field required</ng-message>' +
445+
'</div>' +
446+
'</md-input-container>'
447+
);
448+
449+
var ngMessage = element.find('ng-message');
450+
expect(ngMessage).toHaveClass('md-input-message-animation');
451+
}));
452+
453+
it('should set the animation class on a transcluded ngMessage', function() {
454+
// We can emulate the transclusion, by wrapping the ngMessage inside of a document fragment.
455+
// It is not necessary to add a *extra* component / directive for that, since we just
456+
// want to the test the DocumentFragment detection.
457+
var fragment = document.createDocumentFragment();
458+
459+
var inputContainer = compile(
460+
'<md-input-container>' +
461+
'<input ng-model="inputVal">' +
462+
'<div ng-messages id="messageInsertion">' +
463+
'</div>' +
464+
'</md-input-container>'
465+
);
466+
467+
// We build our element, without compiling and linking it.
468+
// Because we invoke those steps manually during the tests.
469+
var messageElement = angular.element(
470+
'<ng-message id="requiredMessage" when="required">Field Required</ng-message>'
471+
);
472+
473+
fragment.appendChild(messageElement[0]);
474+
475+
// Only compile the element at this time, and link it to its scope later.
476+
// Normally the directive will add the animation class upon compile.
477+
var linkFn = $compile(messageElement);
478+
479+
expect(messageElement).not.toHaveClass('md-input-message-animation');
480+
481+
// Now we emulate the finish of the transclusion.
482+
// We move the element from the fragment into the correct input
483+
// container.
484+
inputContainer[0].appendChild(messageElement[0]);
485+
486+
// Manually invoke the postLink function of the directive.
487+
linkFn($rootScope.$new());
488+
489+
expect(messageElement).toHaveClass('md-input-message-animation');
490+
});
491+
439492
it('should select the input value on focus', inject(function($timeout) {
440493
var container = setup('md-select-on-focus');
441494
var input = container.find('input');

0 commit comments

Comments
 (0)