Angular 4中实现textarea的@提及自动补全功能求助
嘿,我刚好在Angular 4里做过类似的@提及功能,给你分享几个关键的实现思路和代码片段,应该能帮到你:
实现Angular 4 Textarea @提及功能的思路
1. 基础组件结构搭建
首先在组件模板里放好textarea和用于展示用户列表的容器,用*ngIf控制列表的显示隐藏,同时给textarea绑定输入、键盘事件,用ViewChild获取textarea的DOM引用方便后续操作。
模板示例:
<div class="mention-container"> <button (click)="insertAtSymbol()">@</button> <textarea [(ngModel)]="message" (input)="onInputChange()" (keydown)="onKeyDown($event)" #messageTextarea rows="4" ></textarea> <!-- 用户提及列表 --> <div *ngIf="showUserList" class="user-mention-list"> <div *ngFor="let user of filteredUsers; let i = index" (click)="selectUser(user)" class="user-item" [class.active]="selectedIndex === i" > {{user.name}} </div> </div> </div>
2. 核心逻辑:监听输入与检测@符号
在组件类里,我们需要监听textarea的输入事件,判断用户是否输入了@符号,或者处于@开头的输入状态,以此来触发用户列表的显示和过滤。
关键代码片段:
import { Component, ViewChild, ElementRef } from '@angular/core'; @Component({ selector: 'app-mention-textarea', templateUrl: './mention-textarea.component.html', styleUrls: ['./mention-textarea.component.css'] }) export class MentionTextareaComponent { message = ''; showUserList = false; allUsers = [ { id: 1, name: 'Alice Smith' }, { id: 2, name: 'Bob Johnson' }, { id: 3, name: 'Charlie Brown' } ]; filteredUsers = [...this.allUsers]; selectedIndex = -1; // 用于标记列表中选中的用户 @ViewChild('messageTextarea') textareaRef!: ElementRef<HTMLTextAreaElement>; // 处理输入事件,检测@并过滤用户 onInputChange() { const textarea = this.textareaRef.nativeElement; const cursorPos = textarea.selectionStart; const textBeforeCursor = this.message.substring(0, cursorPos); // 找到最后一个@的位置,判断是否处于提及状态 const lastAtIndex = textBeforeCursor.lastIndexOf('@'); if (lastAtIndex === -1) { this.showUserList = false; return; } // 提取@后面的输入内容,用于过滤用户 const searchQuery = textBeforeCursor.substring(lastAtIndex + 1).trim(); if (searchQuery.length === 0 && lastAtIndex === textBeforeCursor.length - 1) { // 刚输入@,显示全部用户 this.filteredUsers = [...this.allUsers]; this.showUserList = true; } else { // 根据输入内容过滤用户 this.filteredUsers = this.allUsers.filter(user => user.name.toLowerCase().includes(searchQuery.toLowerCase()) ); this.showUserList = this.filteredUsers.length > 0; } } // 处理键盘事件:上下箭头切换选中项,回车选中 onKeyDown(event: KeyboardEvent) { if (!this.showUserList) return; switch(event.key) { case 'ArrowDown': event.preventDefault(); this.selectedIndex = (this.selectedIndex + 1) % this.filteredUsers.length; break; case 'ArrowUp': event.preventDefault(); this.selectedIndex = this.selectedIndex === 0 ? this.filteredUsers.length - 1 : this.selectedIndex - 1; break; case 'Enter': event.preventDefault(); if (this.selectedIndex !== -1) { this.selectUser(this.filteredUsers[this.selectedIndex]); } break; case 'Escape': this.showUserList = false; break; } } // 选中用户后插入到textarea中 selectUser(user: { id: number; name: string }) { const textarea = this.textareaRef.nativeElement; const cursorPos = textarea.selectionStart; const textBeforeCursor = this.message.substring(0, cursorPos); const lastAtIndex = textBeforeCursor.lastIndexOf('@'); // 替换@到光标之间的内容为@用户名,后面加空格方便继续输入 const updatedMessage = this.message.substring(0, lastAtIndex) + `@${user.name} ` + this.message.substring(cursorPos); this.message = updatedMessage; this.showUserList = false; this.selectedIndex = -1; // 重置光标位置到用户名后的空格处 setTimeout(() => { const newCursorPos = lastAtIndex + user.name.length + 2; textarea.selectionStart = textarea.selectionEnd = newCursorPos; }, 0); } // 点击@按钮时插入@符号并触发列表 insertAtSymbol() { const textarea = this.textareaRef.nativeElement; const cursorPos = textarea.selectionStart; const [textBefore, textAfter] = [ this.message.substring(0, cursorPos), this.message.substring(cursorPos) ]; this.message = `${textBefore}@${textAfter}`; // 把光标移到@后面 textarea.selectionStart = textarea.selectionEnd = cursorPos + 1; // 触发输入检测,显示用户列表 this.onInputChange(); } }
3. 样式优化,让列表更像自动补全框
给用户列表添加样式,让它定位在textarea下方,有边框和滚动,鼠标 hover 时有高亮:
.mention-container { position: relative; width: 300px; } .user-mention-list { position: absolute; top: 100%; left: 0; right: 0; background: #fff; border: 1px solid #e0e0e0; border-radius: 4px; max-height: 200px; overflow-y: auto; box-shadow: 0 2px 4px rgba(0,0,0,0.1); z-index: 1000; } .user-item { padding: 8px 12px; cursor: pointer; } .user-item:hover, .user-item.active { background-color: #f5f5f5; }
额外注意点
- 如果你需要从后端获取用户列表,可以在
onInputChange里调用HTTP请求,记得加防抖(比如用setTimeout和清除定时器),避免频繁请求。 - 处理多个@的情况:上面的逻辑已经通过找最后一个@来处理,所以支持多次提及。
- Angular 4里
ngModel需要导入FormsModule到你的模块里,不然会报错。
内容的提问来源于stack exchange,提问作者Ankit Patel
相关产品推荐
相关产品推荐

