Skip to content

Commit

Permalink
fix(input): incorrect height with autosize (angular#4084)
Browse files Browse the repository at this point in the history
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 angular#4070.
  • Loading branch information
devversion authored and jelbourn committed Apr 14, 2017
1 parent efb39da commit 21f8899
Show file tree
Hide file tree
Showing 2 changed files with 32 additions and 11 deletions.
33 changes: 27 additions & 6 deletions src/lib/input/autosize.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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.
Expand All @@ -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');
});

Expand Down Expand Up @@ -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');
});

});


Expand Down
10 changes: 5 additions & 5 deletions src/lib/input/autosize.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {Directive, ElementRef, Input, OnInit} from '@angular/core';
import {Directive, ElementRef, Input, AfterViewInit} from '@angular/core';


/**
Expand All @@ -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;

Expand Down Expand Up @@ -46,7 +46,7 @@ export class MdTextareaAutosize implements OnInit {
return this.maxRows ? `${this.maxRows * this._cachedLineHeight}px` : null;
}

ngOnInit() {
ngAfterViewInit() {
this._cacheTextareaLineHeight();
this.resizeToFitContent();
}
Expand All @@ -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);
}

Expand Down

0 comments on commit 21f8899

Please sign in to comment.