I have an <a4j:commandLink> which I need to fire an action on my controller,
<h:form>
<a4j:commandLink
actionListener="#{controller.send(viewBean.id, cc.attrs.guid)}"
onbegin="$.efc.busy($.messages.sending);"
oncomplete="$.efc.busy('', true);">
Send offer to this dealer
</a4j:commandLink>
</h:form>
When I click on the link, the onbegin javascript is successfully fired but the action is never called. Here is my action from my controller:
public void send(String id, String guid) {
if (id != null && guid != null) {
...
}
}
My viewBean is view scoped and the guid comes from the component... What am I missing here?
EDIT
If I change the link to a button it works... but I need a link:
<a4j:commandButton
action="#{controller.send(viewBean.id, cc.attrs.guid)}"
onbegin="$.efc.busy($.messages.sending);"
oncomplete="$.efc.busy('', true);"
value="Send offer to this dealer">
</a4j:commandButton>
According to documentation :
"MethodExpression representing an action listener method that will be notified when this component is activated by the user. The expression must evaluate to a public method that takes an ActionEvent parameter, with a return type of void."
javax.el.MethodExpression (signature must match void actionListener(javax.faces.event.ActionEvent))
Related
I have an accordion panel with tabs containing name of students. I select on one of the tab and choose edit. I am calling a tab change listener and getting student object and trying to edit it works fine.
But My problem is when for the first time Accordion loads, even though I select on the tab,and click on edit, I will get not get student object. Its giving me null pointer exception. since Tab change event does not get called for the first time it loads. How do I solve this?
private StudBean formBean;
public StudBean getFormBean() {
if(formBean==null){
formBean= new StudBean();
}
return formBean;
}
public void onTabChange(TabChangeEvent event) {
formBean = (StudBean) event.getData();
}
public String edit(){
formBean = testDelegate.getDetails(formBean.getName(),formBean.getId());
return "/ui/EditStudent?faces-redirect=true";
}
<p:commandButton process="#this" value="edit" action="#{testBean.edit}" >
<p:accordionPanel value="#{testBean.students}" var="stud">
<p:ajax event="tabChange" listener="#{testBean.onTabChange}" immediate="true"/>
<p:tab title="#{stud.name}">
Trying to develop a composite component using jsf2.0 (Mojarra) which should render command buttons dynamically based on the list from the bean. I was able to render the buttons but action is not getting triggered.Could any one please help me to resolve the issue?
Here follows the code
<composite:interface>
<composite:attribute name="buttonList" required="true"
type="java.util.List" />
<composite:attribute name="beanName" required="true"
type="java.lang.Object" />
</composite:interface>
<composite:implementation>
<ui:repeat var="listItem" value="#{cc.attrs.buttonList}">
<h:commandButton value="#{listItem.buttonName}"
action="#{cc.attrs.beanName.listItem.buttonAction}">
</h:commandButton>
</ui:repeat>
</composite:implementation>
This is used as
<utils:buttonGroup buttonList="#{testButtonBean.buttonList}"
beanName="#{testButtonBean}" />
The bean looks like
public class TestButtonBean {
public List<ButtonPOJO> buttonList = new ArrayList<ButtonPOJO>();
public List<ButtonPOJO> getButtonList() {
return buttonList;
}
public void setButtonList(List<ButtonPOJO> buttonList) {
this.buttonList = buttonList;
}
public void preProcess() {
if (null != buttonList && buttonList.size() == 0) {
ButtonPOJO ob1 = new ButtonPOJO("Continue", "next");
ButtonPOJO ob2 = new ButtonPOJO("Back", "prev");
buttonList.add(ob1);
buttonList.add(ob2);
}
}
public String next() {
return "page1";
}
public String prev() {
return "page2";
}
}
action="#{cc.attrs.beanName.listItem.buttonAction}"
This is not right. This syntax is basically looking for a property listItem on beanName and then trying to invoke the literal action buttonAction() on it.
You need the brace notation action="#{bean[methodName]}" if you want to specify the action method name as string coming from another bean property.
action="#{cc.attrs.beanName[listItem.buttonAction]}"
Unrelated to the concrete problem, if the above solution still fails, then that can only mean that the value="#{cc.attrs.buttonList}" has incompatibly changed during the form submit request. You need to make sure that exactly the same list is prepared during the postback as it was during the initial request. See also point 4 of commandButton/commandLink/ajax action/listener method not invoked or input value not updated.
I have an application using PrettyFaces and PrimeFaces. One page is accessed using the URL pattern /#{ location : navigationBean.location }/#{ year : navigationBean.year }.
On that page I have a p:selectOneMenu containing possible values for location. If I change this menu I want the page to be refreshed with the same year and the new location.
I've tested this using AJAX to set the location in the navigationBean and then pressing a p:commandButton with the outcome pretty:view (view being the url-mapping id). This works but I want to get rid of the button.
I've tried the following, but sadly the return String in the method doesn't perform a navigation.
The JSF:
<p:selectOneMenu value="#{navigationBean.location}">
<p:ajax listener="#{navigationBean.navigate}"/>
<f:selectItems value="#{locationBean.locations}"/>
</p:selectOneMenu>
Backing bean:
public String navigate(AjaxBehaviorEvent event)
{
return (location != null) ? "pretty:view" : null;
}
The PrettyFaces config:
<url-mapping id="view">
<pattern value="/#{ location : navigationBean.location }/#{ /\\d{4}/ year : navigationBean.year }/"/>
<view-id value="/view.xhtml"/>
</url-mapping>
You can't return a navigation outcome from (ajax) action listeners. You can only return it from real action methods as used in <h:commandButton action>.
Use NavigationHandler#handleNavigation() instead.
public void navigate() {
FacesContext context = FacesContext.getCurrentInstance();
context.getApplication().getNavigationHandler()
.handleNavigation(context, null, (location != null) ? "pretty:view" : null);
}
I have a few pages that needs a userId to work, thus the following code:
userpage.xhtml
<!-- xmlns etc. omitted -->
<html>
<f:metadata>
<f:viewParam name="userId" value="#{userPageController.userId}"/>
</f:metadata>
<f:view contentType="text/html">
<h:head>
</h:head>
<h:body>
<h:form>
<h:commandButton action="#{userPageController.doAction}" value="post"/>
</h:form>
</h:body>
</f:view>
userPageController.java
#Named
#ViewScoped
public class userPageControllerimplements Serializable {
private static final long serialVersionUID = 1L;
#Inject protected SessionController sessionController;
#Inject private SecurityContext securityContext;
#Inject protected UserDAO userDAO;
protected User user;
protected Long userId;
public UserPage() {
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
public Long getUserId() {
return userId;
}
public void setUserId(Long userId) {
if(!FacesContext.getCurrentInstance().isPostback()){
User u = userDAO.find(userId);
this.userId = userId;
this.user = u;
}
}
public void doAction(){
}
}
However, after doAction is called, the view parameter in the url disappears. The bean still works due to its viewscoped nature, but it ruins my attempts of future navigation. When i search around, I get the impression that the view parameter should remain after a post thus reading userpage.jsf?userId=123, but this is not the case. What is really the intended behaviour?
Related to this, I've tried to implement automatic adding of view parameters when navigating to another page where I want to keep the userId. It seems to work for others, but for me, the userId in the ViewRoot is always null. Code below used to retrieve the viewparameter (i know i could use my temporarily stored userId in the bean for navigation, but this solution would be much fancier):
String name = "userId";
FacesContext ctx = FacesContext.getCurrentInstance();
ViewDeclarationLanguage vdl = ctx.getApplication().getViewHandler().getViewDeclarationLanguage(ctx, viewId);
ViewMetadata viewMetadata = vdl.getViewMetadata(ctx, viewId);
UIViewRoot viewRoot = viewMetadata.createMetadataView(ctx);
UIComponent metadataFacet = viewRoot.getFacet(UIViewRoot.METADATA_FACET_NAME);
// Looking for a view parameter with the specified name
UIViewParameter viewParam = null;
for (UIComponent child : metadataFacet.getChildren()) {
if (child instanceof UIViewParameter) {
UIViewParameter tempViewParam = (UIViewParameter) child;
if (name.equals(tempViewParam.getName())) {
viewParam = tempViewParam;
break;
}
}
}
if (viewParam == null) {
throw new FacesException("Unknown parameter: '" + name + "' for view: " + viewId);
}
// Getting the value
String value = viewParam.getStringValue(ctx); // This seems to ALWAYS be null.
One last thought is that the setter methods still seem to work, setUserId is called with the correct value on post.
Have I completly missunderstood how view parameters work, or is there some kind of bug here? I think my use case should be extremly common and have basic support in the framework.
When i search around, I get the impression that the view parameter should remain after a post thus reading userpage.jsf?userId=123, but this is not the case. What is really the intended behaviour?
This behaviour is correct. The <h:form> generates a HTML <form> element with an action URL without any view parameters. The POST request just submits to exactly that URL. If you intend to keep the view parameters in the URL, then there are basically 3 ways:
Bring in some ajax magic.
<h:commandButton action="#{userPageController.doAction}" value="post">
<f:ajax execute="#form" render="#form" />
</h:commandButton>
This way the initially requested page and thus also the request URL in browser's address bar remains the same all the time.
If applicable (e.g. for page-to-page navigation), make it a GET request and use includeViewParams=true. You can use <h:link> and <h:button> for this:
<h:button outcome="nextview?includeViewParams=true" value="post" />
However, this has an EL security exploit in Mojarra versions older than 2.1.6. Make sure that you're using Mojarra 2.1.6 or newer. See also issue 2247.
Control the generation of action URL of <h:form> yourself. Provide a custom ViewHandler (just extend ViewHandlerWrapper) wherein you do the job in getActionURL().
public String getActionURL(FacesContext context, String viewId) {
String originalActionURL = super.getActionURL(context, viewId);
String newActionURL = includeViewParamsIfNecessary(context, originalActionURL);
return newActionURL;
}
To get it to run, register it in faces-config.xml as follows:
<application>
<view-handler>com.example.YourCustomViewHandler</view-handler>
</application>
This is also what OmniFaces <o:form> is doing. It supports an additional includeViewParams attribute which includes all view parameters in the form's action URL:
<o:form includeViewParams="true">
Update: obtaining the view parameters of the current view programmatically (which is basically your 2nd question) should be done as follows:
Collection<UIViewParameter> viewParams = ViewMetadata.getViewParameters(FacesContext.getCurrentInstance().getViewRoot());
for (UIViewParameter viewParam : viewParams) {
String name = viewParam.getName();
Object value = viewParam.getValue();
// ...
}
Am I right that for solution 1 <f:ajax execute="#form" render="#form" /> to work one would also have to include something like
<f:param name="userId" value="#{userPageController.userId}"/>
inside the <h:commandButton>?
Like it is described in https://stackoverflow.com/a/14026995/811046
I suspected there was some hidden magic somewhere that stopped what looks like actual method calls all over the place in T4MVC. Then I had a view fail to compile, and the stackTrace went into my actual method.
[Authorize]
public string Apply(string shortName)
{
if (shortName.IsNullOrEmpty())
return "Failed alliance name was not transmitted";
if (Request.IsAuthenticated == false || User == null || User.Identity == null)
return "Apply authentication failed";
Models.Persistence.AlliancePersistance.Apply(User.Identity.Name, shortName);
return "Applied";
}
So this method isn't generating in the template after all.
<%=Ajax.ActionLink("Apply", "Apply", new RouteValueDictionary() { { "shortName", item.Shortname } }, new AjaxOptions() { UpdateTargetId = "masterstatus" })%>
<%=Html.ActionLink("Apply",MVC.Alliance.Apply(item.Shortname),new AjaxOptions() { UpdateTargetId = "masterstatus" }) %>
The second method threw an exception on compile because the method Apply in my controller has an [Authorize] attribute so that if someone that isn't logged on clicks this, they get redirected to login, then right back to this page. There they can click on apply again, this time being logged in.
And yes I realize one is Ajax.ActionLink while the other is Html.ActionLink I did try them both with the T4MVC version.
Update: I see the problem. T4MVC only supports actions that return ActionResult, so it's not processing this particular action that returns string. You can fix this by changing it as follows:
[Authorize]
public ActionResult Apply(string shortName) {
if (shortName.IsNullOrEmpty())
return Content("Failed alliance name was not transmitted");
if (Request.IsAuthenticated == false || User == null || User.Identity == null)
return Content("Apply authentication failed");
Models.Persistence.AlliancePersistance.Apply(User.Identity.Name, shortName);
return Content("Applied");
}
Note how it returns an ActionResult, and calls 'return Content("...")' instead of directly returning strings.
Could you give more details on the compile exception that you are getting? I assume this is something you see in the browser and not in VS? Could you include the full text of the error?
Generally, the T4MVC calls through the MVC prefix are never supposed to call the actual action method. Instead, they call an overridden method in a derived class. Look for a generated file called something like AllianceController.generated.cs (under T4MVC.tt). You should see an overridden 'Apply' method in there, which just does what T4MVC needs.