diff --git a/src/demo-app/input/input-demo.html b/src/demo-app/input/input-demo.html
index 2130e211064e..c1a265b808d1 100644
--- a/src/demo-app/input/input-demo.html
+++ b/src/demo-app/input/input-demo.html
@@ -75,6 +75,31 @@
Textarea
+
+ Expanding textarea
+
+
+
+
+
+
+ With `rows` explicitly set
+
+
+
+
+
+
+
Hello ,
diff --git a/src/lib/input/input.html b/src/lib/input/input.html
index 5b6924d94813..10368dc3d34c 100644
--- a/src/lib/input/input.html
+++ b/src/lib/input/input.html
@@ -51,6 +51,7 @@
[attr.rows]="rows"
[attr.wrap]="wrap"
[autofocus]="autofocus"
+ [mdAutosize]="autosize"
[disabled]="disabled"
[id]="inputId"
[attr.maxlength]="maxlength"
diff --git a/src/lib/input/input.ts b/src/lib/input/input.ts
index 3d8b10a3c613..b11cbccc6e6a 100644
--- a/src/lib/input/input.ts
+++ b/src/lib/input/input.ts
@@ -5,6 +5,8 @@ import {
Input,
Directive,
AfterContentInit,
+ OnInit,
+ HostListener,
ContentChild,
SimpleChange,
ContentChildren,
@@ -87,6 +89,126 @@ export class MdHint {
@Input() align: 'start' | 'end' = 'start';
}
+
+/**
+ * Enables auto expanding behaviour for the textarea
+ * based on: https://github.com/stevepapa/angular2-autosize
+ */
+@Directive({
+ selector: 'textarea[mdAutosize]'
+})
+export class MdAutosize implements OnInit {
+ // public because of AOT
+ @HostListener('input')
+ _onChange() {
+ if (this._isActive) {
+ this._adjust();
+ }
+ }
+
+ // public because of AOT
+ @Input('mdAutosize')
+ _isActive: boolean;
+
+ // public because of AOT
+ // TODO: change to a setter, to recalculate on change
+ @Input('attr.rows')
+ _rows: string;
+
+ private _height: number;
+ private _lineHeight: number;
+
+ constructor(private _elRef: ElementRef) {}
+
+ ngOnInit() {
+ console.log(this._isActive);
+
+ if (this._isActive) {
+ this._elRef.nativeElement.style.overflow = 'hidden';
+
+ // check is rows is explicitly set, otherwise we don't need to
+ // care about it(and getting the actual line-height) at all
+ if (this._rows) {
+ this._lineHeight = this._getLineHeight();
+ this._elRef.nativeElement.style.minHeight = `${+this._rows * this._lineHeight}px`;
+ }
+
+ this._adjust();
+ }
+ }
+
+ private _adjust() {
+ // reset height(and rows if it was set by the user)
+ // to the default values to properly calculate new height
+ this._elRef.nativeElement.style.height = 'auto';
+
+ // only need to do this if user explicitly set rows
+ if (this._rows) {
+ this._elRef.nativeElement.rows = this._rows;
+ }
+
+ this._height = +this._elRef.nativeElement.scrollHeight;
+
+ // only need to do this if user explicitly set rows
+ if (this._rows) {
+ this._elRef.nativeElement.rows = this._getCalculatedRows();
+ }
+
+ this._elRef.nativeElement.style.height = `${this._height}px`;
+ }
+
+ private _getCalculatedRows() {
+ return Math.round(this._height / this._lineHeight);
+ }
+
+ private _getLineHeight(): number {
+ const el: HTMLTextAreaElement = this._elRef.nativeElement;
+
+ // get the actual computed styles for the element
+ const computedStyles = window.getComputedStyle(el);
+
+ // if line height is explicitly set(to a pixel value), use that
+ if (computedStyles.lineHeight && computedStyles.lineHeight.toLowerCase().indexOf('px') >= 0) {
+ // return stripped of the "px" and as a number
+ return parseFloat(computedStyles.lineHeight);
+ }
+
+ // create temporary element
+ const tempEl = document.createElement(el.nodeName);
+
+ // reset styling, visually hiden the element
+ // and set its font styles to match the ones of our textarea
+ tempEl.setAttribute('rows', '1');
+ tempEl.setAttribute(
+ 'style',
+ `
+ margin: 0px;
+ padding: 0px;
+ visibility: hidden;
+ opacity: 0;
+ font-family: ${computedStyles.fontFamily};
+ font-size: ${computedStyles.fontSize};
+ `
+ );
+
+ // fill in one row
+ tempEl.value = '-';
+
+ // append to parent element to correctly inherit same values
+ // as the actual textarea
+ el.parentNode.appendChild(tempEl);
+
+ // get the actual line height
+ const lineHeight = tempEl.clientHeight;
+
+ // cleanup
+ tempEl.parentNode.removeChild(tempEl);
+
+ return lineHeight;
+ }
+}
+
+
/**
* Component that represents a text input. It encapsulates the HTMLElement and
* improve on its behaviour, along with styling it according to the Material Design.
@@ -174,6 +296,7 @@ export class MdInput implements ControlValueAccessor, AfterContentInit, OnChange
private _floatingPlaceholder: boolean = true;
private _autofocus: boolean = false;
+ private _autosize: boolean = false;
private _disabled: boolean = false;
private _readonly: boolean = false;
private _required: boolean = false;
@@ -203,6 +326,10 @@ export class MdInput implements ControlValueAccessor, AfterContentInit, OnChange
get spellcheck(): boolean { return this._spellcheck; }
set spellcheck(value) { this._spellcheck = coerceBooleanProperty(value); }
+ // textarea-specific
+ @Input()
+ get autosize(): boolean { return this._autosize; }
+ set autosize(value) { this._autosize = coerceBooleanProperty(value); }
private _blurEmitter: EventEmitter = new EventEmitter();
private _focusEmitter: EventEmitter = new EventEmitter();
@@ -360,9 +487,9 @@ export class MdInput implements ControlValueAccessor, AfterContentInit, OnChange
@NgModule({
- declarations: [MdPlaceholder, MdInput, MdHint],
+ declarations: [MdPlaceholder, MdInput, MdHint, MdAutosize],
imports: [CommonModule, FormsModule],
- exports: [MdPlaceholder, MdInput, MdHint],
+ exports: [MdPlaceholder, MdInput, MdHint, MdAutosize],
})
export class MdInputModule {
static forRoot(): ModuleWithProviders {