iT邦幫忙

2021 iThome 鐵人賽

DAY 9
0
Modern Web

使用 Kotlin 快速開發 Web 程式 -- Vaadin系列 第 9

vok-orm 關聯性資料的新增/查詢 (下篇) + Vaadin 自訂樣式 - d09

上篇

Refactor

https://ithelp.ithome.com.tw/upload/images/20210924/20138680FTx6sBEPy7.png
上篇實作完成執行結果如上圖所示,藍色區塊為該學生成績,紅色區塊為新增成績編輯畫面,全部程式皆在StudentView.kt裡,接下來我們要將藍色及紅色部份分離出來。

成績顯示區

  • 請開新檔 GradeViewComponent.kt,將藍色區塊程式碼剪下,完成程式如下 :
package com.example.vok

import com.github.mvysny.karibudsl.v10.*
import com.vaadin.flow.component.HasComponents
import com.vaadin.flow.component.dependency.CssImport
import com.vaadin.flow.component.dependency.StyleSheet
import com.vaadin.flow.component.orderedlayout.VerticalLayout

class GradeViewComponent: KComposite() {
    var studentId: Long = 0
        set(value) {
            field = value
            refresh()
        }
    private lateinit var grades: VerticalLayout
    private val root = ui{
        verticalLayout {
            isMargin = false
            h2("成績")
            grades = verticalLayout()
        }
    }

    fun refresh() {
        grades.removeAll()
        Student.getById(studentId).grades.fetch().forEach {
            grades.html("<p>" +
                        "<strong>學期:</strong>${it.description} " +
                        "<strong>國文:</strong>${it.mandarin} " +
                        "<strong>英文:</strong>${it.english} " +
                        "<strong>數學:</strong>${it.math} " +
                        "<strong>體育:</strong>${it.pe}" +
                        "</p>")   
            
        }
    }
}

fun HasComponents.grandsViewComponent(block: GradeViewComponent.()->Unit = {}) = init(GradeViewComponent(), block)

這裡多了studentId屬性值供外部設定,設定studentId屬性後呼叫refresh()方法,清除成績div區塊後,再讀取成績資料以html語法直接替換顯示。

成績編輯區

  • 接著完成紅色區塊,開啟新檔GradeEditorComponent.kt
package com.example.vok

import com.github.mvysny.karibudsl.v10.*
import com.vaadin.flow.component.HasComponents
import com.vaadin.flow.component.button.Button

class GradeEditorComponent : KComposite() {
    var gradeCreatedListener: () -> Unit = {}
    lateinit var student: Student
    private val gradeBinder = beanValidationBinder<Grade>()
    private lateinit var addGradeButton: Button
    private val root = ui {
        verticalLayout {
            text("新增成績")

            textField("學期") {
                bind(gradeBinder).bind(Grade::description)
                placeholder = "第一學期"
            }
            numberField("國文") {
                bind(gradeBinder).bind(Grade::mandarin)
            }
            numberField("英文") {
                bind(gradeBinder).bind(Grade::english)
            }
            numberField("數學") {
                bind(gradeBinder).bind(Grade::math)
            }
            numberField("體育") {
                bind(gradeBinder).bind(Grade::pe)
            }
            addGradeButton = button("新增") {
                onLeftClick { addGrade() }
            }
        }
    }

    private fun addGrade() {
        val grade = Grade()
        if (gradeBinder.validate().isOk && gradeBinder.writeBeanIfValid(grade)) {
            grade.student_id = student.id
            grade.save()
            gradeBinder.readBean(Grade())
            gradeCreatedListener()
        }
    }
}

fun HasComponents.gradeEditorComponent(block: GradeEditorComponent.() -> Unit = {}) =
    init(GradeEditorComponent(), block)

整個畫面

  • 最後,重新整理StudentView.kt
package com.example.vok

