diff --git a/src/cdk/text-field/BUILD.bazel b/src/cdk/text-field/BUILD.bazel
index d1436f158508..d36772408549 100644
--- a/src/cdk/text-field/BUILD.bazel
+++ b/src/cdk/text-field/BUILD.bazel
@@ -8,6 +8,7 @@ ng_module(
module_name = "@angular/cdk/text-field",
deps = [
"@rxjs",
+ "//src/cdk/coercion",
"//src/cdk/platform",
],
tsconfig = "//src/lib:tsconfig-build.json",
diff --git a/src/cdk/text-field/autosize.spec.ts b/src/cdk/text-field/autosize.spec.ts
index 746726a61885..a36240394b73 100644
--- a/src/cdk/text-field/autosize.spec.ts
+++ b/src/cdk/text-field/autosize.spec.ts
@@ -24,6 +24,7 @@ describe('CdkTextareaAutosize', () => {
AutosizeTextAreaWithContent,
AutosizeTextAreaWithValue,
AutosizeTextareaWithNgModel,
+ AutosizeTextareaWithoutAutosize,
],
});
@@ -217,6 +218,49 @@ describe('CdkTextareaAutosize', () => {
expect(autosize.resizeToFitContent).toHaveBeenCalled();
}));
+
+ it('should not trigger a resize when it is disabled', fakeAsync(() => {
+ const fixtureWithoutAutosize = TestBed.createComponent(AutosizeTextareaWithoutAutosize);
+ textarea = fixtureWithoutAutosize.nativeElement.querySelector('textarea');
+ autosize = fixtureWithoutAutosize.debugElement.query(By.css('textarea'))
+ .injector.get(CdkTextareaAutosize);
+
+ fixtureWithoutAutosize.detectChanges();
+
+ const previousHeight = textarea.clientHeight;
+
+ fixtureWithoutAutosize.componentInstance.content = `
+ Line
+ Line
+ Line
+ Line
+ Line`;
+
+ // Manually call resizeToFitContent instead of faking an `input` event.
+ fixtureWithoutAutosize.detectChanges();
+
+ expect(textarea.clientHeight)
+ .toEqual(previousHeight, 'Expected textarea to still have the same size.');
+ expect(textarea.clientHeight)
+ .toBeLessThan(textarea.scrollHeight, 'Expected textarea to a have scrollbar.');
+
+ autosize.enabled = true;
+ fixtureWithoutAutosize.detectChanges();
+
+ expect(textarea.clientHeight)
+ .toBeGreaterThan(previousHeight,
+ 'Expected textarea to have grown after enabling autosize.');
+ expect(textarea.clientHeight)
+ .toBe(textarea.scrollHeight, 'Expected textarea not to have a scrollbar');
+
+ autosize.enabled = false;
+ fixtureWithoutAutosize.detectChanges();
+
+ expect(textarea.clientHeight)
+ .toEqual(previousHeight, 'Expected textarea to have the original size.');
+ expect(textarea.clientHeight)
+ .toBeLessThan(textarea.scrollHeight, 'Expected textarea to have a scrollbar.');
+ }));
});
// Styles to reset padding and border to make measurement comparisons easier.
@@ -257,3 +301,12 @@ class AutosizeTextAreaWithValue {
class AutosizeTextareaWithNgModel {
model = '';
}
+
+@Component({
+ template: ``,
+ styles: [textareaStyleReset],
+})
+class AutosizeTextareaWithoutAutosize {
+ content: string = '';
+}
+
diff --git a/src/cdk/text-field/autosize.ts b/src/cdk/text-field/autosize.ts
index 9f9bb437126a..576f4fd9a68c 100644
--- a/src/cdk/text-field/autosize.ts
+++ b/src/cdk/text-field/autosize.ts
@@ -6,6 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/
+import {coerceBooleanProperty} from '@angular/cdk/coercion';
import {
Directive,
ElementRef,
@@ -35,10 +36,14 @@ import {fromEvent, Subject} from 'rxjs';
export class CdkTextareaAutosize implements AfterViewInit, DoCheck, OnDestroy {
/** Keep track of the previous textarea value to avoid resizing when the value hasn't changed. */
private _previousValue: string;
+ private _initialHeight: string | null;
private readonly _destroyed = new Subject();
private _minRows: number;
private _maxRows: number;
+ private _enabled: boolean = true;
+
+ private _textareaElement: HTMLTextAreaElement;
/** Minimum amount of rows in the textarea. */
@Input('cdkAutosizeMinRows')
@@ -56,13 +61,28 @@ export class CdkTextareaAutosize implements AfterViewInit, DoCheck, OnDestroy {
this._setMaxHeight();
}
+ /** Whether autosizing is enabled or not */
+ @Input('cdkTextareaAutosize')
+ get enabled(): boolean { return this._enabled; }
+ set enabled(value: boolean) {
+ value = coerceBooleanProperty(value);
+
+ // Only act if the actual value changed. This specifically helps to not run
+ // resizeToFitContent too early (i.e. before ngAfterViewInit)
+ if (this._enabled !== value) {
+ (this._enabled = value) ? this.resizeToFitContent(true) : this.reset();
+ }
+ }
+
/** Cached height of a textarea with a single row. */
private _cachedLineHeight: number;
constructor(
private _elementRef: ElementRef,
private _platform: Platform,
- private _ngZone: NgZone) {}
+ private _ngZone: NgZone) {
+ this._textareaElement = this._elementRef.nativeElement as HTMLTextAreaElement;
+ }
/** Sets the minimum height of the textarea as determined by minRows. */
_setMinHeight(): void {
@@ -86,6 +106,9 @@ export class CdkTextareaAutosize implements AfterViewInit, DoCheck, OnDestroy {
ngAfterViewInit() {
if (this._platform.isBrowser) {
+ // Remember the height which we started with in case autosizing is disabled
+ this._initialHeight = this._textareaElement.style.height;
+
this.resizeToFitContent();
this._ngZone.runOutsideAngular(() => {
@@ -103,8 +126,7 @@ export class CdkTextareaAutosize implements AfterViewInit, DoCheck, OnDestroy {
/** Sets a style property on the textarea element. */
private _setTextareaStyle(property: string, value: string): void {
- const textarea = this._elementRef.nativeElement as HTMLTextAreaElement;
- textarea.style[property] = value;
+ this._textareaElement.style[property] = value;
}
/**
@@ -119,10 +141,8 @@ export class CdkTextareaAutosize implements AfterViewInit, DoCheck, OnDestroy {
return;
}
- let textarea = this._elementRef.nativeElement as HTMLTextAreaElement;
-
// Use a clone element because we have to override some styles.
- let textareaClone = textarea.cloneNode(false) as HTMLTextAreaElement;
+ let textareaClone = this._textareaElement.cloneNode(false) as HTMLTextAreaElement;
textareaClone.rows = 1;
// Use `position: absolute` so that this doesn't cause a browser layout and use
@@ -143,9 +163,9 @@ export class CdkTextareaAutosize implements AfterViewInit, DoCheck, OnDestroy {
// See Firefox bug report: https://bugzilla.mozilla.org/show_bug.cgi?id=33654
textareaClone.style.overflow = 'hidden';
- textarea.parentNode!.appendChild(textareaClone);
+ this._textareaElement.parentNode!.appendChild(textareaClone);
this._cachedLineHeight = textareaClone.clientHeight;
- textarea.parentNode!.removeChild(textareaClone);
+ this._textareaElement.parentNode!.removeChild(textareaClone);
// Min and max heights have to be re-calculated if the cached line height changes
this._setMinHeight();
@@ -164,6 +184,11 @@ export class CdkTextareaAutosize implements AfterViewInit, DoCheck, OnDestroy {
* recalculated only if the value changed since the last call.
*/
resizeToFitContent(force: boolean = false) {
+ // If autosizing is disabled, just skip everything else
+ if (!this._enabled) {
+ return;
+ }
+
this._cacheTextareaLineHeight();
// If we haven't determined the line-height yet, we know we're still hidden and there's no point
@@ -211,6 +236,18 @@ export class CdkTextareaAutosize implements AfterViewInit, DoCheck, OnDestroy {
this._previousValue = value;
}
+ /**
+ * Resets the textarea to it's original size
+ */
+ reset() {
+ // Do not try to change the textarea, if the initialHeight has not been determined yet
+ // This might potentially remove styles when reset() is called before ngAfterViewInit
+ if (this._initialHeight === undefined) {
+ return;
+ }
+ this._textareaElement.style.height = this._initialHeight;
+ }
+
_noopInputHandler() {
// no-op handler that ensures we're running change detection on input events.
}
diff --git a/src/demo-app/input/input-demo.html b/src/demo-app/input/input-demo.html
index 850e0919f235..30f0bdcbdad9 100644
--- a/src/demo-app/input/input-demo.html
+++ b/src/demo-app/input/input-demo.html
@@ -559,6 +559,12 @@ <textarea> with ngModel
Plain textarea with auto size
+
+ <textarea> with bindable autosize
+
+ Autosize enabled
+
+
diff --git a/src/demo-app/input/input-demo.ts b/src/demo-app/input/input-demo.ts
index ccac8bf1fd9e..54178dbe7bff 100644
--- a/src/demo-app/input/input-demo.ts
+++ b/src/demo-app/input/input-demo.ts
@@ -29,6 +29,7 @@ export class InputDemo {
hideRequiredMarker: boolean;
ctrlDisabled = false;
textareaNgModelValue: string;
+ textareaAutosizeEnabled = false;
placeholderTestControl = new FormControl('', Validators.required);
name: string;
diff --git a/src/lib/input/autosize.ts b/src/lib/input/autosize.ts
index 7d24c0391c0c..fd4f50c6f9cf 100644
--- a/src/lib/input/autosize.ts
+++ b/src/lib/input/autosize.ts
@@ -34,4 +34,12 @@ export class MatTextareaAutosize extends CdkTextareaAutosize {
@Input()
get matAutosizeMaxRows(): number { return this.maxRows; }
set matAutosizeMaxRows(value: number) { this.maxRows = value; }
+
+ @Input('mat-autosize')
+ get matAutosize(): boolean { return this.enabled; }
+ set matAutosize(value: boolean) { this.enabled = value; }
+
+ @Input()
+ get matTextareaAutosize(): boolean { return this.enabled; }
+ set matTextareaAutosize(value: boolean) { this.enabled = value; }
}