自订 Vaadin 组件 / Grid 扩充功能 -- day07

重复使用程序码

Vaadin 自订 Component

各位发现了吗?在写完CRUD後,打开CreateStudentView.ktEditStudent.kt两相对照,新增和编缉的画面几乎一样,能不能共用程序码?怎麽共用?可不可以写成日後可重复使用的组件?

  • VoK 提供了非常简单自订组件的方法。开新档案StudentEditorComponent.kt,将EditStudent.kt复制进来後,给student一个set()方法,让这个component可设定student属性
package com.example.vok

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

class StudentEditorComponent: KComposite() {
    private val binder = beanValidationBinder<Student>()
    var student: Student? = null
        set(value) {
            field = value
            value?.let { binder.readBean(value) }
        }
    private val root = ui {
        verticalLayout {
            isMargin = false
            textField("姓名 : "){
                bind(binder).bind(Student::name)
            }
            comboBox<Gender>("性别 : "){
                setItems(*Gender.values())
                bind(binder).bind(Student::gender)
            }
            datePicker("生日 : "){
                bind(binder).bind(Student::birthday)
            }
            numberField("身高"){
                bind(binder).bind(Student::height)
            }
            numberField("体重"){
                bind(binder).bind(Student::weight)
            }
            button("储存"){
                onLeftClick {
                    val student = student!!
                    if (binder.validate().isOk && binder.writeBeanIfValid(student)){
                        student.save()
                        StudentView.navigateTo(student.id!!)
                    }
                }
            }
            routerLink(null, "返回", AllStudentsView::class)
        }
    }
}

fun HasComponents.studentEditorComponent(block: StudentEditorComponent.()->Unit = {}) = init(StudentEditorComponent(), block)

最後一行,HasComponents.studentEditorComponent() Extension function,参数为要加入组件的程序码区段,再将组件回传。

  • 画面准备好了,但要怎麽把这个组件组合到其他画面中呢?请打开CreateStudentView.kt,移除重复的程序码,由於此画面是新增学生资料,所以指定 student 属性为 Student(),改好後程序如下 :
class CreateStudentView: KComposite() {
    private lateinit var editorComponent: StudentEditorComponent
    private val root = ui {
        verticalLayout {
            h1("新增学生资料")
            editorComponent = studentEditorComponent {
                student = Student()
            }
        }
    }
}

看起来是不是变得很简洁?

  • 接着来看修改,请打开EditStudent.kt,这里也一样移除重复的程序码後加上StudentEditorComponent
    private lateinit var studentEditorComponent: StudentEditorComponent
    private val root = ui {
        verticalLayout {
            h1("学生资料修改")
            studentEditorComponent = studentEditorComponent()
        }
    }
  • 设定student属性
    override fun setParameter(event: BeforeEvent?, studentId: Long?) {
        studentEditorComponent.student = Student.getById(studentId!!)
    }

这段原本重新取读 bean 让 grid 自动更新的程序码,改为设定组件的student属性值。

执行後的结果和原本一样一样,画面组件化後程序码不但可读性更高,且组件和原本程序码脱勾相互不受影响。

扩充Grid功能,加上附 listener 的图型按键

我们先来看看,如果今天想要在grid里使用带clicklistener的图型按键需要做哪些事。开启AllStudentsView.kt

    addColumn(ComponentRenderer<Button, Student>{ student: Student ->
        val button = Button(VaadinIcon.EYE.create())
        button.addThemeVariants(ButtonVariant.LUMO_ICON, ButtonVariant.LUMO_TERTIARY, ButtonVariant.LUMO_SMALL)
        button.onLeftClick { StudentView.navigateTo(student.id!!)}
        button
    }).setWidth("50px").isExpand = false
                
    1. 首先要在column里加入Renderer,因为button是component,这里使用 ComponentRenderer
    1. 再来产制一个图形Button,并设置图示大小等图形显示样式,
    1. 在 button 加上 clickListener 事件

