目标: 城市列表页搜索区域的功能实现
功能:在搜索城市名字或者拼音的时候,能够把搜索的结果显示出来。
搜索列表的布局与逻辑实现
<template>
<div>
<div class="search">
<input class="search-input" type="text" placeholder="请输入城市或字母">
</div>
<div class="search-content">
<ul>
<li>123</li>
</ul>
</div>
</div>
</template>
样式美化:
.search-content
overflow hidden
position absolute
top 1.58rem
left 0
right 0
bottom 0
z-index 1
background green
要想在这个区域中显示搜索结果,就需要将用户的搜索词与和数据做一个双向绑定。data中设置一个keyword,默认为空字符串,使用vue中的v-model让input框的内容与这个keyword值做一个双向绑定。
<div class="search">
<input v-model="keyword" class="search-input" type="text" placeholder="请输入城市或字母">
</div>
此外,City.vue需要向Search.vue传递cities数据:<city-search :cities="cities"></city-search>
,Search.vue接收cities数据:
props: {
cities: Object
}
Search.vue中在data里面定义一个list空数组,并设置一个watch侦听器来侦听keyword的值,同样的,为了提高性能,我们采用节流的方式:在data中初始化一个值为null的timer,即:
watch: {
keyword () {
if (this.timer) {
clearTimeout(this.timer)
}
this.timer = setInterval(() => {
}, 100);
}
}
具体的逻辑实现:
this.timer = setInterval(() => {
const result = []
for (let i in this.cities) {
this.cities[i].forEach((value) => {
if (value.spell.indexOf(this.keyword) > -1 || value.name.indexOf(this.keyword) > -1) {
result.push(value)
}
});
}
this.list = result
}, 100);
代码分析:
在cities中,每一项数据是这样的:
"A": [{
"id": 56,
"spell": "aba",
"name": "阿坝"
}
循环遍历cities中的数据,判断用户搜索的中文关键词或者拼音是否在这个数据库当中,如果存在的话就把它们放在一个数组里面,最终将这个结果给到this.list用于渲染到页面上的搜索列表当中:
<div class="search-content">
<ul>
<li class="search-item border-bottom" v-for="item of list" :key="item.id">
{{item.name}}
</li>
</ul>
</div>
至此,页面上的搜索功能就能实现了,接下来再做进一步的样式优化:
.search-item
line-height .62rem
padding-left .2rem
background #fff
color #666
此时会发现,当我们搜索一个城市的名字,比如果是“阿”开头的,就会显示出很多“阿”开头的城市名字,但是此时的页面无法滚动,超出页面的部分无法查看,我们借助better-scroll插件来实现页面的局部滚动功能(使用这个插件之前不要忘记引入)。
首先,在class="search-content"
的div上添加一个ref属性,然后借助生命周期钩子:
mounted () {
this.scroll = new Bscroll(this.$refs.search)
}
就可以实现搜索列表的局部滚动了。
但是此时会发现,当我在输入框中删除“阿”后,搜索列表还是显示在那里,所以需要进行一个小的优化。在侦听这个keyword的值的时候,判断当这个keyword不存在时候,就将这个list设置为一个空数组然后return就可以了。
当用户输入的数据在数据库中不存在的时候,搜索列表中没有内容,这时可以给用户一个"没有找到匹配数据"的提示:
<li class="search-item border-bottom">没有找到匹配数据</li>
但是会发现,这个提示语句在一开始搜索的时候以及搜索过程中都会显示,我们想要的效果是,当搜索列表中有数据的时候不显示提示信息,当匹配不到用户搜索的数据的时候才会显示出来。可以使用v-show
来实现:
<li class="search-item border-bottom" v-show="!list.length">没有找到匹配数据</li>
上述的效果实现了,但是这个列表区域把之前写过的城市列表全部遮挡住了。这个搜索列表区域的显示语法可以通过一个变量来控制:
<div
class="search-content"
ref="search"
v-show="keyword"
>
<ul>
<li
class="search-item border-bottom"
v-for="item of list"
:key="item.id"
>
{{item.name}}
</li>
<li class="search-item border-bottom" v-show="!list.length">没有找到匹配数据</li>
</ul>
</div>
这就表示,当keyword有值的时候就会显示,没有值的时候就会隐藏。
代码小优化
<li class="search-item border-bottom" v-show="!list.length">没有找到匹配数据</li>
在上面的代码中,v-show指令中有一个取反的运算符!, 我们应该避免在模板当中出现逻辑性的描述。所以,可以定义一个计算属性:
hasNoData () {
return !this.list.length
}
所以模板中就变成了:
<li class="search-item border-bottom" v-show="hasNoData">没有找到匹配数据</li>