Typeahead无法展示所有匹配结果问题求助
问题
我在用Typeahead组件从下拉列表选择共享同一邮箱的家庭成员账户。输入完整邮箱后,系统会向后端搜索所有匹配项,Typeahead应该展示全部结果,但现在它会过滤掉部分匹配项,不清楚原因。
相关代码:
$input.typeahead({ minLength: 5, limit: 30, }, { name: 'accounts', displayKey: 'fullName', source: function (query, sync, async) { if (isEmail(query)) { $.getJSON('{% url 'account_finder_json' %}?email=' + query, function (data) { data.accounts.map(function (account) { account.fullName = `${account.first_name} ${account.last_name}`; }); // data.accounts hold all expected results async(data.accounts); }); } } }).on('typeahead:selected', function (e, item) { $input.typeahead('val', ''); confirmAccountSelection(item); });
测试时后端返回的两个JSON数据集:
数据集1
"[ { \"first_name\": \"Bero\", \"last_name\": \"Underfoot\", \"email\": \"bero.underfoot@email.org\", \"fullName\": \"Bero Underfoot\" }, { \"first_name\": \"Falco\", \"last_name\": \"Underfoot\", \"email\": \"bero.underfoot@email.org\", \"fullName\": \"Falco Underfoot\" }, { \"first_name\": \"Magnachar\", \"last_name\": \"Underfoot\", \"email\": \"bero.underfoot@email.org\", \"fullName\": \"Magnachar Underfoot\" } ]"
数据集2
"[ { \"first_name\": \"Bilbo\", \"last_name\": \"Brandagamba\", \"email\": \"frodo.brandagamba@email.org\", \"fullName\": \"Bilbo Brandagamba\" }, { \"first_name\": \"Frodo\", \"last_name\": \"Brandagamba\", \"email\": \"frodo.brandagamba@email.org\", \"fullName\": \"Frodo Brandagamba\" }, { \"first_name\": \"Merry\", \"last_name\": \"Brandagamba\", \"email\": \"frodo.brandagamba@email.org\", \"fullName\": \"Merry Brandagamba\" }, { \"first_name\": \"Pippin\", \"last_name\": \"Brandagamba\", \"email\": \"frodo.brandagamba@email.org\", \"fullName\": \"Pippin Brandagamba\" } ]"
实际表现:
- 搜索
bero.underfoot@email.org时,建议框仅显示“Bero”和“Falco”,遗漏了“Magnachar” - 搜索
frodo.brandagamba@email.org时,仅显示“Bilbo”,其余均被过滤 - 补充:如果在数据集中添加“Abel Underfoot”,它会成为
bero.underfoot@email.org搜索的唯一匹配项
解决方案
问题出在Typeahead默认的匹配策略上:Typeahead会自动用当前输入的query(即你输入的邮箱)去匹配数据源中displayKey指定的字段(这里是fullName),只有匹配上的项才会被展示。而你的场景中,query是邮箱字符串,fullName是姓名,两者几乎没有匹配关系,导致大部分结果被错误过滤,只有少数项因姓氏或名字的巧合被保留。
要解决这个问题,只需强制Typeahead不做默认过滤,直接展示后端返回的所有结果。在数据源配置中添加filter函数,让它直接返回全部数据即可:
$input.typeahead({ minLength: 5, limit: 30, }, { name: 'accounts', displayKey: 'fullName', source: function (query, sync, async) { if (isEmail(query)) { $.getJSON('{% url 'account_finder_json' %}?email=' + query, function (data) { data.accounts.map(function (account) { account.fullName = `${account.first_name} ${account.last_name}`; }); async(data.accounts); }); } }, // 添加该函数,跳过Typeahead的默认过滤逻辑 filter: function(results) { return results; } }).on('typeahead:selected', function (e, item) { $input.typeahead('val', ''); confirmAccountSelection(item); });
如果使用的是基于Bloodhound引擎的Typeahead版本,更规范的做法是直接配置Bloodhound并关闭过滤:
// 初始化Bloodhound引擎 var accounts = new Bloodhound({ datumTokenizer: Bloodhound.tokenizers.obj.whitespace('fullName'), queryTokenizer: Bloodhound.tokenizers.whitespace, remote: { url: '{% url 'account_finder_json' %}?email=%QUERY', wildcard: '%QUERY', transform: function(data) { return data.accounts.map(function(account) { account.fullName = `${account.first_name} ${account.last_name}`; return account; }); } }, // 关闭自动过滤,返回全部结果 filter: function(results) { return results; } }); // 初始化Typeahead组件 $input.typeahead({ minLength: 5, limit: 30 }, { name: 'accounts', displayKey: 'fullName', source: accounts }).on('typeahead:selected', function (e, item) { $input.typeahead('val', ''); confirmAccountSelection(item); });
核心逻辑就是:通过自定义filter函数绕过Typeahead的默认匹配过滤,确保后端返回的所有账户数据都能在下拉框中展示。
内容的提问来源于stack exchange,提问作者dabadaba
相关产品推荐
相关产品推荐

