Django-Vue搭建个人博客:搜索文章
5397 views, 2021/03/23 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
的用法详见文档。
这就搞定了。随便搜索点东西看看效果:
翻页试试,看看路径和文章数据的变化,应该都是正常工作的。