>> 前往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>
- 修改样式需要注意选择器的权重。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。