Skip to content

Commit 03c73b2

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 03c73b2

File tree

2 files changed

+53
-20
lines changed

2 files changed

+53
-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: 40 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,21 @@
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+
Optional,
18+
} from '@angular/core';
1019
import {Platform} from '@angular/cdk/platform';
20+
import {fromEvent} from 'rxjs/observable/fromEvent';
21+
import {auditTime} from 'rxjs/operators/auditTime';
22+
import {takeUntil} from 'rxjs/operators/takeUntil';
23+
import {Subject} from 'rxjs/Subject';
1124

1225

1326
/**
@@ -22,9 +35,10 @@ import {Platform} from '@angular/cdk/platform';
2235
'rows': '1',
2336
},
2437
})
25-
export class MatTextareaAutosize implements AfterViewInit, DoCheck {
38+
export class MatTextareaAutosize implements AfterViewInit, DoCheck, OnDestroy {
2639
/** Keep track of the previous textarea value to avoid resizing when the value hasn't changed. */
2740
private _previousValue: string;
41+
private _destroyed = new Subject<void>();
2842

2943
private _minRows: number;
3044
private _maxRows: number;
@@ -47,7 +61,10 @@ export class MatTextareaAutosize implements AfterViewInit, DoCheck {
4761
/** Cached height of a textarea with a single row. */
4862
private _cachedLineHeight: number;
4963

50-
constructor(private _elementRef: ElementRef, private _platform: Platform) {}
64+
constructor(
65+
private _elementRef: ElementRef,
66+
private _platform: Platform,
67+
@Optional() private _ngZone?: NgZone) {}
5168

5269
/** Sets the minimum height of the textarea as determined by minRows. */
5370
_setMinHeight(): void {
@@ -72,9 +89,22 @@ export class MatTextareaAutosize implements AfterViewInit, DoCheck {
7289
ngAfterViewInit() {
7390
if (this._platform.isBrowser) {
7491
this.resizeToFitContent();
92+
93+
if (this._ngZone) {
94+
this._ngZone.runOutsideAngular(() => {
95+
fromEvent(window, 'resize')
96+
.pipe(auditTime(16), takeUntil(this._destroyed))
97+
.subscribe(() => this.resizeToFitContent(true));
98+
});
99+
}
75100
}
76101
}
77102

103+
ngOnDestroy() {
104+
this._destroyed.next();
105+
this._destroyed.complete();
106+
}
107+
78108
/** Sets a style property on the textarea element. */
79109
private _setTextareaStyle(property: string, value: string): void {
80110
const textarea = this._elementRef.nativeElement as HTMLTextAreaElement;
@@ -132,8 +162,12 @@ export class MatTextareaAutosize implements AfterViewInit, DoCheck {
132162
}
133163
}
134164

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

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

148182
// Only resize of the value changed since these calculations can be expensive.
149-
if (value === this._previousValue) {
183+
if (value === this._previousValue && !force) {
150184
return;
151185
}
152186

0 commit comments

Comments
 (0)