创建缩略语页面 封面
Vapor 大型教程

创建缩略语页面

本章我们在网站前端加上一个「创建缩略语」的网页:既有填写表单的页面,也有接收表单提交、把数据存进数据库的接口。

在控制器里添加路由和处理方法

首先,在 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) 遍历控制器传来的用户列表渲染选项;表单的 methodpost,提交后由上面的 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 才能把表单字段自动解码成模型属性。

本系列其他文章