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("姓名 : "); [email protected] = text("")
            }
            div {
                strong("性别 : "); [email protected] = text("")
            }
            div {
                strong("生日 : "); [email protected] = 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


<<:  CSS display:grid

>>:  【Day09-填空】漏漏缺缺欠欠填填删删补补——面对缺失值的处理方式

第42篇-学习 DNS Server - 2

今天学习进度还是 鸟哥的 Linux 私房菜 -- DNS Server /etc/nsswitch...

Day 16 : 案例分享(5.2) CRM与ERP整合 - 商机与客户往来记录(会议与行事历)

案例说明及适用场景 客户往来记录,属於商机管理的一环,一个客户不见得袛有一次商机,而一个商机的完成必...

[第九只羊] 迷雾森林舞会II 房间座位设定

天亮了 昨晚是平安夜 关於迷雾森林故事 粉红烟花三个月 由於黑洞把12只 animal 吸走後的烟 ...

Day7:函数返回

我们已经使用过"="给变量命名,今天主要演示的是如何使用"return...

[Day 30] 完赛心得

前言 很开心能够确实每天发文,并且持续30天成功完赛! 虽然这些天的发文大多都是过去学习中累计下来的...