Gửi nhận data, event giữa các panel trong Apache Wicket
Lập trình web với Apache Wicket không thể không sử dụng Panel và Container. Panel và container có tác dụng phân chia code dễ quản lý code, sử dụng lại code. Trên là hình mô phỏng 2 tab (panel) của page quản lý employee. Tab2 là list danh sách employee, khi click vào Id của một employee ...
Lập trình web với Apache Wicket không thể không sử dụng Panel và Container.
Panel và container có tác dụng phân chia code dễ quản lý code, sử dụng lại code.
Trên là hình mô phỏng 2 tab (panel) của page quản lý employee.
Tab2 là list danh sách employee, khi click vào Id của một employee thì sẽ chuyển sang Tab1 là panel edit thông tin employee. Trong Wicket làm sao để thực hiện điều đó?
1. Sử dụng interface IAjaxUpdateListener
Tạo mới interface có method onUpdate(AjaxRequestTarget target) gửi và lắng nghe evnet.
public interface IAjaxUpdateListener extends Serializable { public void onUpdate (AjaxRequestTarget target); }
Tại EmployeePage page khởi tạo interface IAjaxUpdateListener như sau.
IAjaxUpdateListener listener = new IAjaxUpdateListener() { private static final long serialVersionUID = 1L; @Override public void onUpdate(AjaxRequestTarget target) { //Get employee info from DB by id //Set Model for EmployeeDetail panel //Set Tab index = 0 (tab 0 contain EmployeeDetail panel) } }
Tại page này khi khởi tạo panel EmployeeListPanel thì truyền theo tham số là listener và employeeId (biến nhận employee id khi chọn từ list)
List<ITab> tabs = new ArrayList<ITab>(); tabs.add(new AbstractTab("Employee list", this, null)) { private static final long serialVersionUID = 1L; @Override public Panel getPanel(String panelId) { employeeListPanel = new EmployeeListPanel(panelId, listener, employeeId); employeeListPanel.setOutputMarkupPlaceholderTag(true); return employeeListPanel; } });
Vậy tại EmployeeListPanel panel ta sử dụng đối tượng IAjaxUpdateListener listener như thế nào?
Rất đơn giản, sự kiện onClick() của link (Id) ta gán employee id cho argument int employeeId (được truyền từ Employee page), gọi hàm listener.onUpdate() với truyền tham số target (Id link sử dụng component AjaxLink hoặc link thường nhưng add thêm lớp AjaxEventBehavior("onclick")).
Khi gọi listener.onUpdate() thì method onUpdate() đã được override khi khởi tạo tại Employee page được thực hiện.
Vậy thông qua đối tượng IAjaxUpdateListener ta đã truyền employeeId từ EmployeeListPanel về page gốc, đồng thời gọi mothod (onUpdate()) để thực hiện chuyển tab (Employee list qua tab Employee detail)
2. Sử dụng (override) method "gửi" và "lắng nghe" broad cast message của Component
Trong Wicket, Component thực thi interface IEventSource, và IEventSink
Hai interface trên cung cấp 2 method là send(IEventSink sink, Broadcast broadcast, T payload), và onEvent(IEvent<?> event).
Như ta đã biết, trong Wicket thì Component là lớp abstract mà Panel nói riêng và các thành phần khác (Page, Container,...) nói chung đều kế thừa.
a. send(IEventSink sink, Broadcast type, T payload)
- sink - là object sẽ nhận event
- type - quy định cách mà object nhận event broadcast tiếp event cho các object khác.
- payload - object giử theo event.
IEventSink là Application, Session, RequestCycle, Page, Components
Broadcast
- BREADTH
Nếu một component của page là đối tượng chỉ định nhận (sink) event thì các component con của nó cũng được nhận event.
Nếu Session là sink thì session, request cycle, page, và các component con đều nhận event.
Thứ tự nhận event từ Component, các Component con.
- DEPTH
Cũng như BREADTH type nhưng thứ tự nhận event thì ngược lại từ component con rồi đến component sink.
- BUBBLE
Chỉ component sink và các component cha, session, request cycle, và application được thông báo.
- EXACT
Chỉ component sink nhận được thông báo.
b. onEvent(IEvent<?> event)
- event - là ComponentEvent nó impliments interface IEvent.
Method này được gọi khi chính đối tượng khai báo được gửi event.
Interface này cung cấp 3 method
- Broadcast getType()
Trả về type (BREADTH, DEPTH, BUBBLE, EXACT) của event.
- IEventSource getSource()
Trả về source (đối tượng gửi event) của event.
- T getPayload()
Trả về object gửi kèm theo event, cái này quan trọng.
Quay lại ví dụ ban đầu. Để truyền Id hay objec Employee từ tab2 sang tab1 khi click vào link ID từ listView, ta làm như sau.
Sự kiện Link onSubmit()
@Override public void onSubmit(AjaxRequestTarget target, Form<?> form) { // get Employee object Employee employeToEdit = item.getModelObject(); // tabIndex will on int tabIndex = 0; // call send() method with params // IEventSink is this page // Broadcast type is BREADTH // T is Employee object to edit send(getPage(), Broadcast.BREADTH, employeeToEdit); }
Tại EmployeePage ta Override method lắng nghe event onEvent(IEvent<?> event) ta sẽ lấy được T (Employee) đã gửi bởi EmployeeListPanel panel bằng cách gọi event.getPayLoad() cái này đã nói ở trên.
Ta thấy rằng ở EmployeePage page đã lấy được đối tượng Employee nhưng, làm sao để set tab index hay nói cách khác là ẩn tab2 hiển thị tab1? Ta cầ xử lý Ajax ở đây, do vậy cần sửa lại method send() ở trên một chút.
Thay vì set T là đối tượng Employee, ta tạo lớp Dto EmployeeDto như sau.
public class EmployeeDto extends Employee { private AjaxRequestTarget target; public AjaxRequestTarget getTarget() { return this.target; } public void setTarget(AjaxRequestTarget target) { this.target = target; } }
Và sự kiên onSubmit() được viết lại như sau
public void onSubmit(AjaxRequestTarget target, Form<?> form) { ..... EmployeeDto employeeDtoToEdit = new EmployeeDto(); BeanUtil.copyProperties(employeeToEdit, employeeDtoToEdit); //set AjaxRequestTarget employeeDtoToEdit.setTarget(target); send(getPage(), Broadcast.BREADTH, employeeDtoToEdit)); }
Lắng nghe sự kiện gửi tới onEvent()
Ta Override method onEvent() trên EmplyeePage page
@Override public void onEvent(IEvent<?> event) { super.onEvent(event); //Check and get T (is EmployeeDto) if (event.getPayload() != null && event.getPayload() instanceof EmployeeDto) { EmployeeDto employeeDto = (EmployeeDto)event.getPayload(); AjaxRequestTarget target = params.getTarget(); // có EmployeeDto, AjaxRequestTarget rồi // ta hoàn toàn hiển thị thông tin Employee trên tab1 (Edit tab) } }
3. Nhận xét
Cả hai giải pháp trên có ưu và nhược điểm khác nhau.
- Giải pháp 1 sử dụng IAjaxUpdateListener
a. Ưu điểm
Từ page chính, đối tượng IAjaxUpdateListener listener được truyền qua panel, được sử dụng để gọi onUpdate() người đọc code sẽ dễ lần theo "luồng" code để xem lớp nào gửi và nhận.
b. Nhược điểm
Chỉ có thể gửi và nhận: 1 - 1 (một đối tượng gửi, một đối tượng nhận, hay n - 1 (nhiều đối tượng gửi, một đối tượng nhận)
- Giải pháp override method send(), onEvent()
a. Ưu điểm
Có thể gửi nhận: n - n
b. Nhược điểm
Một developer mới sử dụng Wicket đọc code sẽ khó hiểu luồng gửi nhận event khi đọc method send() và onEvent() mà chưa tìm hiểu kỹ về các argument của hai method nó.
- http://wicket.apache.org/apidocs/1.5/org/apache/wicket/event/package-summary.html
- http://wicket.apache.org/apidocs/1.5/org/apache/wicket/Component.html