import com.github.mvysny.karibudsl.v10.*
import com.vaadin.flow.component.Text
import com.vaadin.flow.component.icon.VaadinIcon
import com.vaadin.flow.router.BeforeEvent
import com.vaadin.flow.router.HasUrlParameter
import com.vaadin.flow.router.Route
import com.vaadin.flow.router.RouterLink

@Route("student", layout = MainLayout::class)
class StudentView: KComposite(), HasUrlParameter<Long> {
    private lateinit var editLink: RouterLink
    private lateinit var name: Text
    private lateinit var gender: Text
    private lateinit var birthday: Text
    private lateinit var gradeView: GradeViewComponent
    private lateinit var gradeEditor: GradeEditorComponent
    private val root = ui {
        verticalLayout {
            routerLink(VaadinIcon.ARROW_LEFT, null, viewType = AllStudentsView::class)
            div {
                strong("姓名 : "); this@StudentView.name = text("")
            }
            div {
                strong("性別 : "); this@StudentView.gender = text("")
            }
            div {
                strong("生日 : "); this@StudentView.birthday = text("")
            }
            gradeView = grandsViewComponent()
            gradeEditor = gradeEditorComponent {
                gradeCreatedListener = { gradeView.refresh() }
            }
            editLink = routerLink(VaadinIcon.EDIT,null)
        }
    }

    override fun setParameter(event: BeforeEvent?, studentId: Long) {

        val student = Student.getById(studentId)
        gradeView.studentId = studentId
        gradeEditor.student = student
        name.text = student.name
        gender.text = student.gender.toString()
        birthday.text = student.birthday.toString()
        editLink.setRoute(EditStudent::class, student.id!!)
    }

    companion object {
        fun navigateTo(studentId: Long) = navigateToView(StudentView::class, studentId)
    }
}

gradeViewgradeEditor分別為藍色、紅色區塊

自訂樣式

以下程式會使用到簡單 html、css 概念

除了可使用html()方法輸出 HTML 外,亦可使用 Karibu-DSL提供的其他方法。紅色區塊要將它改為div table 顯示

    fun refresh() {
        grades.removeAll()
        grades.apply {
            div("table") {
                div("tr") {
                    div("td") { strong("學期") }
                    div("td") { strong("國文") }
                    div("td") { strong("英文") }
                    div("td") { strong("數學") }
                    div("td") { strong("體育") }
                }
            }
        }
        Student.getById(studentId).grades.fetch().forEach {
            grades.apply {
                div("table") {
                    div("tr") {
                        div("td") { label("${it.description}") }
                        div("td") { label("${it.mandarin}") }
                        div("td") { label("${it.english}") }
                        div("td") { label("${it.math}") }
                        div("td") { label("${it.pe}") }
                }
            }
        }
    }

div後面括號內為css class name ,表示我們需要一個 css檔。在Vaadin flow framework 專案結構,css檔放置在 web/frontend/,新增 table.css

.table {
  display: table;
  border-collapse: collapse;
  width: 500px;
  margin:0px;
}

/* tr */
.tr{
    display: table-row; border:0px; margin:0px;
}

/* td , th */
.td {
  display: table-cell;
  width: 180px;
}

css 已經準備好了,該如何引入呢?請開啟 GradeViewComponent.kt,在最前面加上標示

@CssImport("frontend://table.css")
class GradeViewComponent : KComposite() {

(略)
}

這樣就完成自訂樣式(style)的畫面了。雖然看起來似乎複雜了一點,但需要自訂Style時非常的好用,執行結如果下
https://ithelp.ithome.com.tw/upload/images/20210924/201386804swaKR3baT.png

參考資料 : css產生器

本日程式已上傳GitHub


上一篇
vok-orm 關聯性資料的新增/查詢 (上篇) -- d08
下一篇
VoK 整合式登入 - day10
系列文
使用 Kotlin 快速開發 Web 程式 -- Vaadin30

尚未有邦友留言

立即登入留言