Skip to content

Commit b8664b8

Browse files
crisbetommalerba
authored andcommitted
fix(autosize): not updating when window is resized (#8619)
Fixes the autosize textarea not recalculating its height when the viewport size has changed. Fixes #8610.
1 parent fb9ea53 commit b8664b8

File tree

2 files changed

+54
-20
lines changed

2 files changed

+54
-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: 41 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
/**
@@ -23,9 +35,10 @@ import {Platform} from '@angular/cdk/platform';
2335
'rows': '1',
2436
},
2537
})
26-
export class MatTextareaAutosize implements AfterViewInit, DoCheck {
38+
export class MatTextareaAutosize implements AfterViewInit, DoCheck, OnDestroy {
2739
/** Keep track of the previous textarea value to avoid resizing when the value hasn't changed. */
2840
private _previousValue: string;
41+
private _destroyed = new Subject<void>();
2942

3043
private _minRows: number;
3144
private _maxRows: number;
@@ -49,7 +62,12 @@ export class MatTextareaAutosize implements AfterViewInit, DoCheck {
4962
/** Cached height of a textarea with a single row. */
5063
private _cachedLineHeight: number;
5164

52-
constructor(private _elementRef: ElementRef, private _platform: Platform) {}
65+
constructor(
66+
private _elementRef: ElementRef,
67+
private _platform: Platform,
68+
private _ngZone?: NgZone) {}
69+
70+
// TODO(crisbeto): make the `_ngZone` a required param in the next major version.
5371

5472
/** Sets the minimum height of the textarea as determined by minRows. */
5573
_setMinHeight(): void {
@@ -74,9 +92,22 @@ export class MatTextareaAutosize implements AfterViewInit, DoCheck {
7492
ngAfterViewInit() {
7593
if (this._platform.isBrowser) {
7694
this.resizeToFitContent();
95+
96+
if (this._ngZone) {
97+
this._ngZone.runOutsideAngular(() => {
98+
fromEvent(window, 'resize')
99+
.pipe(auditTime(16), takeUntil(this._destroyed))
100+
.subscribe(() => this.resizeToFitContent(true));
101+
});
102+
}
77103
}
78104
}
79105

106+
ngOnDestroy() {
107+
this._destroyed.next();
108+
this._destroyed.complete();
109+
}
110+
80111
/** Sets a style property on the textarea element. */
81112
private _setTextareaStyle(property: string, value: string): void {
82113
const textarea = this._elementRef.nativeElement as HTMLTextAreaElement;
@@ -134,8 +165,12 @@ export class MatTextareaAutosize implements AfterViewInit, DoCheck {
134165
}
135166
}
136167

137-
/** Resize the textarea to fit its content. */
138-
resizeToFitContent() {
168+
/**
169+
* Resize the textarea to fit its content.
170+
* @param force Whether to force a height recalculation. By default the height will be
171+
* recalculated only if the value changed since the last call.
172+
*/
173+
resizeToFitContent(force = false) {
139174
this._cacheTextareaLineHeight();
140175

141176
// If we haven't determined the line-height yet, we know we're still hidden and there's no point
@@ -148,7 +183,7 @@ export class MatTextareaAutosize implements AfterViewInit, DoCheck {
148183
const value = textarea.value;
149184

150185
// Only resize of the value changed since these calculations can be expensive.
151-
if (value === this._previousValue) {
186+
if (value === this._previousValue && !force) {
152187
return;
153188
}
154189

0 commit comments

Comments
 (0)