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

fix(textarea): fix textarea grow #5757

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions src/components/input/demoTextarea/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<div layout="column" ng-cloak class="md-inline-form" ng-init="textBind = 'Input is binded to a Model'">

<md-content layout-padding>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

layout-padding is making the text areas look weird, wrap them with divs or make a class

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just copied that from the Basic Demo, or is this a difference because of the other inputs?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The CSS for layout-padding changed recently. We are aware of some issues with it, but the quick workaround is to wrap the content in a <div>.


<div class="demoTextarea">
<md-input-container class="md-block">
<label>Fixed Rows</label>
<textarea rows="5" ng-model="textBind">This is an example Content</textarea>
</md-input-container>
</div>

<div class="demoTextarea">
<md-input-container class="md-block">
<label>Minimum Rows</label>
<textarea min-rows="5" ng-model="textBind"></textarea>
</md-input-container>
</div>

<div class="demoTextarea">
<md-input-container class="md-block">
<label>Dynamic Rows</label>
<textarea>This is an example Content</textarea>
</md-input-container>
</div>

</md-content>

</div>
2 changes: 2 additions & 0 deletions src/components/input/demoTextarea/script.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
angular
.module('inputTextareaDemo', ['ngMaterial', 'ngMessages']);
4 changes: 4 additions & 0 deletions src/components/input/demoTextarea/style.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
.demoTextarea {
margin: 14px;
background-color: #f7f7f7;
}
42 changes: 31 additions & 11 deletions src/components/input/input.js
Original file line number Diff line number Diff line change
Expand Up @@ -323,31 +323,36 @@ function inputTextareaDirective($mdUtil, $window, $mdAria) {
var node = element[0];
var container = containerCtrl.element[0];

var min_rows = NaN;
var rows = NaN;
var minRows = NaN;
var lineHeight = null;
// can't check if height was or not explicity set,
// so rows attribute will take precedence if present
if (node.hasAttribute('rows')) {
min_rows = parseInt(node.getAttribute('rows'));
rows = parseInt(node.getAttribute('rows'));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is a breaking change isn't it?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes it is.

} else if (node.hasAttribute('min-rows')) {
minRows = parseInt(node.getAttribute('min-rows'));
}

var onChangeTextarea = $mdUtil.debounce(growTextarea, 1);
var onChangeTextarea = $mdUtil.debounce(function() {
$mdUtil.nextTick(growTextarea);
}, 1);

function pipelineListener(value) {
onChangeTextarea();
return value;
}

if (ngModelCtrl) {
if (hasNgModel) {
ngModelCtrl.$formatters.push(pipelineListener);
ngModelCtrl.$viewChangeListeners.push(pipelineListener);
} else {
onChangeTextarea();
}
element.on('keydown input', onChangeTextarea);

if (isNaN(min_rows)) {
element.attr('rows', '1');
if (isNaN(rows)) {
element.attr('rows', isNaN(minRows) ? '1' : minRows);

element.on('scroll', onScroll);
}
Expand All @@ -365,12 +370,13 @@ function inputTextareaDirective($mdUtil, $window, $mdAria) {
// temporarily disables element's flex so its height 'runs free'
element.addClass('md-no-flex');

if (isNaN(min_rows)) {
var contentHeight;
if (isNaN(rows) || !isNaN(minRows)) {
node.style.height = "auto";
node.scrollTop = 0;
var height = getHeight();
if (height) node.style.height = height + 'px';
} else {
contentHeight = getHeight();
}
if (!isNaN(rows) || !isNaN(minRows)) {
node.setAttribute("rows", 1);

if (!lineHeight) {
Expand All @@ -381,8 +387,22 @@ function inputTextareaDirective($mdUtil, $window, $mdAria) {
node.style.minHeight = null;
}

var rows = Math.min(min_rows, Math.round(node.scrollHeight / lineHeight));
node.setAttribute("rows", rows);
}

if (isNaN(rows)) {
if (!isNaN(minRows) && contentHeight) {
var calcHeight;
var minHeight = lineHeight * minRows;

if (contentHeight <= minHeight) calcHeight = minHeight;
else calcHeight = minHeight + (contentHeight - minHeight);

node.style.height = calcHeight + "px";
} else if (contentHeight) {
node.style.height = contentHeight + 'px';
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i'd expect a variable to save the height into and applying node.style.height = only once for more readability

}
} else {
node.style.height = lineHeight * rows + "px";
}

Expand Down
84 changes: 84 additions & 0 deletions src/components/input/input.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -418,5 +418,89 @@ describe('md-input-container directive', function() {
var newHeight = textarea.offsetHeight;
expect(textarea.offsetHeight).toBeGreaterThan(oldHeight);
});

it('should not grow or shrink the textarea with rows attribute', function() {
var rows = 5;
createAndAppendElement('rows="' + rows +'"');

textarea.style.minHeight = '0';
var lineHeight = ngTextarea.prop('clientHeight');
textarea.style.minHeight = null;

ngTextarea.val('Multiple\nlines\nof\ntext');
ngTextarea.triggerHandler('input');

scope.$apply();
$timeout.flush();

expect(textarea.style.height).toBe(rows * lineHeight + "px");
});

it('should grow and shrink the textarea without any attribute', function() {
createAndAppendElement();
var lastHeight;

ngTextarea.val('Multiple\nlines\nof\ntext\nwhich\ncontains\neight\nlines');
ngTextarea.triggerHandler('input');

scope.$apply();
$timeout.flush();

lastHeight = textarea.scrollHeight;
expect(textarea.style.height).toBe(lastHeight + "px");

ngTextarea.val('Multiple\nlines\nof\ntext');
ngTextarea.triggerHandler('input');

scope.$apply();
$timeout.flush();

expect(textarea.style.height).toBeLessThan(lastHeight + "px");
lastHeight = textarea.scrollHeight;

ngTextarea.val('Multiple\nlines\nof\ntext\nwhich\ncontains\neight\nlines\nplus\nmore');
ngTextarea.triggerHandler('input');

scope.$apply();
$timeout.flush();

expect(textarea.style.height).toBeGreaterThan(lastHeight + "px");
});

it("should grow the textarea with min-rows attribute", function() {
var rows = 3;
var lastHeight;
createAndAppendElement('min-rows="' + rows +'"');

textarea.style.minHeight = '0';
var lineHeight = ngTextarea.prop('clientHeight');
textarea.style.minHeight = null;

ngTextarea.val('Multiple\nlines');
ngTextarea.triggerHandler('input');

scope.$apply();
$timeout.flush();

var minHeight = lastHeight = rows * lineHeight;
expect(textarea.style.height).toBe(lastHeight + "px");

ngTextarea.val('Multiple\nlines\nof\ntext\nwhich\ngot\nincreased');
ngTextarea.triggerHandler('input');

scope.$apply();
$timeout.flush();

expect(parseInt(textarea.style.height)).toBeGreaterThan(lastHeight);

ngTextarea.val('Multiple\nlines');
ngTextarea.triggerHandler('input');

scope.$apply();
$timeout.flush();

expect(textarea.style.height).not.toBeLessThan(minHeight);
});

});
});