อะไรคือความแตกต่างระหว่างaction
และactionListener
และเมื่อใดที่ฉันควรใช้action
กับactionListener
?
อะไรคือความแตกต่างระหว่างaction
และactionListener
และเมื่อใดที่ฉันควรใช้action
กับactionListener
?
คำตอบ:
ใช้actionListener
หากคุณต้องการให้มี hook ก่อนที่การดำเนินการทางธุรกิจจริงจะถูกดำเนินการเช่นเพื่อบันทึกและ / หรือเพื่อตั้งค่าคุณสมบัติเพิ่มเติม (โดย<f:setPropertyActionListener>
) และ / หรือให้เข้าถึงส่วนประกอบที่เรียกใช้การกระทำ (ซึ่งมีให้โดยActionEvent
ข้อโต้แย้ง). ดังนั้นเพื่อวัตถุประสงค์ในการเตรียมการก่อนที่จะมีการดำเนินการทางธุรกิจจริง
actionListener
วิธีโดยค่าเริ่มต้นมีลายเซ็นต่อไปนี้:
import javax.faces.event.ActionEvent;
// ...
public void actionListener(ActionEvent event) {
// ...
}
และมันควรจะประกาศดังต่อไปนี้โดยไม่ต้องวงเล็บวิธีใด ๆ :
<h:commandXxx ... actionListener="#{bean.actionListener}" />
โปรดทราบว่าคุณไม่สามารถผ่านการขัดแย้งเพิ่มเติมโดย EL 2.2 อย่างไรก็ตามคุณสามารถแทนที่ActionEvent
อาร์กิวเมนต์ทั้งหมดได้โดยผ่านและระบุอาร์กิวเมนต์ที่กำหนดเอง ตัวอย่างต่อไปนี้ถูกต้อง:
<h:commandXxx ... actionListener="#{bean.methodWithoutArguments()}" />
<h:commandXxx ... actionListener="#{bean.methodWithOneArgument(arg1)}" />
<h:commandXxx ... actionListener="#{bean.methodWithTwoArguments(arg1, arg2)}" />
public void methodWithoutArguments() {}
public void methodWithOneArgument(Object arg1) {}
public void methodWithTwoArguments(Object arg1, Object arg2) {}
สังเกตความสำคัญของวงเล็บในนิพจน์เมธอดที่ไม่มีข้อโต้แย้ง หากพวกเขาไม่อยู่ JSF จะยังคงคาดหวังวิธีการที่มีActionEvent
ข้อโต้แย้ง
หากคุณอยู่ใน EL 2.2 + <f:actionListener binding>
แล้วคุณสามารถประกาศวิธีการดำเนินการหลายฟังผ่านทาง
<h:commandXxx ... actionListener="#{bean.actionListener1}">
<f:actionListener binding="#{bean.actionListener2()}" />
<f:actionListener binding="#{bean.actionListener3()}" />
</h:commandXxx>
public void actionListener1(ActionEvent event) {}
public void actionListener2() {}
public void actionListener3() {}
จดบันทึกความสำคัญของวงเล็บในbinding
แอตทริบิวต์ หากพวกเขาขาดหายไป EL จะโยน a javax.el.PropertyNotFoundException: Property 'actionListener1' not found on type com.example.Bean
เนื่องจากbinding
แอตทริบิวต์นั้นโดยค่าเริ่มต้นถูกตีความว่าเป็นการแสดงออกของค่าไม่ใช่การแสดงออกของวิธีการ การเพิ่มวงเล็บสไตล์ EL 2.2+ จะเปลี่ยนนิพจน์ค่าให้กลายเป็นการแสดงออกของเมธอดอย่างโปร่งใส ดูเพิ่มเติม ao เหตุใดฉันจึงสามารถเชื่อมโยง <f: actionListener> กับวิธีการตามอำเภอใจได้หาก JSF ไม่รองรับ
ใช้action
หากคุณต้องการดำเนินการทางธุรกิจและหากจำเป็นต้องจัดการการนำทาง action
วิธีกระป๋อง (ดังนั้นไม่ต้อง) กลับString
ซึ่งจะนำมาใช้เป็นกรณีที่ลูกศรผล (มุมมองเป้าหมาย) ค่าตอบแทนของnull
หรือvoid
จะให้มันกลับไปที่หน้าเดียวกันและทำให้ขอบเขตมุมมองปัจจุบันยังคงอยู่ ค่าส่งคืนของสตริงว่างหรือ ID มุมมองเดียวกันจะกลับไปยังหน้าเดียวกัน แต่สร้างขอบเขตมุมมองใหม่และทำลายถั่วที่มีมุมมองที่แอ็คทีฟอยู่ในปัจจุบันและสร้างถ้าทำได้
action
วิธีการอาจจะถูกต้องใด ๆMethodExpression
นอกจากนี้ยังมีคนที่ใช้ EL 2.2 ข้อโต้แย้งดังกล่าวดังต่อไปนี้:
<h:commandXxx value="submit" action="#{bean.edit(item)}" />
ด้วยวิธีนี้:
public void edit(Item item) {
// ...
}
โปรดทราบว่าเมื่อวิธีการกระทำของคุณส่งกลับสตริงเพียงอย่างเดียวคุณสามารถระบุสตริงนั้นในaction
แอตทริบิวต์ได้ ดังนั้นนี่คือเงอะงะทั้งหมด:
<h:commandLink value="Go to next page" action="#{bean.goToNextpage}" />
ด้วยวิธีการที่ไม่ใช้ความรู้สึกนี้จะส่งคืนสตริงฮาร์ดโค้ด:
public String goToNextpage() {
return "nextpage";
}
ให้ใส่สตริง hardcoded ลงในแอตทริบิวต์โดยตรงแทน
<h:commandLink value="Go to next page" action="nextpage" />
โปรดทราบว่าสิ่งนี้จะบ่งบอกถึงการออกแบบที่ไม่ดี: การนำทางโดย POST นี่ไม่ใช่ผู้ใช้หรือเป็นมิตรกับ SEO ทั้งหมดนี้อธิบายไว้ในเมื่อใดที่ฉันควรใช้ h: outputLink แทนที่จะเป็น h: commandLink และควรจะแก้ไขตามที่
<h:link value="Go to next page" outcome="nextpage" />
ดูวิธีการนำทางใน JSF ได้อย่างไร วิธีการทำ URL ที่สะท้อนให้เห็นถึงหน้าปัจจุบัน (และไม่ได้ก่อนหน้านี้อย่างใดอย่างหนึ่ง)
ตั้งแต่ JSF 2.x <f:ajax listener>
มีวิธีที่สาม
<h:commandXxx ...>
<f:ajax listener="#{bean.ajaxListener}" />
</h:commandXxx>
ajaxListener
วิธีโดยค่าเริ่มต้นมีลายเซ็นต่อไปนี้:
import javax.faces.event.AjaxBehaviorEvent;
// ...
public void ajaxListener(AjaxBehaviorEvent event) {
// ...
}
ใน Mojarra AjaxBehaviorEvent
อาร์กิวเมนต์เป็นตัวเลือกด้านล่างใช้ได้ดี
public void ajaxListener() {
// ...
}
แต่ใน MyFaces MethodNotFoundException
ก็จะโยน ด้านล่างใช้งานได้กับทั้งการติดตั้ง JSF เมื่อคุณไม่ต้องการโต้แย้ง
<h:commandXxx ...>
<f:ajax execute="@form" listener="#{bean.ajaxListener()}" render="@form" />
</h:commandXxx>
ผู้ฟัง Ajax ไม่ได้มีประโยชน์กับองค์ประกอบคำสั่งจริงๆ พวกเขาจะมีประโยชน์มากขึ้นกับการป้อนข้อมูลและส่วนประกอบเลือก/<h:inputXxx>
<h:selectXxx>
ในองค์ประกอบคำสั่งเพียงติดaction
และ / หรือactionListener
เพื่อความชัดเจนและรหัสเอกสารด้วยตนเองที่ดีขึ้น นอกจากนี้ยังชอบactionListener
ที่f:ajax listener
ไม่สนับสนุนการส่งกลับผลนำทาง
<h:commandXxx ... action="#{bean.action}">
<f:ajax execute="@form" render="@form" />
</h:commandXxx>
สำหรับคำอธิบายบนexecute
และrender
คุณลักษณะหัวPrimeFaces เข้าใจกระบวนการ / อัปเดตและ JSF f: อาแจ็กซ์รัน / ทำให้คุณลักษณะ
actionListener
s จะเรียกเสมอก่อนที่จะaction
ในลำดับเดียวกับที่พวกเขาจะได้รับการประกาศในมุมมองและติดอยู่กับองค์ประกอบ f:ajax listener
ถูกเรียกเสมอก่อนที่จะฟังการกระทำใด ๆ ดังนั้นตัวอย่างต่อไปนี้:
<h:commandButton value="submit" actionListener="#{bean.actionListener}" action="#{bean.action}">
<f:actionListener type="com.example.ActionListenerType" />
<f:actionListener binding="#{bean.actionListenerBinding()}" />
<f:setPropertyActionListener target="#{bean.property}" value="some" />
<f:ajax listener="#{bean.ajaxListener}" />
</h:commandButton>
จะเรียกใช้เมธอดตามลำดับต่อไปนี้:
Bean#ajaxListener()
Bean#actionListener()
ActionListenerType#processAction()
Bean#actionListenerBinding()
Bean#setProperty()
Bean#action()
สนับสนุนข้อยกเว้นพิเศษ:actionListener
AbortProcessingException
หากข้อยกเว้นนี้ถูกโยนออกมาจากactionListener
วิธีการแล้ว JSF จะข้ามตัวรับฟังการดำเนินการใด ๆ ที่เหลืออยู่และวิธีการดำเนินการและดำเนินการเพื่อแสดงการตอบสนองโดยตรง คุณจะไม่เห็นหน้าข้อผิดพลาด / ข้อยกเว้น JSF จะทำการบันทึก นี้จะทำได้โดยปริยายเมื่อใดก็ตามที่ยกเว้นอื่น ๆ actionListener
ที่จะถูกโยนลงมาจาก ดังนั้นหากคุณตั้งใจจะปิดกั้นเพจด้วยหน้าข้อผิดพลาดอันเป็นผลมาจากข้อยกเว้นทางธุรกิจคุณควรดำเนินการตามaction
วิธีที่แน่นอน
หากเหตุผลเดียวที่ใช้actionListener
คือให้มีvoid
วิธีการกลับไปที่หน้าเดียวกันนั่นเป็นเหตุผลที่ไม่ดี action
วิธีการที่ดีที่สุดที่สามารถยังกลับvoid
ในทางตรงกันข้ามกับสิ่งที่ IDEs บางให้คุณเชื่อว่าผ่านการตรวจสอบ EL โปรดทราบว่าตัวอย่างการแสดง PrimeFacesจะทิ้งกระจุยกระจายกับประเภทนี้actionListener
ทั่วทุกสถานที่ นี่เป็นสิ่งที่ผิดแน่นอน อย่าใช้สิ่งนี้เป็นข้อแก้ตัวในการทำเช่นนั้นด้วยตัวเอง
อย่างไรก็ตามในคำร้องขอ ajax ต้องการตัวจัดการข้อยกเว้นพิเศษ สิ่งนี้ไม่ว่าคุณจะใช้listener
คุณลักษณะของ<f:ajax>
หรือไม่ก็ตาม สำหรับคำอธิบายและตัวอย่างหัวเพื่อการจัดการข้อยกเว้นใน JSF คำขออาแจ็กซ์
actionListener
แต่ก็ยังไม่ได้ทำให้มันเป็นข้อแก้ตัวที่ดีactionListener
สำหรับการกระทำที่ผิดทางธุรกิจ
action
สอดคล้องกับสิ่งนั้น actionListener
สำหรับสิ่งของรอง แค่อยากจะชี้แจงว่าข้อยกเว้นจากactionListener
s สามารถแพร่กระจายถ้าจำเป็นต้องใช้เพื่อให้;)
actionListener
แอตทริบิวต์และจะต้องมีpublic
เช่นกัน processAction
ชื่อมีผลบังคับใช้เฉพาะเมื่อคุณกำลังใช้<f:actionListener type>
เพียงเพราะประเภทที่มีการใช้ActionListener
อินเตอร์เฟซที่มีชื่อตรงที่วิธีการprocessAction
ที่กำหนดโดย
<f:ajax>
งานคุณจะต้องใช้action
แอตทริบิวต์คำสั่งสำหรับการดำเนินธุรกิจ <h:commandButton action="#{bean.businessAction}"><f:ajax/></h:commandButton>
เช่น
ดังที่ BalusC ระบุไว้actionListener
โดยค่าเริ่มต้นจะทำการยกเว้นข้อยกเว้น แต่ใน JSF 2.0 จะมีข้อผิดพลาดเล็กน้อย กล่าวคือมันไม่เพียง แต่กลืนและบันทึก แต่จะเผยแพร่ข้อยกเว้น
สิ่งนี้เกิดขึ้นผ่านการโทรเช่นนี้:
context.getApplication().publishEvent(context, ExceptionQueuedEvent.class,
new ExceptionQueuedEventContext(context, exception, source, phaseId)
);
ฟังเริ่มต้นสำหรับเหตุการณ์นี้เป็นที่สำหรับกระโดงมีการตั้งค่าExceptionHandler
com.sun.faces.context.ExceptionHandlerImpl
การใช้งานนี้โดยทั่วไปจะ rethrow ข้อยกเว้นใด ๆ ยกเว้นเมื่อมันเกี่ยวข้องกับ AbortProcessingException ซึ่งถูกบันทึกไว้ ActionListeners จะตัดข้อยกเว้นที่เกิดขึ้นจากรหัสลูกค้าใน AbortProcessingException ที่อธิบายว่าทำไมสิ่งเหล่านี้ถึงถูกบันทึกไว้เสมอ
นี้ExceptionHandler
สามารถเปลี่ยนได้ แต่ในใบหน้า-config.xml กับการดำเนินการที่กำหนดเอง:
<exception-handlerfactory>
com.foo.myExceptionHandler
</exception-handlerfactory>
แทนที่จะฟังทั่วโลกถั่วตัวเดียวก็สามารถฟังเหตุการณ์เหล่านี้ได้ ต่อไปนี้เป็นข้อพิสูจน์แนวคิดนี้:
@ManagedBean
@RequestScoped
public class MyBean {
public void actionMethod(ActionEvent event) {
FacesContext.getCurrentInstance().getApplication().subscribeToEvent(ExceptionQueuedEvent.class, new SystemEventListener() {
@Override
public void processEvent(SystemEvent event) throws AbortProcessingException {
ExceptionQueuedEventContext content = (ExceptionQueuedEventContext)event.getSource();
throw new RuntimeException(content.getException());
}
@Override
public boolean isListenerForSource(Object source) {
return true;
}
});
throw new RuntimeException("test");
}
}
(โปรดทราบว่านี่ไม่ใช่วิธีการที่ผู้ฟังโค้ดควรใช้เพื่อวัตถุประสงค์ในการสาธิต!)
เรียกสิ่งนี้จาก Facelet เช่นนี้:
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core">
<h:body>
<h:form>
<h:commandButton value="test" actionListener="#{myBean.actionMethod}"/>
</h:form>
</h:body>
</html>
จะทำให้หน้าแสดงข้อผิดพลาดปรากฏขึ้น
ActionListener เริ่มทำงานก่อนโดยมีตัวเลือกในการแก้ไขการตอบกลับก่อนที่ Action จะถูกเรียกใช้และกำหนดตำแหน่งของหน้าถัดไป
หากคุณมีหลายปุ่มในหน้าเดียวกันซึ่งควรไปยังสถานที่เดียวกัน แต่ทำสิ่งที่แตกต่างออกไปเล็กน้อยคุณสามารถใช้ Action เดียวกันสำหรับแต่ละปุ่ม แต่ใช้ ActionListener ที่แตกต่างกันเพื่อจัดการฟังก์ชันการทำงานที่แตกต่างกันเล็กน้อย
นี่คือลิงค์ที่อธิบายความสัมพันธ์:
TL; DR :
ActionListener
s (สามารถมีได้หลาย) ดำเนินการเพื่อที่พวกเขาได้รับการจดทะเบียนก่อนaction
คำตอบยาว :
action
โดยทั่วไปธุรกิจจะเรียกใช้บริการ EJB และหากจำเป็นต้องตั้งค่าผลลัพธ์สุดท้ายและ / หรือนำทางไปยังมุมมองที่แตกactionListener
ต่างหากนั่นไม่ใช่สิ่งที่คุณกำลังทำอยู่ก็จะเหมาะสมกว่าเช่นเมื่อผู้ใช้โต้ตอบกับส่วนประกอบเช่นh:commandButton
หรือh:link
สามารถ ได้รับการจัดการโดยส่งชื่อของวิธี bean ที่ได้รับการจัดการในactionListener
แอตทริบิวต์ของ UI Component หรือเพื่อนำActionListener
อินเตอร์เฟสไปใช้และส่งผ่านชื่อคลาสการนำไปใช้งานกับactionListener
แอตทริบิวต์ของ Component UI