fix(input): incorrect height with autosize (#4084)

Currently when using the `mdTextareaAutosize` directive the textarea height might be incorrect on component initialization.

This happens because the textarea `scrollHeight` property is not ready in the `ngOnInit` lifecycle hook yet.

Other libraries like `angular-elastic` have timeouts for that. But using the `ngAfterViewInit` lifecycle hook is more elegant and also ensures that the `scrollHeight` property is ready.

Also switches `offsetHeight` to `clientHeight` since we don't want to include the border in our line-height calculations.

Also by default `textarea` elements have a padding of `2px` and the `padding` needs to be explicitly set to `0px` when resolving the line-height.

Fixes #4070.
This commit is contained in:
Paul Gschwendtner
2017-04-14 14:03:33 -07:00
committed by Jeremy Elbourn
parent efb39da377
commit 21f88999bb
2 changed files with 32 additions and 11 deletions
+27 -6
View File
@@ -29,7 +29,7 @@ describe('MdTextareaAutosize', () => {
});
it('should resize the textarea based on its content', () => {
let previousHeight = textarea.offsetHeight;
let previousHeight = textarea.clientHeight;
fixture.componentInstance.content = `
Once upon a midnight dreary, while I pondered, weak and weary,
@@ -43,12 +43,12 @@ describe('MdTextareaAutosize', () => {
fixture.detectChanges();
autosize.resizeToFitContent();
expect(textarea.offsetHeight)
expect(textarea.clientHeight)
.toBeGreaterThan(previousHeight, 'Expected textarea to have grown with added content.');
expect(textarea.offsetHeight)
expect(textarea.clientHeight)
.toBe(textarea.scrollHeight, 'Expected textarea height to match its scrollHeight');
previousHeight = textarea.offsetHeight;
previousHeight = textarea.clientHeight;
fixture.componentInstance.content += `
Ah, distinctly I remember it was in the bleak December;
And each separate dying ember wrought its ghost upon the floor.
@@ -60,9 +60,9 @@ describe('MdTextareaAutosize', () => {
fixture.detectChanges();
autosize.resizeToFitContent();
expect(textarea.offsetHeight)
expect(textarea.clientHeight)
.toBeGreaterThan(previousHeight, 'Expected textarea to have grown with added content.');
expect(textarea.offsetHeight)
expect(textarea.clientHeight)
.toBe(textarea.scrollHeight, 'Expected textarea height to match its scrollHeight');
});
@@ -103,6 +103,27 @@ describe('MdTextareaAutosize', () => {
expect(fixture.componentInstance.autosize.resizeToFitContent).toBeTruthy();
});
it('should properly resize to content on init', () => {
// Manually create the test component in this test, because in this test the first change
// detection should be triggered after a multiline content is set.
fixture = TestBed.createComponent(AutosizeTextAreaWithContent);
textarea = fixture.nativeElement.querySelector('textarea');
autosize = fixture.debugElement.query(By.css('textarea')).injector.get(MdTextareaAutosize);
fixture.componentInstance.content = `
Line
Line
Line
Line
Line`;
fixture.detectChanges();
expect(textarea.clientHeight)
.toBe(textarea.scrollHeight, 'Expected textarea height to match its scrollHeight');
});
});
+5 -5
View File
@@ -1,4 +1,4 @@
import {Directive, ElementRef, Input, OnInit} from '@angular/core';
import {Directive, ElementRef, Input, AfterViewInit} from '@angular/core';
/**
@@ -14,7 +14,7 @@ import {Directive, ElementRef, Input, OnInit} from '@angular/core';
'[style.max-height]': '_maxHeight',
},
})
export class MdTextareaAutosize implements OnInit {
export class MdTextareaAutosize implements AfterViewInit {
/** @deprecated Use mdAutosizeMinRows */
@Input() minRows: number;
@@ -46,7 +46,7 @@ export class MdTextareaAutosize implements OnInit {
return this.maxRows ? `${this.maxRows * this._cachedLineHeight}px` : null;
}
ngOnInit() {
ngAfterViewInit() {
this._cacheTextareaLineHeight();
this.resizeToFitContent();
}
@@ -71,13 +71,13 @@ export class MdTextareaAutosize implements OnInit {
textareaClone.style.position = 'absolute';
textareaClone.style.visibility = 'hidden';
textareaClone.style.border = 'none';
textareaClone.style.padding = '';
textareaClone.style.padding = '0';
textareaClone.style.height = '';
textareaClone.style.minHeight = '';
textareaClone.style.maxHeight = '';
textarea.parentNode.appendChild(textareaClone);
this._cachedLineHeight = textareaClone.offsetHeight;
this._cachedLineHeight = textareaClone.clientHeight;
textarea.parentNode.removeChild(textareaClone);
}