Skip to content

Commit aec5a66

Browse files
committed
fix(autosize): not updating when window is resized
Fixes the autosize textarea not recalculating its height when the viewport size has changed. Fixes #8610.
1 parent 6e865b7 commit aec5a66

File tree

2 files changed

+52
-20
lines changed

2 files changed

+52
-20
lines changed

src/lib/input/autosize.spec.ts

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
import {Component, ViewChild} from '@angular/core';
22
import {FormsModule} from '@angular/forms';
3-
import {ComponentFixture, TestBed, async, fakeAsync, flushMicrotasks} from '@angular/core/testing';
3+
import {ComponentFixture, TestBed, async, fakeAsync, flush, tick} from '@angular/core/testing';
44
import {By} from '@angular/platform-browser';
55
import {MatInputModule} from './index';
66
import {MatTextareaAutosize} from './autosize';
77
import {MatStepperModule} from '@angular/material/stepper';
88
import {MatTabsModule} from '@angular/material/tabs';
99
import {NoopAnimationsModule} from '@angular/platform-browser/animations';
10+
import {dispatchFakeEvent} from '@angular/cdk/testing';
1011

1112

1213
describe('MatTextareaAutosize', () => {
@@ -185,14 +186,14 @@ describe('MatTextareaAutosize', () => {
185186
autosize = fixtureWithPlaceholder.debugElement.query(
186187
By.directive(MatTextareaAutosize)).injector.get<MatTextareaAutosize>(MatTextareaAutosize);
187188

188-
triggerTextareaResize();
189+
autosize.resizeToFitContent(true);
189190

190191
const heightWithLongPlaceholder = textarea.clientHeight;
191192

192193
fixtureWithPlaceholder.componentInstance.placeholder = 'Short';
193194
fixtureWithPlaceholder.detectChanges();
194195

195-
triggerTextareaResize();
196+
autosize.resizeToFitContent(true);
196197

197198
expect(textarea.clientHeight).toBe(heightWithLongPlaceholder,
198199
'Expected the textarea height to be the same with a long placeholder.');
@@ -213,7 +214,7 @@ describe('MatTextareaAutosize', () => {
213214
Some late visitor entreating entrance at my chamber door;—
214215
This it is and nothing more.” `;
215216
fixtureWithForms.detectChanges();
216-
flushMicrotasks();
217+
flush();
217218
fixtureWithForms.detectChanges();
218219

219220
expect(textarea.clientHeight)
@@ -229,7 +230,7 @@ describe('MatTextareaAutosize', () => {
229230
`;
230231

231232
fixture.detectChanges();
232-
flushMicrotasks();
233+
flush();
233234
fixture.detectChanges();
234235

235236
expect(textarea.clientHeight)
@@ -250,16 +251,14 @@ describe('MatTextareaAutosize', () => {
250251
expect(textarea.getBoundingClientRect().height).toBeGreaterThan(1);
251252
});
252253

253-
/** Triggers a textarea resize to fit the content. */
254-
function triggerTextareaResize() {
255-
// To be able to trigger a new calculation of the height with a short placeholder, the
256-
// textarea value needs to be changed.
257-
textarea.value = '1';
258-
autosize.resizeToFitContent();
254+
it('should trigger a resize when the window is resized', fakeAsync(() => {
255+
spyOn(autosize, 'resizeToFitContent');
259256

260-
textarea.value = '';
261-
autosize.resizeToFitContent();
262-
}
257+
dispatchFakeEvent(window, 'resize');
258+
tick(16);
259+
260+
expect(autosize.resizeToFitContent).toHaveBeenCalled();
261+
}));
263262
});
264263

265264
// Styles to reset padding and border to make measurement comparisons easier.

src/lib/input/autosize.ts

Lines changed: 39 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,20 @@
66
* found in the LICENSE file at https://angular.io/license
77
*/
88

9-
import {Directive, ElementRef, Input, AfterViewInit, DoCheck} from '@angular/core';
9+
import {
10+
Directive,
11+
ElementRef,
12+
Input,
13+
AfterViewInit,
14+
DoCheck,
15+
OnDestroy,
16+
NgZone,
17+
} from '@angular/core';
1018
import {Platform} from '@angular/cdk/platform';
19+
import {fromEvent} from 'rxjs/observable/fromEvent';
20+
import {auditTime} from 'rxjs/operators/auditTime';
21+
import {takeUntil} from 'rxjs/operators/takeUntil';
22+
import {Subject} from 'rxjs/Subject';
1123

1224

1325
/**
@@ -22,9 +34,10 @@ import {Platform} from '@angular/cdk/platform';
2234
'rows': '1',
2335
},
2436
})
25-
export class MatTextareaAutosize implements AfterViewInit, DoCheck {
37+
export class MatTextareaAutosize implements AfterViewInit, DoCheck, OnDestroy {
2638
/** Keep track of the previous textarea value to avoid resizing when the value hasn't changed. */
2739
private _previousValue: string;
40+
private _destroyed = new Subject<void>();
2841

2942
private _minRows: number;
3043
private _maxRows: number;
@@ -47,7 +60,10 @@ export class MatTextareaAutosize implements AfterViewInit, DoCheck {
4760
/** Cached height of a textarea with a single row. */
4861
private _cachedLineHeight: number;
4962

50-
constructor(private _elementRef: ElementRef, private _platform: Platform) {}
63+
constructor(
64+
private _elementRef: ElementRef,
65+
private _platform: Platform,
66+
private _ngZone?: NgZone) {}
5167

5268
/** Sets the minimum height of the textarea as determined by minRows. */
5369
_setMinHeight(): void {
@@ -72,9 +88,22 @@ export class MatTextareaAutosize implements AfterViewInit, DoCheck {
7288
ngAfterViewInit() {
7389
if (this._platform.isBrowser) {
7490
this.resizeToFitContent();
91+
92+
if (this._ngZone) {
93+
this._ngZone.runOutsideAngular(() => {
94+
fromEvent(window, 'resize')
95+
.pipe(auditTime(16), takeUntil(this._destroyed))
96+
.subscribe(() => this.resizeToFitContent(true));
97+
});
98+
}
7599
}
76100
}
77101

102+
ngOnDestroy() {
103+
this._destroyed.next();
104+
this._destroyed.complete();
105+
}
106+
78107
/** Sets a style property on the textarea element. */
79108
private _setTextareaStyle(property: string, value: string): void {
80109
const textarea = this._elementRef.nativeElement as HTMLTextAreaElement;
@@ -132,8 +161,12 @@ export class MatTextareaAutosize implements AfterViewInit, DoCheck {
132161
}
133162
}
134163

135-
/** Resize the textarea to fit its content. */
136-
resizeToFitContent() {
164+
/**
165+
* Resize the textarea to fit its content.
166+
* @param force Whether to force a height recalculation. By default the height will be
167+
* recalculated only if the value changed since the last call.
168+
*/
169+
resizeToFitContent(force = false) {
137170
this._cacheTextareaLineHeight();
138171

139172
// If we haven't determined the line-height yet, we know we're still hidden and there's no point
@@ -146,7 +179,7 @@ export class MatTextareaAutosize implements AfterViewInit, DoCheck {
146179
const value = textarea.value;
147180

148181
// Only resize of the value changed since these calculations can be expensive.
149-
if (value === this._previousValue) {
182+
if (value === this._previousValue && !force) {
150183
return;
151184
}
152185

0 commit comments

Comments
 (0)