11.15 实现写文章模块

我们在列表页上面添加一个“写文章”的入口:

  1. <a href="addArticleView" target="_blank" class="btn btn-primary pull-right add-article">写文章</a>

其中,btn btn-primary pull-right 这三个css样式类是Flat UI组件的。add-article是我们自定义的样式类:

  1. .add-article {
  2. margin: 20px;
  3. }

下面我们来写新建文章的页面。我们写文章的跳转页面路径是 <a href="addArticleView">, 我们先来新建一个写文章页面addArticleView.ftl:

  1. <!DOCTYPE html>
  2. <html>
  3. <#include "head.ftl">
  4. <body>
  5. <div class="container">
  6. <h2>写文章</h2>
  7. <form id="addArticleForm" class="form-horizontal">
  8. <div class="form-group">
  9. <input type="text" name="title" class="form-control" placeholder="文章标题">
  10. </div>
  11. <div class="form-group">
  12. <textarea id="articleContentEditor" type="text" name="content" class="form-control" rows="20"
  13. placeholder=""></textarea>
  14. </div>
  15. <div class="form-group save-article">
  16. <div class="col-sm-offset-2 col-sm-10">
  17. <button type="submit" class="btn btn-primary" id="addArticleBtn">保存并发表</button>
  18. </div>
  19. </div>
  20. </form>
  21. </div>
  22. </body>
  23. </html>

然后,再添加控制器请求转发。这里我们使用集成WebMvcConfigurerAdapter类,重写实现addViewControllers方法的方式来添加一个不带数据传输的,单纯的请求转发的跳转View的RequestMapping Controller:

  1. @Configuration
  2. class WebMvcConfig : WebMvcConfigurerAdapter() {
  3. // 注册简单请求转发跳转View的RequestMapping Controller
  4. override fun addViewControllers(registry: ViewControllerRegistry?) {
  5. //写文章的RequestMapping
  6. registry?.addViewController("addArticleView")?.setViewName("addArticleView")
  7. }
  8. }

这样前端浏览器来的请求addArticle会直接映射转发到视图addArticle.ftl文件渲染解析。

重启应用,进入到我们的写文章的页面,如下图:

Kotlin极简教程

11.15.1 加上导航栏

为了方便页面之间的跳转,我们给我们的博客站点加上导航栏,我们新建一个navbar.ftl文件,内容如下:

  1. <nav class="navbar navbar-default" role="navigation">
  2. <div class="container-fluid">
  3. <div class="navbar-header">
  4. <button type="button" class="navbar-toggle" data-toggle="collapse"
  5. data-target="#example-navbar-collapse">
  6. <span class="sr-only">切换导航</span>
  7. <span class="icon-bar"></span>
  8. <span class="icon-bar"></span>
  9. <span class="icon-bar"></span>
  10. </button>
  11. <a class="navbar-brand" href="#">我的博客</a>
  12. </div>
  13. <div class="collapse navbar-collapse" id="example-navbar-collapse">
  14. <ul class="nav navbar-nav">
  15. <li class=""><a href="listAllArticleView">文章列表</a></li>
  16. <li class="active"><a href="addArticleView">写文章</a></li>
  17. <li><a href="#">关于</a></li>
  18. <li class="dropdown">
  19. <a href="http://www.jianshu.com/nb/12976878" class="dropdown-toggle" data-toggle="dropdown">
  20. Kotlin <b class="caret"></b>
  21. </a>
  22. <ul class="dropdown-menu">
  23. <li><a href="http://www.jianshu.com/nb/12976878" target="_blank">Kotlin极简教程</a></li>
  24. <li class="divider"></li>
  25. <li><a href="#">Java</a></li>
  26. <li><a href="#">Scala</a></li>
  27. <li><a href="#">Groovy</a></li>
  28. <li class="divider"></li>
  29. <li><a href="http://www.jianshu.com/nb/12066555" target="_blank">SpringBoot极简教程</a></li>
  30. </ul>
  31. </li>
  32. </ul>
  33. </div>
  34. </div>
  35. </nav>

11.15.2 抽取公共模板文件

考虑到head.ftl、navbar.ftl都是公共的文件,我们把他们单独放到一个common目录下。

然后,我们分别在addArticleView.ftl、listAllArticleView.ftl引用如下:

  1. <!DOCTYPE html>
  2. <html>
  3. <#include "common/head.ftl">
  4. <body>
  5. <#include "common/navbar.ftl">

加入了导航栏之后,我们的页面现在更加美观了:

Kotlin极简教程

Kotlin极简教程

11.15.3 写文章的控制器层接口

控制器层保存接口:

  1. @PostMapping("saveArticle")
  2. @ResponseBody
  3. fun saveArticle(article: Article): Article? {
  4. article.gmtCreated = Date()
  5. article.gmtModified = Date()
  6. article.version = 0
  7. return articleRepository?.save(article)
  8. }

另外,为了支持较多内容的文章,我们把文章内容字段设置成LONGTEXT:

  1. ALTER TABLE `blog`.`article`
  2. CHANGE COLUMN `content` `content` LONGTEXT NULL DEFAULT NULL ;

11.15.4 前端 ajax 请求

我们在blog.js中加上ajax POST请求后端接口的逻辑:

  1. $(function () {
  2. $('#addArticleBtn').on('click', function () {
  3. saveArticle()
  4. })
  5. function saveArticle() {
  6. $.ajax({
  7. url: "saveArticle",
  8. data: $('#addArticleForm').serialize(),
  9. type: "POST",
  10. async: false,
  11. success: function (resp) {
  12. if (resp) {
  13. saveArticleSuccess(resp)
  14. } else {
  15. saveArticleFail()
  16. }
  17. },
  18. error: function () {
  19. saveArticleFail()
  20. }
  21. })
  22. }
  23. function saveArticleSuccess(resp) {
  24. alert('保存成功: ' + JSON.stringify(resp))
  25. window.open('detailArticleView?id=' + resp.id)
  26. }
  27. function saveArticleFail() {
  28. alert("保存失败!")
  29. }
  30. })

11.15.5 文章详情页

保存成功后,我们默认跳转该文章详情页。
后端控制器代码:

  1. @GetMapping("detailArticleView")
  2. fun detailArticleView(id: Long, model: Model): ModelAndView {
  3. model.addAttribute("article", articleRepository?.findById(id)?.get())
  4. return ModelAndView("detailArticleView")
  5. }

注意,这里的articleRepository?.findById(id) 方法返回的是Optional

, 我们调用其get()方法,返回真正的Article实体对象。

前端视图detailArticleView.ftl代码:

  1. <!DOCTYPE html>
  2. <html>
  3. <#include "common/head.ftl">
  4. <body>
  5. <#include "common/navbar.ftl">
  6. <div class="container">
  7. <h3>${article.title}</h3>
  8. <h6>${article.author}</h6>
  9. <h6>${article.gmtModified}</h6>
  10. <div>${article.content}</div>
  11. </div>
  12. </body>
  13. </html>

我们在文章列表页中,给每篇文章标题加上跳转文章详情的超链接:

  1. <td><a target="_blank" href="detailArticleView?id=${article.id}">${article.title}</a></td>

现在我们的文章列表页面如下:

Kotlin极简教程

点击一篇文章标题,即可进入详情页:

Kotlin极简教程