- 前端
- UI组件
- AI 应用
【免费下载链接】MateChat
前端智能化场景解决方案UI库,轻松构建你的AI应用,我们将持续完善更新,欢迎你的使用与建议。 官网地址:https://matechat.gitcode.com
本篇基于 MateChat 的 Angular 组件库@matechat/ng中的 List 列表组件编写,系统梳理mc-list的全部输入参数、事件、插槽与类型定义,并结合仓库源码还原其选中高亮、懒加载触发阈值与键盘快捷键的实现细节,帮助你在历史对话列表、下拉选择、标签展示等 AI 聊天界面场景中直接落地该组件。
组件定位与引入方式
List 组件(选择器mc-list)用于展示一组内容或选项,适用于历史对话列表呈现、下拉选项、标签展示等场景。它由两部分组成:
- 组件层:
ListComponent(standalone 组件),负责输入绑定、事件派发、键盘监听与模板渲染; - 公共层:
components-common/List下的 Foundation、类型与样式,负责类名计算与视觉呈现。
从 list.component.ts 可以看到组件声明为selector: 'mc-list',而 list.module.ts 将其包装为ListModule对外导出。引入方式按当前仓库文档的说明:
import { ListModule } from '@matechat/ng';类型定义(ListItemData、ListDirection、ListVariant)通过 index.ts 一并导出,最终经由 public-api.ts 暴露给包使用者。根据 package.json,当前库版本为 20.1.0,peer 依赖要求@angular/core/@angular/common^20.3.0。
输入参数(Inputs)
以下为文档给出的完整参数表(见 api.md):
| 参数名 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| data | ListItemData[] | [] | 需要显示的数据 |
| direction | ListDirection | 'vertical' | 选项排列方式,垂直或者水平 |
| autoWrap | boolean | true | 是否自动换行,仅在水平排列时生效 |
| variant | ListVariant | 'transparent' | 选项的形态,默认无背景色,悬浮和选中高亮 |
| selectable | boolean | true | 是否可选中,可选中时,点击后选项高亮 |
| enableLazyLoad | boolean | false | 是否支持懒加载 |
| enableShortKey | boolean | false | 是否支持快捷键选择 |
| inputEl | HTMLElement | -- | 当支持快捷键选择时,监听键盘事件的元素,默认为document |
在 list.component.ts#L37-L45 中,上述@Input()声明与文档默认值完全一致。此外源码中还声明了一个文档参数表未列出的输入preSelectIndex(默认值为this.enableShortKey ? 0 : -1),用于记录键盘预选中的下标,它是快捷键机制的内部状态入口,开启enableShortKey后默认预选第一项。
关于direction与autoWrap的生效关系,foundation.ts 的getListClasses()给出了精确结论:
direction === 'horizontal'时容器追加mc-list-horizontal类;- 水平排列且
autoWrap === false时再追加mc-list-nowrap类。
也就是说垂直排列时autoWrap完全不起作用,与文档“仅在水平排列时生效”的说明互相印证。
事件(Outputs)
| 事件名 | 类型 | 说明 |
|---|---|---|
| select | (e: ListItemData) => void | 点击选项时触发的事件,参数为用户传入的选项数据 |
| loadMore | (e: Event) => void | 滚动到底时触发的事件,仅在启用懒加载且排列方式为垂直时触发 |
select 事件:发出的是选项的副本。查看 list.component.ts#L132-L142 的onItemClick实现,组件在派发事件时执行的是this.select.emit({ ...clickedItem }),即对选项做了浅拷贝。这意味着你在业务代码中修改事件参数不会反向污染data里的原始对象;同时该方法在clickedItem.disabled为true时直接return,禁用项点击不会触发任何事件。
loadMore 事件:50px 阈值与方向限制。滚动监听在 list.component.html 的容器div上通过(scroll)="onListScroll($event)"绑定,处理逻辑位于 list.component.ts#L119-L131:
onListScroll(e: Event): void { if (!this.enableLazyLoad || this.direction !== ListDirection.Vertical) { return; } const targetEl = e.target as HTMLElement; const scrollHeight = targetEl.scrollHeight; const clientHeight = targetEl.clientHeight; const scrollTop = targetEl.scrollTop; if (scrollHeight - clientHeight - scrollTop < LazyLoadThreshold) { this.loadMore.emit(e); } }其中LazyLoadThreshold定义在 list-const.ts#L1,取值为50——即距底部不足 50px 时触发。注意该判断对每次 scroll 事件都会执行,源码层没有“正在加载”节流,业务侧应自行用 loading 标志位或数据总量守卫(后文懒加载示例即展示了这种写法)。
插槽:自定义选项内容
| 插槽名 | 返回值 | 说明 |
|---|---|---|
| itemTemplate | { item: ListItemData } | 自定义选项内容 |
在 Angular 模板中的实际用法是通过ng-template内容投影,引用名需为item(组件内部以@ContentChild('item')捕获,见 list.component.ts#L51):
<mc-list [data]="options"> <ng-template #item let-item> <i [class]="item.icon" style="margin-right:4px;"></i>{{ item.label }} </ng-template> </mc-list>从 list.component.html 的渲染分支可以看到插槽与variant的两种配合方式:
variant === 'none'时,选项不再包裹mc-list-item容器,模板内容原样输出,悬浮高亮、圆角等样式全部由使用者自控,适合完全自定义场景;- 其他 variant 下,模板内容被渲染进
mc-list-item的div中,若未提供插槽则回退为默认展示{{ item['label'] }}。
这一分支逻辑与官方 demo(custom-demo.component.html)中“部分自定义”和“完全自定义”两个示例一一对应。
类型定义
ListItemData
interface ListItemData { label: string; // 显示的内容 value: string | number; // 对应的值 disabled?: boolean; // 是否禁用 active?: boolean; // 是否选中 [key: string]: any; // 其他自定义字段 }源码中的定义见 list-types.ts#L11-L17,与文档一致。label是默认模板展示的内容,value是选中高亮的匹配依据(后文会说明匹配规则),disabled/active为可选状态位,索引签名则允许携带icon等任意业务字段。
ListDirection
enum ListDirection { Horizontal = 'horizontal', Vertical = 'vertical', }ListVariant
enum ListVariant { Transparent = 'transparent', // 无背景色,悬浮和选中高亮 Filled = 'filled', // 带背景色 Bordered = 'bordered', // 带边框 None = 'none', //无任何样式,完全由用户自定义 }四种形态的视觉差异由 list.scss 控制:filled使用$devui-gray-form-control-bg背景,bordered使用$devui-dividing-line边框;而悬浮/预选态统一使用$devui-list-item-hover-*变量、激活态使用$devui-list-item-active-*变量,禁用态使用$devui-disabled-*变量——因此组件外观会跟随 DevUI 主题变量自动变化。
源码级实现解析
选中高亮的匹配规则
selectable为true时,onItemClick会遍历data,将value与点击项value相等的项标记为active = true,其余置为false。这说明高亮匹配基于value而非索引或label,当你用同一份value复用于多个选项时会出现多高亮,这是使用上的隐含约束。同时该过程会直接改写传入的data数组元素,配合select事件的浅拷贝派发,形成“状态写回 data、事件发出副本”的模式。
快捷键选择:监听目标元素的确定逻辑
开启enableShortKey后,组件在ngOnInit中调用initListenDom()决定键盘事件的挂载点(list.component.ts#L92-L104):
- 未传
inputEl:监听document; - 传入的
inputEl标签名在['TEXTAREA', 'INPUT'](InputTagNames常量)中:直接监听该元素; - 其他情况:在该元素内部
querySelector('textarea') || querySelector('input'),找不到则回退到document。
即inputEl既可以是输入框本身,也可以是包含输入框的容器元素。
按键处理逻辑在onKeyDown(list.component.ts#L143-L161):
ArrowUp:preSelectIndex减 1,为 0 时环绕到末项;ArrowDown:preSelectIndex加 1,为末项时环绕到 0;Enter:仅当selectable为true时对data[preSelectIndex]执行与点击完全一致的onItemClick,从而同样派发select事件。
模板中index === preSelectIndex的选项会带mc-list-item-pre-selection类,视觉上与悬浮态一致,形成“键盘移动 → 高亮跟随 → 回车确认”的连贯操作流,这正是文档 demo 中所述“下拉列表场景的连贯性操作”。
需要注意生命周期细节:ngOnDestroy中会调用removeListener()解绑 keydown,但由于源码中addEventListener与removeEventListener分别调用了this.onKeyDown.bind(this)(两次 bind 产生不同函数引用),从源码结构看该解绑可能不会真正生效;如果你的应用存在大量 List 实例频繁销毁重建,从源码结构看值得留意监听器在document上累积的可能性。
懒加载与默认高度
容器样式.mc-list默认width: 100%; max-height: 300px; overflow: auto(list.scss#L3-L7),这是文档 demo 中“组件默认设置列表最大高度为 300px,可通过style或class覆盖”说法的来源。水平排列时容器为display: flex; flex-wrap: wrap; gap: 12px,mc-list-nowrap下改为flex-wrap: nowrap且项不参与伸缩。
实战示例(来自 demo 应用)
以下示例取自仓库内的演示工程 demo-app,均可直接改写进你的业务组件。
基本用法:选中类场景
选项通过disabled设置禁用、active设置选中,默认悬浮高亮、点击选中高亮(basic-demo.component.html、basic-demo.component.ts):
<div class="demo-container"> <mc-list [data]="options" (select)="onSelect($event)"></mc-list> </div>get options() { return new Array(6).fill(0).map((_item, i) => ({ label: `Option ${i + 1}`, value: i + 1, disabled: i === 3, // 第 4 项禁用 active: i === 1, // 第 2 项初始选中 })); } onSelect(option: any) { console.log('list selected', option); }水平排列:展示类场景
direction设置排列方式,variant设置形态,autoWrap控制水平换行,selectable="false"禁用点击选中(display-demo.component.html):
<mc-list [data]="options" [selectable]="false" [autoWrap]="autoWrap" [direction]="direction" [variant]="selectedVariant" ></mc-list>快捷键:与输入框联动
先获取目标输入框元素,再传给inputEl(shortcut-demo.component.html、shortcut-demo.component.ts):
<input type="text" id="list-shortcut-demo-input" [(ngModel)]="inputValue"> <mc-list [data]="options" [enableShortKey]="true" [inputEl]="inputEl" (select)="handleSelect($event)"></mc-list>ngOnInit() { this.inputEl = document.getElementById('list-shortcut-demo-input') as HTMLInputElement; } handleSelect(evt: any) { this.inputValue = evt.label; // 键盘确认后回显 }懒加载:滚动加载分页数据
启用enableLazyLoad并在loadMore中做加载守卫(lazyload-demo.component.html、lazyload-demo.component.ts)。注意这里通过style="height:400px"覆盖了默认 300px 最大高度,并用dLoading包裹展示加载态:
<div dLoading [showLoading]="loading"> <mc-list [data]="options" [enableLazyLoad]="true" (loadMore)="onLoadMore($event)" style="height:400px"></mc-list> </div>loading = false; total = 30; onLoadMore(_option: any) { if (this.loading || this.options.length >= this.total) { return; } this.loading = true; setTimeout(() => { const newData = new Array(10).fill(0).map((_item, i) => ({ label: `Option ${this.options.length + i + 1}`, value: this.options.length + i + 1, })); this.options.push(...newData); this.loading = false; }, 1500); }该示例同时印证了前文两点:loadMore依赖垂直排列(水平排列时onListScroll直接返回),以及组件自身不做加载节流,loading与总量守卫必须由业务侧提供。
关键文件索引
| 内容 | 路径 |
|---|---|
| 参数/事件/插槽/类型文档 | docs/components-ng/list/api.md |
| 场景 demo 说明 | docs/components-ng/list/demo.md |
| 组件实现 | packages/components-ng/projects/components-ng/src/List/list.component.ts |
| 模板渲染 | packages/components-ng/projects/components-ng/src/List/list.component.html |
| 模块与导出 | packages/components-ng/projects/components-ng/src/List/index.ts |
| 类型与常量 | packages/components-ng/projects/components-ng/src/components-common/List/common/list-types.ts、list-const.ts |
| 样式 | packages/components-ng/projects/components-ng/src/components-common/List/common/list.scss |
- 前端
- UI组件
- AI 应用
【免费下载链接】MateChat
前端智能化场景解决方案UI库,轻松构建你的AI应用,我们将持续完善更新,欢迎你的使用与建议。 官网地址:https://matechat.gitcode.com
相关推荐
MateChat Angular 版 Input 组件详解:mc-input 参数、事件与格式化录入实现原理
MateChat Angular 版 Input 组件详解:mc input 参数、事件与格式化录入实现原理 MateChat 是面向前端智能化场景的 UI 组
前端UI组件AI 应用MateChat @matechat/ng Introduction 介绍组件详解:mc-introduction 参数、插槽与源码实现
MateChat @matechat/ng Introduction 介绍组件详解:mc introduction 参数、插槽与源码实现 MateChat 的
前端UI组件AI 应用MateChat Introduction 组件实战:Angular 版 @matechat/ng 中 AI 应用介绍页的完整配置与实现剖析
MateChat Introduction 组件实战:Angular 版 @matechat/ng 中 AI 应用介绍页的完整配置与实现剖析 本篇技术指南以 M
前端UI组件AI 应用
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考