创建缩略语页面
本章我们在网站前端加上一个「创建缩略语」的网页:既有填写表单的页面,也有接收表单提交、把数据存进数据库的接口。
在控制器里添加路由和处理方法
首先,在 WebsiteController.swift 中定义一个用于渲染「创建缩略语」页面的上下文 CreateAcronymContext。除了页面标题,它还用一个 Future<[User]> 把所有可能的创建者(用户列表)传给模板,让下拉框能选择作者。
struct CreateAcronymContext: Encodable {
let title = "Create An Acronym"
let users: Future<[User]>
}
接着在 boot(router:) 里注册两条路由:一条 GET 用于显示创建页面,一条 POST 用于接收提交。注意 router.post(Acronym.self, at: ...) 这种写法,Vapor 会自动把提交上来的表单解码成 Acronym 实例并作为参数传入。
router.get("acronyms", "create", use: createAcronymHandler)
router.post(Acronym.self, at: "acronyms", "create", use: createAcronymPostHandler)
其中 createAcronymHandler 通过 User.query(on: req).all() 取出全部用户,并用 req.view().render("createAcronym", context) 渲染 Leaf 模板返回页面。
func createAcronymHandler(_ req: Request) throws -> Future<View> {
let context = CreateAcronymContext(users: User.query(on: req).all())
return try req.view().render("createAcronym", context)
}
提交处理方法里,Vapor 已经帮我们把表单解码成了 acronym。直接 acronym.save(on: req) 保存,保存成功后拿到 id,再重定向到该缩略语的详情页。若保存后拿不到 id 则抛出 500 错误。
func createAcronymPostHandler(_ req: Request, acronym: Acronym) throws -> Future<Response> {
return acronym.save(on: req).map(to: Response.self) { acronym in
guard let id = acronym.id else {
throw Abort(HTTPStatus.internalServerError)
}
return req.redirect(to: "/acronyms/\(id)")
}
}
编写 createAcronym.leaf 模板
新建 Resources/Views/createAcronym.leaf,用 Bootstrap 的表单组件做一个提交页面。下拉框通过 #for(user in users) 遍历控制器传来的用户列表渲染选项;表单的 method 是 post,提交后由上面的 POST 路由接收。
#set("content"){
<h1>#(title)</h1>
<form method = "post">
<div class="form-group">
<label for = "short">Acronym</label>
<input type="text" name="short" class="form-control" id = "short"/>
</div>
<div class="form-group">
<label for = "long">Meaning</label>
<input type="text" name="long" class="form-control" id="long"/>
</div>
<div class="form-group">
<label for="userID">User</label>
<select name="userID" class="form-control" id="userID">
#for(user in users) {
<option value="#(user.id)">
#(user.name)
</option>
}
</select>
</div>
<button type="submit" class = "btn btn-primary">
Submit
</button>
</form>
}
#embed("base")
在导航栏加上入口
最后在 base.leaf 的导航列表中加入「Create An Acronym」入口,并利用 #if(title=="Create An Acronym"){active} 在访问本页时高亮当前导航项。
<li class="nav-item #if(title=="Create An Acronym"){active}">
<a href="/acronyms/create" class="nav-link">Create An Acronym</a>
</li>
router.post(Acronym.self, at: ...) 依赖 Acronym 遵守 Content 协议,Vapor 才能把表单字段自动解码成模型属性。