各位發現了嗎?在寫完CRUD後,打開CreateStudentView.kt
和EditStudent.kt
兩相對照,新增和編緝的畫面幾乎一樣,能不能共用程式碼?怎麼共用?可不可以寫成日後可重複使用的組件?
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()
}
}
override fun setParameter(event: BeforeEvent?, studentId: Long?) {
studentEditorComponent.student = Student.getById(studentId!!)
}
這段原本重新取讀 bean 讓 grid 自動更新的程式碼,改為設定組件的student屬性值。
執行後的結果和原本一樣一樣,畫面組件化後程式碼不但可讀性更高,且組件和原本程式碼脫勾相互不受影響。
我們先來看看,如果今天想要在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
加上三個帶事件的 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()
}
}
執行結果如下:
經過Refactor後,整個程式是不是看起來既乾淨又簡捷?
明天開始進入第二張資料表,就要講到資料庫關聯囉~
本日程式已上傳 GitHub