From 9b812bc3593147d1e76f956c67b779d99387edb8 Mon Sep 17 00:00:00 2001 From: Paul Gschwendtner Date: Thu, 13 Apr 2017 12:24:16 -0700 Subject: [PATCH 1/2] fix(input): incorrect height with autosize 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. --- src/lib/input/autosize.spec.ts | 33 +++++++++++++++++++++++++++------ src/lib/input/autosize.ts | 10 +++++----- 2 files changed, 32 insertions(+), 11 deletions(-) diff --git a/src/lib/input/autosize.spec.ts b/src/lib/input/autosize.spec.ts index 6ca9f10e32b5..be85b894181e 100644 --- a/src/lib/input/autosize.spec.ts +++ b/src/lib/input/autosize.spec.ts @@ -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'); + }); + }); diff --git a/src/lib/input/autosize.ts b/src/lib/input/autosize.ts index f6bb2aacfc6a..8b1911b6560b 100644 --- a/src/lib/input/autosize.ts +++ b/src/lib/input/autosize.ts @@ -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 = '0px'; 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); } From a8c3781e81ae5daea0d4ff3475cd6af41e188318 Mon Sep 17 00:00:00 2001 From: Paul Gschwendtner Date: Thu, 13 Apr 2017 13:02:12 -0700 Subject: [PATCH 2/2] Remove unit suffix for padding --- src/lib/input/autosize.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/input/autosize.ts b/src/lib/input/autosize.ts index 8b1911b6560b..175a8d91e486 100644 --- a/src/lib/input/autosize.ts +++ b/src/lib/input/autosize.ts @@ -71,7 +71,7 @@ export class MdTextareaAutosize implements AfterViewInit { textareaClone.style.position = 'absolute'; textareaClone.style.visibility = 'hidden'; textareaClone.style.border = 'none'; - textareaClone.style.padding = '0px'; + textareaClone.style.padding = '0'; textareaClone.style.height = ''; textareaClone.style.minHeight = ''; textareaClone.style.maxHeight = '';