加上三个带事件的 button column 後,就会变成这样

    addColumn(ComponentRenderer<Button, Student>{ student: Student ->
        val button = Button(VaadinIcon.EYE.create())
        button.addThemeVariants(ButtonVariant.LUMO_ICON, ButtonVariant.LUMO_TERTIARY, ButtonVariant.LUMO_SMALL)
        button.onLeftClick { StudentView.navigateTo(student.id!!)}
        button
    }).setWidth("50px").isExpand = false
    addColumn(ComponentRenderer<Button, Student>{ student: Student ->
        val button = Button(VaadinIcon.EDIT.create())
        button.addThemeVariants(ButtonVariant.LUMO_ICON, ButtonVariant.LUMO_TERTIARY, ButtonVariant.LUMO_SMALL)
        button.onLeftClick { EditStudent.navigateTo(student.id!!)}
        button
    }).setWidth("50px").isExpand = false
    addColumn(ComponentRenderer<Button, Student>{ student: Student ->
        val button = Button(VaadinIcon.TRASH.create())
        button.addThemeVariants(ButtonVariant.LUMO_ICON, ButtonVariant.LUMO_TERTIARY, ButtonVariant.LUMO_SMALL)
        button.onLeftClick {
            confirmDialog(text = "是否确定删除${student.name}的资料?") {
            student.delete()
            this.refresh()
        }}
        button
    }).setWidth("50px").isExpand = false

程序码看起来不仅兀长且不易阅读。如果 Grid 能够直接提供一个方法,只要传进icon、column key、clickListener三个参数,就能回给我一个带有listener的图形按键,好像还不错。

fun <T> Grid<T>.addButtonColumn(icon: IconFactory, key: String, clickListener: (T) -> Unit): Grid.Column<T> {
    val renderer = ComponentRenderer<Button, T> { data: T ->
        val button = Button(icon.create())
        button.addThemeVariants(ButtonVariant.LUMO_ICON, ButtonVariant.LUMO_TERTIARY, ButtonVariant.LUMO_SMALL)
        button.onLeftClick { clickListener(data) }
        button
    }
    val column: Grid.Column<T> = addColumn(renderer).setKey(key).setWidth("50px")
    column.isExpand = false
    return column
}

grid{} 加上图形按键 column 的程序码改为

        addButtonColumn(VaadinIcon.EYE, "view") { StudentView.navigateTo(it.id!!) }
        addButtonColumn(VaadinIcon.EDIT, "edit") { EditStudent.navigateTo(it.id!!) }
        addButtonColumn(VaadinIcon.TRASH, "delete"){
            confirmDialog(text = "是否确定删除${it.name}的资料?") {
                it.delete()
                this.refresh()
            }
        }

执行结果如下:
https://ithelp.ithome.com.tw/upload/images/20210922/20138680uReelp97JZ.png

经过Refactor後,整个程序是不是看起来既乾净又简捷?
明天开始进入第二张资料表,就要讲到资料库关联罗~

本日程序已上传 GitHub


<<:  DAY02 - 环境安装

>>:  [Day7] Array Cardio Day 2

Day13-D3 的 Drag 拖曳

本篇大纲:selection.call( )、drag.on( )、范例 今天要来看另一个d3有趣...

EP27 - 建立 VPN 连线,直接连线到 AWS

今天是要来填之前未补之坑, 那就是建立 VPN 连线, 以小公司来说, 其实能够快速加快产品上市比较...

OpenStack Neutron 介绍 — OVS Self-service Networks

本系列文章同步发布於笔者网站 上篇介绍了 Open vSwitch with Provider Ne...

Day 13 Mailhog - 模拟 SMTP 邮件服务的开发利器

由於 Mautic 是一个自动化行销利器,那麽寄发电子邮件便是一个必须的功能。不过在开发时一再的利用...