Tổng quan chức năng
Component này hỗ trợ người dùng nhập từ khóa vào ô text, hệ thống sẽ tự động lọc danh sách phù hợp theo thời gian thực. Khi người dùng chọn một mục từ danh sách gợi ý, giá trị sẽ được cập nhật vào ô nhập liệu và gửi sự kiện về trang cha.
Các bước tích hợp
Để sử dụng component này trong dự án, hãy thực hiện theo quy trình sau:
- Sao chép thư mục component vào directory chứa các thành phần chung của project.
- Khai báo component trong file cấu hình JSON của page cần sử dụng.
- Chèn tag component vào file WXML và binding dữ liệu tương ứng.
- Xử lý sự kiện trả về trong file JS của page.
Ví dụ minh họa
1. Cấu hình JSON của Page:
{
"usingComponents": {
"smart-picker": "/components/smart-picker/smart-picker"
}
}
2. Sử dụng trong WXML:
<smart-picker
listSource="{{ clinicList }}"
presetValue="{{ currentClinic }}"
hintText="Tìm kiếm phòng khám"
bind:onChoose="handleClinicPick">
</smart-picker>
3. Xử lý logic trong JS:
Page({
data: {
clinicList: [
{ id: 101, label: 'Phòng khám A' },
{ id: 102, label: 'Phòng khám B' },
{ id: 103, label: 'Phòng khám C' }
],
currentClinic: ''
},
handleClinicPick(event) {
const item = event.detail.item;
this.setData({
currentClinic: item.label
});
}
})
Chi tiết Implementation
File logic (smart-picker.js)
Component({
properties: {
listSource: {
type: Array,
value: []
},
presetValue: {
type: String,
value: ''
},
hintText: {
type: String,
value: 'Nhập để tìm...'
}
},
data: {
dropdownActive: false,
matchList: [],
searchTerm: '',
noResult: false,
searchTimer: null
},
observers: {
listSource(newList) {
this.setData({ matchList: newList });
},
presetValue(val) {
this.setData({ searchTerm: val });
}
},
methods: {
activateDropdown() {
this.setData({ dropdownActive: true });
this.queryList(this.data.searchTerm);
},
onTextChange(e) {
const val = e.detail.value;
if (this.data.searchTimer) {
clearTimeout(this.data.searchTimer);
}
this.data.searchTimer = setTimeout(() => {
this.queryList(val);
}, 400);
},
queryList(keyword) {
if (!keyword) {
this.setData({ matchList: [], noResult: true });
return;
}
const results = this.data.listSource.filter(item => {
return item.label.indexOf(keyword) > -1;
});
this.setData({
searchTerm: keyword,
matchList: results,
noResult: results.length === 0
});
},
closeDropdown() {
this.setData({ dropdownActive: false });
},
confirmSelection(e) {
const item = e.currentTarget.dataset.item;
this.setData({
searchTerm: item.label,
dropdownActive: false,
noResult: false
});
this.triggerEvent('onChoose', { item: item });
}
}
});
File cấu hình (smart-picker.json)
{
"component": true,
"usingComponents": {},
"styleIsolation": "isolated"
}
File giao diện (smart-picker.wxml)
<view class="smart-picker">
<view class="input-wrapper">
<input
class="search-field"
type="text"
value="{{ searchTerm }}"
placeholder="{{ hintText }}"
bindfocus="activateDropdown"
bindblur="closeDropdown"
bindinput="onTextChange"
readonly="{{ !dropdownActive }}"
/>
<view class="arrow-icon {{ dropdownActive ? 'active' : '' }}"></view>
</view>
<view class="result-container" wx:if="{{ dropdownActive }}">
<view class="result-list">
<view
class="list-item {{ searchTerm == item.label ? 'highlight' : '' }}"
wx:for="{{ matchList }}"
wx:key="id"
data-item="{{ item }}"
catchtap="confirmSelection"
>
{{ item.label }}
</view>
<view class="empty-msg" wx:if="{{ noResult }}">Không tìm thấy kết quả</view>
</view>
</view>
</view>
File style (smart-picker.wxss)
.search-field {
flex: 1;
border: 0;
outline: 0;
z-index: 100;
}
.smart-picker {
position: relative;
}
.input-wrapper {
display: flex;
align-items: center;
border-bottom: 1rpx solid #e0e0e0;
padding: 18rpx 24rpx;
}
.result-container {
position: absolute;
top: 100%;
left: 0;
right: 0;
min-height: 100vh;
z-index: 20;
}
.result-list {
top: 100%;
left: 0;
right: 0;
box-sizing: border-box;
border-top: none;
max-height: 280px;
overflow-y: auto;
z-index: 20;
border-radius: 8rpx;
padding: 10rpx 16rpx 8rpx 16rpx;
box-shadow: 0rpx 0rpx 2rpx 1rpx rgba(0, 0, 0, 0.15) inset;
background-color: #ffffff;
margin: 0 24rpx;
}
.list-item {
padding: 18rpx 8rpx;
cursor: pointer;
border-bottom: 1rpx solid #f5f5f5;
}
.list-item.highlight {
background-color: #e8e8e8;
}
.list-item:last-child {
border-bottom: none;
}
.empty-msg {
padding: 12px;
text-align: center;
color: #aaa;
}
.arrow-icon::after {
content: '';
position: absolute;
top: 50%;
right: 24rpx;
transform: translateY(-50%);
width: 0;
height: 0;
border: 8rpx solid transparent;
border-top: 8rpx solid #888;
transition: transform 0.2s ease;
}
.arrow-icon.active::after {
transform: translateY(-50%) rotate(180deg);
}