11/08/2018, 23:50

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.

imageedit_3_4330048352.png

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à listeneremployeeId (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.

IEventSinkApplication, 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()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
0