Khi nào sử dụng trình nghe valueChangeListener hoặc f: ajax?


87

Sự khác biệt giữa hai đoạn mã sau - liên quan đến listenervị trí là gì?

<h:selectOneMenu ...>
    <f:selectItems ... />
    <f:ajax listener="#{bean.listener}" />
</h:selectOneMenu>

<h:selectOneMenu ... valueChangeListener="#{bean.listener}">
    <f:selectItems ... />
</h:selectOneMenu>

Câu trả lời:


182

Di valueChangeListenerchúc chỉ được gọi khi biểu mẫu được gửi giá trị đã gửi khác với giá trị ban đầu. Do đó, nó không được gọi khi chỉchange sự kiện HTML DOM được kích hoạt. Nếu bạn muốn gửi biểu mẫu trong changesự kiện HTML DOM , thì bạn cần thêm một biểu mẫu khác <f:ajax/>không có trình xử lý (!) Vào thành phần đầu vào. Nó sẽ tạo ra một biểu mẫu gửi chỉ xử lý thành phần hiện tại (như trong execute="@this").

<h:selectOneMenu value="#{bean.value}" valueChangeListener="#{bean.changeListener}">
    <f:selectItems ... />
    <f:ajax />
</h:selectOneMenu>

Khi sử dụng <f:ajax listener>thay vì valueChangeListener, nó sẽ được thực thi theo mặc định trong changesự kiện HTML DOM . Các UICommandthành phần bên trong và các thành phần đầu vào đại diện cho một hộp kiểm hoặc nút radio, nó sẽ được thực thi theo mặc định chỉ trong clicksự kiện HTML DOM .

<h:selectOneMenu value="#{bean.value}">
    <f:selectItems ... />
    <f:ajax listener="#{bean.ajaxListener}" />
</h:selectOneMenu>

Một sự khác biệt lớn khác là valueChangeListenerphương thức được gọi trong thời gian cuối của PROCESS_VALIDATIONSgiai đoạn. Tại thời điểm đó, giá trị đã gửi chưa được cập nhật trong mô hình. Vì vậy, bạn không thể lấy nó bằng cách chỉ truy cập thuộc tính bean được liên kết với thành phần đầu vào value. Bạn cần phải có được nó bằng cách ValueChangeEvent#getNewValue(). Giá trị cũ bằng cách này cũng có sẵn bởi ValueChangeEvent#getOldValue().

public void changeListener(ValueChangeEvent event) {
    Object oldValue = event.getOldValue();
    Object newValue = event.getNewValue();
    // ...
}

Các <f:ajax listener>phương pháp được gọi trong INVOKE_APPLICATIONgiai đoạn. Tại thời điểm đó, giá trị đã gửi đã được cập nhật trong mô hình. Bạn chỉ có thể lấy nó bằng cách truy cập trực tiếp vào thuộc tính bean được liên kết với thành phần đầu vào value.

private Object value; // +getter+setter.

public void ajaxListener(AjaxBehaviorEvent event) {
    System.out.println(value); // Look, (new) value is already set.
}

Ngoài ra, nếu bạn cần cập nhật một thuộc tính khác dựa trên giá trị đã gửi, thì nó sẽ không thành công khi bạn đang sử dụng valueChangeListenervì thuộc tính cập nhật thể bị ghi đè bởi giá trị đã gửi trong UPDATE_MODEL_VALUESgiai đoạn tiếp theo . Đó chính xác là lý do tại sao bạn thấy trong các ứng dụng / hướng dẫn / tài nguyên JSF 1.x cũ mà a valueChangeListenercó trong cấu trúc như vậy được sử dụng kết hợp với immediate="true"FacesContext#renderResponse()để ngăn điều đó xảy ra. Xét valueChangeListenercho cùng , việc sử dụng để thực hiện các hành động kinh doanh thực sự luôn là một cách giải quyết / hack.

Tóm tắt: Chỉ sử dụng valueChangeListenernếu bạn cần chặn trên chính sự thay đổi giá trị thực tế. Tức là bạn thực sự quan tâm đến cả giá trị cũ và giá trị mới (ví dụ: ghi lại chúng).

public void changeListener(ValueChangeEvent event) {
    changeLogger.log(event.getOldValue(), event.getNewValue());
}

Chỉ sử dụng <f:ajax listener>nếu bạn cần thực hiện một hành động kinh doanh trên giá trị mới được thay đổi. Tức là bạn đang thực sự quan tâm đến việc chỉ những giá trị mới (ví dụ để cư một danh sách thả xuống thứ hai).

public void ajaxListener(AjaxBehaviorEvent event) {
    selectItemsOfSecondDropdown = populateItBasedOn(selectedValueOfFirstDropdown);
}

Nếu bạn thực sự cũng quan tâm đến giá trị cũ trong khi thực hiện một hành động kinh doanh, thì hãy quay trở lại valueChangeListener, nhưng xếp nó vào INVOKE_APPLICATIONgiai đoạn.

public void changeListener(ValueChangeEvent event) {
    if (event.getPhaseId() != PhaseId.INVOKE_APPLICATION) {
        event.setPhaseId(PhaseId.INVOKE_APPLICATION);
        event.queue();
        return;
    }

    Object oldValue = event.getOldValue();
    Object newValue = event.getNewValue();
    System.out.println(newValue.equals(value)); // true
    // ...
}

@BalusC, có lý do gì để không lấy giá trị từ đối tượng sao lưu trong bộ cài đặt trước khi đặt nó thành giá trị mới được chuyển vào? Một cái gì đó như thế này: logger.trace( "setting changeTypes from {} to {}", this.changeTypes, changeTypes );. Nó có vẻ như bạn có thể sử dụng các giá trị cũ và mới thu được trong thời trang đó để làm logic kinh doanh trực tiếp trong setter cũng như khai thác gỗ đơn giản, nhưng tôi không biết nếu điều này sẽ gây ra tác dụng phụ ...
Lucas

Câu trả lời hoàn hảo và đầy đủ! Cảm ơn vì đã chia sẻ kiến ​​thức của bạn!
hbobenicio

@BalusC có thể cũng nhận được giá trị đã thay đổi thông qua AjaxBehaviorEvent không? Tôi có <h: selectManyListbox được xích vào các thành phần khác và sẽ muốn sử dụng các thay đổi (thêm / bớt) để thực hiện một số hành động
Paullo


Cảm ơn @BalusC Nhưng tôi không nghĩ ValueChangeListener sẽ phù hợp trong trường hợp của tôi vì tôi có một số giá trị thực thi và kết xuất như được hiển thị <f: ajax event = "valueChange" render = "tests" execute = "@ this" listening = "# {testController. processTimeTable} "/>
Paullo

9

cho phân đoạn đầu tiên (thuộc tính trình nghe ajax):

Thuộc tính "Listening" của thẻ ajax là một phương thức được gọi ở phía máy chủ mỗi khi hàm ajax xảy ra ở phía máy khách. Ví dụ: bạn có thể sử dụng thuộc tính này để chỉ định một hàm phía máy chủ để gọi mỗi khi người dùng nhấn phím

nhưng phân đoạn thứ hai (valueChangeListener):

ValueChangeListener sẽ chỉ được gọi khi biểu mẫu được gửi, không phải khi giá trị của đầu vào được thay đổi

* bạn có thể muốn xem câu trả lời hữu ích này

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.