>> 前往stackblitz編輯代碼
核心思路
- 創建兩個textarea,這裏暫取名爲text和text1。(最後會將text1隱藏,調試時先讓text1顯示)。
- 將text1的高度和rows設置爲僅能輸入一行,這麼做是爲了用元素的scrollHeight表示其內容的高度。
- 用戶將在text中輸入,我們將輸入的值同步綁定到text1中,並通過text1的scrollHeight獲取輸入內容的高度,並同步改變text的height。
實現
準備工作
- 首先,新建一個模塊textarea.module.ts,並引入FormsModule,因爲接下來將會用到ngModel進行雙向數據綁定。
- 在模塊內新建一個組件textarea。
- 在模塊內exports出該組件。以後只需引入該模塊即可使用該組件。
實作
- 在組件模板內寫兩個textarea,並標記爲模板變量#text和#text1。
-
在模板中數據綁定,並監聽數據變化。
<textarea (ngModelChange)="onChange()" [(ngModel)]="val" #text class="autosize" rows="1"></textarea> <textarea class="autosize hidden" rows="1" [value]="val" #text1></textarea>
-
在textarea.component.ts中增加一個輸入屬性和一個輸出屬性。輸入屬性maxHeight表示textarea的heigh的極限。輸出屬性valChange將會在用戶輸入的數據變化時發出數據。
@Input('max-height') maxHeight = 100; @Output('valChange') valChange = new EventEmitter();
-
在textarea.component.ts中寫模板中調用的onChange方法。讓text的高度始終等於text1的scrollHeight;這裏是直接操作Dom,建議最好使用Renderer2進行dom的修改。
onChange() { this.reset(); setTimeout(() => { this.valChange.emit(this.val); this.reset(); }, 0) } reset() { this.text1.nativeElement.style.width = (this.text.nativeElement.scrollWidth + 2) + 'px'; if (this.text1.nativeElement.scrollHeight < this.maxHeight) { this.text.nativeElement.style.height = (this.text1.nativeElement.scrollHeight + 2) + 'px' } }
注意1:這裏獲取scrollwidth的目的是因爲不同的瀏覽器對滾動條的呈現邏輯有差異,我們在css中已經設置了text1的overflow=hidden,始終不會讓text1出現滾動條,因此我們需要讓他的寬度始終等於text1的寬度,以保證當text出現滾動條是他的的寬度也保持一致,從而讓scrollHeight可以完美映射到text,否則會出現text中明明還沒有達到邊界,高度就自行變化了。
注意2:setTimeout中的邏輯是爲了應付事件環,因爲我們監聽的是text的變化,當text中輸入變化時,text1中通過數據綁定得到的值往往還沒有改變,需要等一個節拍。
使用
- 只需要監聽輸出屬性valChange,並傳入$event就可以獲取用戶輸入了。
-
如有需要可以在此基礎上繼續擴展,使其兼容響應式表單。
<app-yu-textarea (valChange)="onChange($event)" max-height='100' class="tex"></app-yu-textarea>
- 修改樣式需要注意選擇器的權重。