Django-Vue搭建个人博客:搜索文章
6515 views, 2023/10/16 updated Go to Comments
前面 Django 开发的部分已经实现了搜索接口,本章就基于此将其对应的搜索功能补充完整。
准备工作
为了让用户在博客的所有页面都能找到搜索框,将它放到页眉里是个不错的注意。
修改 BlogHeader.vue
,将搜索框的外观放进去:
<!-- frontend/src/components/BlogHeader.vue -->
<template>
<div id="header">
<div class="grid">
<div></div>
<h1>My Drf-Vue Blog</h1>
<div class="search">
<form>
<input type="text" placeholder="输入搜索内容...">
<button></button>
</form>
</div>
</div>
<hr>
</div>
</template>
<script>
export default {
name: 'BlogHeader'
}
</script>
<style scoped>
#header {
text-align: center;
margin-top: 20px;
}
.grid {
display: grid;
grid-template-columns: 1fr 4fr 1fr;
}
.search {
padding-top: 22px;
}
/* css 来源:https://blog.csdn.net/qq_39198420/article/details/77973339*/
* {
box-sizing: border-box;
}
form {
position: relative;
width: 200px;
margin: 0 auto;
}
input, button {
border: none;
outline: none;
}
input {
width: 100%;
height: 35px;
padding-left: 13px;
padding-right: 46px;
}
button {
height: 35px;
width: 35px;
cursor: pointer;
position: absolute;
}
.search input {
border: 2px solid gray;
border-radius: 5px;
background: transparent;
top: 0;
right: 0;
}
.search button {
background: gray;
border-radius: 0 5px 5px 0;
width: 45px;
top: 0;
right: 0;
}
.search button:before {
content: "搜索";
font-size: 13px;
color: white;
}
</style>
代码大部分是在定义搜索框的外观,有兴趣的同学自行研究。
现在你的博客标题的右边应该就出现一个像模像样的搜索框了(无功能)。
接下来开始正式编写搜索的逻辑。
编程式导航
为了让搜索框发挥功能,继续修改 BlogHeader.vue
:
<!-- frontend/src/components/BlogHeader.vue -->
<template>
...
<div class="search">
<form>
<input v-model="searchText" type="text" placeholder="输入搜索内容...">
<button v-on:click.prevent="searchArticles"></button>
</form>
</div>
...
</template>
<script>
export default {
name: 'BlogHeader',
data: function () {
return {
searchText: ''
}
},
methods: {
searchArticles() {
const text = this.searchText.trim();
if (text.charAt(0) !== '') {
this.$router.push({name: 'Home', query: { search: text }})
}
}
}
}
</script>
...
v-model
指令可以在表单控件上创建双向数据绑定。具体来说,就是上面的<input>
中的数据和 Vue 管理的searchText
数据绑定在一起了,其中一个发生变化,另一个也会改变。v-on:click
绑定了按钮的鼠标点击事件,即点击则触发searchArticles()
方法。.prevent
用于阻止按钮原本的表单提交功能。
前面章节我们用 <router-link>
标签实现了路由跳转。在必要时候路由跳转也可以通过脚本来动态实现,也就是上面代码的 this.$router.push(...)
了。注意 this.$route
和 this.$router
,前者代表路径对象,后者代表路由器对象。
总之,点击按钮触发 searchArticles()
,然后此方法将 searchText
作为参数跳转到新的路径。
正餐
页面跳转实现了,但是因为前面章节把 get_article_data()
方法中的 url 写死为 '/api/article'
了,所以跳转之后还不能够根据路径的中 search
参数展示筛选后的数据。因此要换个战场,在 ArticleList.vue
里进行修改(主要是 Javascript 部分)。
旧的翻页 <router-link>
仅考虑了路径参数中的 page
值。为了在翻页后取得包括 page
和 search
的正确路径,新写一个方法 get_path()
:
<!-- frontend/src/components/ArticleList.vue -->
...
<script>
...
export default {
...
methods: {
...
get_path: function (direction) {
let url = '';
try {
switch (direction) {
case 'next':
if (this.info.next !== undefined) {
url += (new URL(this.info.next)).search
}
break;
case 'previous':
if (this.info.previous !== undefined) {
url += (new URL(this.info.previous)).search
}
break;
}
}
catch { return url }
return url
}
},
...
}
</script>
...
如果下一页的路径存在,那么则返回其带参数的路径,否则就返回无任何参数的首页路径。
有了 get_path()
获取到路径后,还需要将路径用到请求数据的接口里。
修改 get_article_data()
方法,如下面这样:
<!-- frontend/src/components/ArticleList.vue -->
...
<script>
...
export default {
...
methods: {
...
get_article_data: function () {
let url = '/api/article';
let params = new URLSearchParams();
// 注意 appendIfExists 方法是原生没有的
// 原生只有 append 方法,但此方法不能判断值是否存在
params.appendIfExists('page', this.$route.query.page);
params.appendIfExists('search', this.$route.query.search);
const paramsString = params.toString();
if (paramsString.charAt(0) !== '') {
url += '/?' + paramsString
}
axios
.get(url)
.then(response => (this.info = response.data))
}
},
...
}
</script>
...
这里的代码抛弃了之前用的字符串拼接的方式,改为专门用于处理路径参数的 URLSearchParams()
对象。为了将路径中已有的参数添加到 URLSearchParams()
中,可以用其本身的 append()
方法,但此方法不能判断值是否存在,从而获得类似 http://localhost:8080/?page=undefined
这种错误的路径。
解决方法可以在 methods
里写一个 appendIfExists()
方法,调用它来排除错误路径。还有一种方法是由于 JavaScript 是基于原型链的语言,因此可以通过原型链将此方法添加到已有对象中(包括内置原生对象),以扩展此对象的功能。
具体实施方法就是在 main.js
中写入:
// frontend/src/main.js
import ...
URLSearchParams.prototype.appendIfExists = function (key, value) {
if (value !== null && value !== undefined) {
this.append(key, value)
}
};
createApp(App)...;
因为 main.js
在 Vue 初始化时必然会执行,如此一来URLSearchParams
对象就有了这个 appendIfExists()
了。
警告:以上示例仅供参考。请谨慎扩展原生类型,尤其是如果你的代码将被其他人使用,这可能导致意外的代码行为。建议在扩展方法的名称前加上一些标识符,以便潜在用户可以区分你注入的方法和原生的方法。
关于继承、原型链的解释,详见原型链。关于 URLSearchParams,详见URLSearchParams。
把这些脚本写好后,就可以修改路由的模板了:
<!-- frontend/src/components/ArticleList.vue -->
<template>
...
<div id="paginator">
<span v-if="...">
<router-link :to="get_path('previous')">
Prev
</router-link>
</span>
<span class="...e">
...
</span>
<span v-if="...">
<router-link :to="get_path('next')">
Next
</router-link>
</span>
</div>
</template>
...
:to
是 v-bind:to
的简写,意思是“将 to
属性和 get_path(...)
的返回值保持一致”。如果不需要这种响应式行为,也可以 to="/abc"
这样直接赋值给属性。
v-bind
的用法详见文档。
这就搞定了。随便搜索点东西看看效果:
翻页试试,看看路径和文章数据的变化,应该都是正常工作的。