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

Commit

Permalink
fix(input): add support for dynamically loaded ngMessage directives.
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
devversion authored and ThomasBurleson committed May 19, 2016
1 parent 77958a1 commit 06e7e99
Show file tree
Hide file tree
Showing 2 changed files with 87 additions and 7 deletions.
41 changes: 34 additions & 7 deletions src/components/input/input.js
Original file line number Diff line number Diff line change
Expand Up @@ -717,16 +717,43 @@ function ngMessageDirective($mdUtil) {
priority: 100
};

function compile(element) {
var inputContainer = $mdUtil.getClosest(element, "md-input-container");
function compile(tElement) {
if (!isInsideInputContainer(tElement)) {

// When the current element is inside of a document fragment, then we need to check for an input-container
// in the postLink, because the element will be later added to the DOM and is currently just in a temporary
// fragment, which causes the input-container check to fail.
if (isInsideFragment()) {
return function (scope, element) {
if (isInsideInputContainer(element)) {
// Inside of the postLink function, a ngMessage directive will be a comment element, because it's
// currently hidden. To access the shown element, we need to use the element from the compile function.
initMessageElement(tElement);
}
};
}
} else {
initMessageElement(tElement);
}

// If we are not a child of an input container, don't do anything
if (!inputContainer) return;
function isInsideFragment() {
var nextNode = tElement[0];
while (nextNode = nextNode.parentNode) {
if (nextNode.nodeType === Node.DOCUMENT_FRAGMENT_NODE) {
return true;
}
}
return false;
}

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

return {};
function initMessageElement(element) {
// Add our animation class
element.toggleClass('md-input-message-animation', true);
}
}
}

Expand Down
53 changes: 53 additions & 0 deletions src/components/input/input.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -436,6 +436,59 @@ describe('md-input-container directive', function() {
expect(el[0].querySelector("[ng-messages]").classList.contains('md-auto-hide')).toBe(false);
}));

it('should set the animation class on the ngMessage properly', inject(function() {
var element = compile(
'<md-input-container>' +
'<input ng-model="inputVal">' +
'<div ng-messages>' +
'<ng-message id="requiredMessage" when="required">Field required</ng-message>' +
'</div>' +
'</md-input-container>'
);

var ngMessage = element.find('ng-message');
expect(ngMessage).toHaveClass('md-input-message-animation');
}));

it('should set the animation class on a transcluded ngMessage', function() {
// We can emulate the transclusion, by wrapping the ngMessage inside of a document fragment.
// It is not necessary to add a *extra* component / directive for that, since we just
// want to the test the DocumentFragment detection.
var fragment = document.createDocumentFragment();

var inputContainer = compile(
'<md-input-container>' +
'<input ng-model="inputVal">' +
'<div ng-messages id="messageInsertion">' +
'</div>' +
'</md-input-container>'
);

// We build our element, without compiling and linking it.
// Because we invoke those steps manually during the tests.
var messageElement = angular.element(
'<ng-message id="requiredMessage" when="required">Field Required</ng-message>'
);

fragment.appendChild(messageElement[0]);

// Only compile the element at this time, and link it to its scope later.
// Normally the directive will add the animation class upon compile.
var linkFn = $compile(messageElement);

expect(messageElement).not.toHaveClass('md-input-message-animation');

// Now we emulate the finish of the transclusion.
// We move the element from the fragment into the correct input
// container.
inputContainer[0].appendChild(messageElement[0]);

// Manually invoke the postLink function of the directive.
linkFn($rootScope.$new());

expect(messageElement).toHaveClass('md-input-message-animation');
});

it('should select the input value on focus', inject(function($timeout) {
var container = setup('md-select-on-focus');
var input = container.find('input');
Expand Down

0 comments on commit 06e7e99

Please sign in to comment.