前端视图detailArticleView.ftl代码:
<!DOCTYPE html>
<html>
<#include "common/head.ftl">
<body>
<#include "common/navbar.ftl">
<div class="container">
<h3>${article.title}</h3>
<h6>${article.author}</h6>
<h6>${article.gmtModified}</h6>
<div>${article.content}</div>
</div>
</body>
</html>
我们在文章列表页中,给每篇文章标题加上跳转文章详情的超链接:
<td><a target="_blank" href="detailArticleView?id=${article.id}">${article.title}</a></td>
现在我们的文章列表页面如下:
点击一篇文章标题,即可进入详情页:
11.16 添加Markdown支持
我们写技术博客文章,最常用的就是使用Markdown了。我们来为我们的博客添加Markdown的支持。我们使用前端js组件Mditor来支持Markdown的编辑。 Mditor是一个简洁、易于集成、方便扩展、期望舒服的编写 markdown 的编辑器。
11.16.1 引入静态资源
<link href="/mditor-master/dist/css/mditor.css" rel="stylesheet">
<script src="/mditor-master/dist/js/mditor.js"></script>
11.16.2 初始化Mditor
我们在写文章的页面addArticleView.ftl,初始化Mditor如下:
<script>
$(function () {
//写文章 mditor
var mditor = Mditor.fromTextarea(document.getElementById('articleContentEditor'));
//是否打开分屏
mditor.split = true; //打开
//是否打开预览
mditor.preivew = true; //打开
//是否全屏
mditor.fullscreen = false; //关闭
//获取或设置编辑器的值
mditor.on('ready', function () {
mditor.value = '# ';
});
hljs.initHighlightingOnLoad();
//源码高亮
$('pre code').each(function (i, block) {
hljs.highlightBlock(block);
});
})
</script>
另外,我们还使用了代码高亮插件highlight.js 。
这样,写文章的页面对应的textarea区域就变成了支持Markdown在线编辑+预览的功能了:
11.16.3 文章详情显示Markdown渲染
下面我们来使我们的详情页也能支持Markdown的渲染显示。
详情页的视图文件detailArticleView.ftl如下:
<!DOCTYPE html>
<html>
<#include "common/head.ftl">
<body>
<#include "common/navbar.ftl">
<div class="container">
<h3>${article.title}</h3>
<h6>${article.author}</h6>
<h6>${article.gmtModified}</h6>
<textarea id="articleContentShow" placeholder="<#escape x as x?html>${article.content}</#escape>" style="display:
none"></textarea>
<div id="article-content" class="markdown-body"></div>
</div>
</body>
</html>
这里我们把文章内容放到一个隐藏的textarea的placeholder属性中:
<textarea id="articleContentShow" placeholder="<#escape x as x?html>${article.content}</#escape>" style="display:
none"></textarea>
注意,这里我们作了字符的转义escape,防止有特殊字符导致页面显示错乱。 然后,我们在js中获取这个内容:
<script>
$(function () {
// 文章详情 mditor
var parser = new Mditor.Parser();
var articleContent = document.getElementById('articleContentShow').placeholder //直接取原本的字符串。不会被转译,默认html页面中textarea区域text需要escape编码
articleContent = unescape(articleContent);//unescape解码
var html = parser.parse(articleContent);
$('#article-content').append(html);
hljs.initHighlightingOnLoad();
//源码高亮
$('pre code').each(function (i, block) {
hljs.highlightBlock(block);
});
})
</script>
其中,我们是直接调用的Mditor.Parser()函数来解析Markdown字符文本的。
这样我们的详情页也支持了Markdown的渲染显示了:
11.17 文章列表分页搜索
为了方便检索我们的博客文章,我们再来给文章列表页面添加分页、搜索、排序等功能。我们使用前端组件DataTables来实现。
提示:更多关于DataTables,可参考: http://www.datatables.club/
11.17.1 引入静态资源文件
<link href="/datatables/media/css/jquery.dataTables.css" rel="stylesheet">
<script src="/datatables/media/js/jquery.dataTables.js"></script>
11.17.2 给表格加上id
我们给表格加个属性id="articlesDataTable" :
<table id="articlesDataTable" class="table table-responsive table-bordered">
<thead>
<th>序号</th>
<th>标题</th>
<th>作者</th>
<th>发表时间</th>
<th>操作</th>
</thead>
<tbody>
<#-- 使用FTL指令 -->
<#list articles as article>
<tr>
<td>${article.id}</td>
<td><a target="_blank" href="detailArticleView?id=${article.id}">${article.title}</a></td>
<td>${article.author}</td>
<td>${article.gmtModified}</td>
<td><a href="#" target="_blank">编辑</a></td>
</tr>
</#list>
</tbody>
</table>
11.17.3 调用DataTable函数
首先,我们配置一下DataTable的选项:
var aLengthMenu = [7, 10, 20, 50, 100, 200]
var dataTableOptions = {
"bDestroy": true,
dom: 'lfrtip',
"paging": true,
"lengthChange": true,
"searching": true,
"ordering": true,
"info": true,
"autoWidth": true,
"processing": true,
"stateSave": true,
responsive: true,
fixedHeader: false,
order: [[1, "desc"]],
"aLengthMenu": aLengthMenu,
language: {
"search": "<div style='border-radius:10px;margin-left:auto;margin-right:2px;width:760px;'>_INPUT_ <span class='btn btn-primary'><span class='fa fa-search'></span> 搜索</span></div>",
paginate: {//分页的样式内容
previous: "上一页",
next: "下一页",
first: "第一页",
last: "最后"
}
},
zeroRecords: "没有内容",//table tbody内容为空时,tbody的内容。
//下面三者构成了总体的左下角的内容。
info: "总计 _TOTAL_ 条,共 _PAGES_ 页,_START_ - _END_ ",//左下角的信息显示,大写的词为关键字。
infoEmpty: "0条记录",//筛选为空时左下角的显示。
infoFiltered: ""//筛选之后的左下角筛选提示
}
然后把我们刚才添加了id的表格使用JQuery选择器获取对象,然后直接调用:
$('#articlesDataTable').DataTable(dataTableOptions)
再次看我们的文章列表页:
已经具备了分页、搜索、排序等功能了。
到这里,我们的这个较为完整的极简博客站点应用基本就开发完成了。
11.18 Spring 5.0对Kotlin的支持
Kotlin 关键性能之一就是能与 Java 库很好地互用。但要在 Spring 中编写惯用的 Kotlin 代码,还需要一段时间的发展。 Spring 对 Java 8 的新支持:函数式 Web 编程、bean 注册 API , 这同样可以在 Kotlin 中使用。
Kotlin 扩展是Kotlin 的编程利器。它能对现有的 API 实现非侵入式的扩展,从而向 Spring中加入 Kotlin 的专有的功能特性。
11.18.1 一种注册 Bean 的新方法
Spring Framework 5.0 引入了一种注册 Bean 的新方法,作为利用 XML 或者 JavaConfig 的 @Configuration 或者 @Bean 的替代方案。简言之就是 Lambda 表达式。
例如用 Java 代码我们会这样写:
GenericApplicationContext context = new GenericApplicationContext();
context.registerBean(Foo.class);
context.registerBean(Bar.class, () -> new
Bar(context.getBean(Foo.class))
);
而使用 Kotlin 我们可以将代码写成这样:
val context = GenericApplicationContext {
registerBean<foo>()
registerBean { Bar(it.getBean<foo>()) }
}
11.18.2 Spring Web 函数式 API
Spring 5.0 中的 RouterFunctionDsl 可以让我们使用干净且优雅的 Kotlin 代码来使用崭新的 Spring Web 函数式 API:
fun route(request: ServerRequest) = RouterFunctionDsl {
accept(TEXT_HTML).apply {
(GET("/user/") or GET("/users/")) { findAllView() }
GET("/user/{login}") { findViewById() }
}
accept(APPLICATION_JSON).apply {
(GET("/api/user/") or GET("/api/users/")) { findAll() }
POST("/api/user/") { create() }
POST("/api/user/{login}") { findOne() }
}
} (request)
11.18.3 Reactor Kotlin 扩展
Reactor 是 Spring 5.0 中提供的响应式框架。而 reactor-kotlin 项目则是对 Reactor 中使用Kotlin 的支持。目前该项目正在早期阶段。
11.18.4 基于 Kotlin脚本的 Gradle 构建配置
之前我们的 Gradle 构建配置文件都是用Groovy 来编写的,这导致我们基于 Gradle 的 Kotlin 工程还要配置 Groovy 的语法的构建配置文件。
在gradle-script-kotlin 项目中,我们可以直接用 Kotlin 脚本来编写 Gradle 的构建配置文件了。而且 IDE 还为我们提供了在编写配置文件过程中的自动完成功能和重构功能的支持。
11.18.5 基于模板的 Kotlin 脚本
从 4.3 版本开始,Spring 提供了一个 ScriptTemplateView,用于利用支持 JSR-223 的脚本引擎来渲染模板。 Kotlin 1.1-M04 提供了这样的支持,并支持渲染基于 Kotlin 的模板,类似下面这样:
import io.spring.demo.User
import io.spring.demo.joinToLine
"""
${include("header", bindings)}
<h1>Title : $title</h1>
<ul>
${(users as List<User>).joinToLine{ "<li>User ${it.firstname} ${it.lastname}</li>" }}
</ul>
${include("footer")}
"""
本章小结
本章我们较为细致完整地介绍了使用Kotlin集成SpringBoot进行服务后端开发,并结合简单的前端开发,完成了一个极简的技术博客Web站点。我们可以看到,使用Kotlin结合Spring Boot、Spring MVC、JPA等Java框架的无缝集成,关键是大大简化了我们的代码。同时,在本章最后我们简单介绍了Spring 5.0中对Kotlin的支持诸多新特性,这些新特性令人惊喜。
使用Kotlin编写Spring Boot应用程序越多,我们越觉得这两种技术有着共同的目标,让我们广大程序员可以使用——
- 富有表达性
- 简洁优雅
- 可读
的代码来更高效地编写应用程序,而Spring Framework 5 Kotlin支持将这些技术以更加自然,简单和强大的方式来展现给我们。
未来Spring Framework 5.0 和 Kotlin 结合的开发实践更加值得我们期待。
在下一章中我们将一起学习Kotlin 集成 Gradle 开发的相关内容。
本章项目源码: https://github.com/EasyKotlin/chapter11_kotlin_springboot