HI:
When using a form,the parameter form the clien can be bound to an object,for example:
processing-forms.html
In the client:
<s:form action="register">
<s:textfield name="personBean.firstName" label="First name" />
<s:textfield name="personBean.lastName" label="Last name" />
<s:textfield name="personBean.email" label ="Email"/>
<s:textfield name="personBean.age" label="Age" />
<s:submit/>
</s:form>
In the severside:
public class Register extends ActionSupport {
private static final long serialVersionUID = 1L;
private Person personBean;
//................
}
Then the parameter of the client are bound to the personBean instance.
Now,my problem is how to bind the parameters without a from?
My action work as a service which will be called in the javascript,so how to bind them?
I know how to get the parameters:
Map(String,Object) map=ActionContext.getContext.getParameters();
String firstName= map.get("firstname")[0];
//..........
This is too ugly :(
UPDATE
public class ParaWrapper(){
private String firstName;
public void setFirstName(String ..){
this.firstName=...
}
//the getter of firstName
public ....
}
public MyAction extends ActionSupport{
private ParaWrapper wrapper;
public void setXXX()...
public void getXXX()...
public void execute(){
System.out.println(wrapper); //here I can not get the parameters,it seems that the parameters are not poputed to this object.
}
}
Since I do not use the s:form tag,so How do struts know where to put the paramters ?
You handle it the same way. If your field is named firstname, then you will need a setFirstname method on the action. Whether the parameters are coming from a form or from JavaScript is irrelevant.
Update
Based on your revised code example, you will need a getWrapper method on your action to expose the ParaWrapper object.
You can avoid the "wrapper." prefix by implementing the ModelDriven interface and making ParaWrapper your model. Then you would just have parameters such as: firstName, lastName, etc (whatever fields are on ParaWrapper).
I think you shouldn't use private fields for the values that should be set via Struts2.
Explanation:
I don't know how you post the parameters to your action via JavaScript, but it should work if you add the necessary parameters to the URL you call. You can possibly call (as suggested in the mailing list):
http://yourdomain/yourstruts.action?personBean.firstName=a_string&personBean.lastName=my_lastName& ... (more person parameters)
Struts2 will understand the dot-notation and try to set the personBean variable in your target action. If this is of a Bean class (with an empty public constructor and public setters for each parameter), it will generate a new object and call the setters with the parameters. If it cannot access the parameters, nothing can be set.
So, if your setters are public and your PersonBean class is defined correctly, a PersonBean should be in your actions personBean field.
Hope this helps.
Related
I'm using the Kendo AutoComplete client javascript widget, which sends server requests such as the following:
https://domainName/Proto2/api/Goal/Lookup?text=ABC&goalId=8b625c56-7b04-4281-936f-b88d7ca27d76&filter%5Blogic%5D=and&filter%5Bfilters%5D%5B0%5D%5Bvalue%5D=&filter%5Bfilters%5D%5B0%5D%5Boperator%5D=contains&filter%5Bfilters%5D%5B0%5D%5Bfield%5D=Description&filter%5Bfilters%5D%5B0%5D%5BignoreCase%5D=true&_=1423833493290
The MVC server side method to receive this is:
[Route("api/Goal/Lookup")]
[HttpGet] // if the action name doesn't start with "Get", then we need to specify this attribute
public ICollection<IAllegroGoalContract> Lookup(Guid goalId, string text = "")
The problem occurs if the client sends an empty value for the text parameter (ex: text=&goalId=8b625c56-7b04-4281-936f-b88d7ca27d76). In this case .net returns the following error.
"System error - unable to process parameters
(goalId,text,text.String) - invalid data detected"
I've tried various Route attribute values:
[Route("api/Goal/Lookup/{goalId:guid},{text?}")]
[Route("api/Goal/Lookup/{text?}")]
Looks like your parameters are used as a filter, so instead of the GoalId and Text parameters to be part of the route, define a class like this:
public class LookupOptions
{
public Guid GoalId { get; set; } // change this to Guid? if the client can send a nullable goalId.
public string Text { get; set; }
}
So your method signature will be :
[Route("api/Goal/Lookup")]
[HttpGet]
public ICollection<IAllegroGoalContract> Lookup([FromUri]LookupOptions options)
{
// Note that [FromUri] will allow the mapping of the querystring into LookupOptions class.
}
Now, you can pass your options from the client as part of the Query string and it will be assigned to the LookupOptions parameter.
Hope this helps.
I have an #Stateful EJB in a #SessionScoped bean.
My EJB:
#Stateful
public class SomeEjb implements someEjbInterface{
private SomeEntity entity;
#Override
public Boolean getEntityAssigned(){
return entity!= null;
}
#Override
public void selectEntity(String id){
//assign entity with some values retrieved from db according to the criteria
}
}
My Session Scoped Bean:
#ManagedBean
#SessionScoped
public class SessionBean{
#EJB
private SomeEntity entity;
//getter and setter
public String selectEntity(){
entity.selectEntity(someId);
return null;
//Edited: if using this, no problem will occur.
// return "index";
}
}
My Page index.xhtml (xmlns omitted):
<h:form>
<h:commandButton value="Select entity" action="#{sessionBean.selectEntity()}">
</h:form>
<h:link outcome="someOutcome" disabled="#{sessionBean.entity.entityAssigned}">
I expect that the link is initially disabled, when I click "Select entity", the ejb will retrieve an entity from database, if the retrieve succeed, then the link will be enabled.
The problem is that when I click the button, the link will break (rendering an tag with href attribute but no innerHtml to click). It can only be fixed if I reload the page without data resubmission(re-enter the page by pressing enter at the url, not using F5 which will resubmit the form).
The error message is:
HTML nesting warning on closing span: element a rendered by component : {Component-Path : some long component path refer to the link element} not explicitly closed
Does anyone know what did I messed up with the rendering?
Edit:
I just found out that the problem does not exist if I return the outcome of that same page instead of null, which probably discard #ViewScoped bean I used to call sessionBean.selectEntity(). Can anyone explain the mechanism for the cause of this difference?
The documentation says that the "toString" of the object returned by the method will be used to handel navigation, you can try returning "" (no navigation, only refresh the page).
Please add the relevant parts of the xhtml.
I have two pages myaccount.xhtml and selectbank.xhtml
In my account page there is one option for recharge account in which user will enter the amount when user will press submit button then it will goto the select bank page using following bean method.
public String gotoPayMethod() {
FacesMessage doneMessage=new FacesMessage("Redirecting to Payment Type Page");
FacesContext.getCurrentInstance().addMessage(null, doneMessage);
return "SelectBank";
}
When user will goto to selectbank there user will have to submit payment method but in this page it shows the amount as null which was entered in the previous page.
Both the pages are using the same bean and the scope of the bean is request scope.
So how can I access that value without passing this values through URL GET method.
Just for my satisfaction I used session scope then it was working but I know thats not the proper way because I start using session scope for each pages then it will not be efficient.
Thanks
Well, if your beans are RequestScoped than you don't have same bean for both pages. These beans are recreated for every request, so you should pass parameters. Change return statement of your gotoPayMethod() to:
return "SelectBank?faces-redirect=true&includeViewParams=true";
and on selectbank.xhtml add:
<f:metadata>
<f:viewParam name="amount" value="#{bean.amount}" />
</f:metadata>
Adapt this to your property and bean name.
If using parameters is not a solution you can add this parameter in the session, and remove it from session in second bean when you retrieve it:
FacesContext.getCurrentInstance().getExternalContext().getSessionMap().put("amount", amount);
((HttpServletRequest)FacesContext.getCurrentInstance().getExternalContext().getRequest()).getSession().removeAttribute("amount");
Second construction for removing the attribute is necessary as Map returned from getSessionMap() is immutable.
You can use the #{flash} object that will keep your data until the next view. This way you won't need to deal with view parameters.
Details from myaccount.xhtml:
<h:form>
<h:outputText value="Enter amount: " />
<h:inputText value="#{flash.amount}" />
<br/>
<h:commandButton value="Go to payment method" action="#{bean.gotoPayMethod}" />
<h:form>
Bean of both views:
#ManagedBean
#RequestScoped
public class Bean {
#ManagedProperty("#{flash}")
private Flash flash;
private int amount = -1;
public Bean () { }
public String getAmount() {
if(amount == -1) {
int val = Integer.parseInt((String)flash.get("amount"));
flash.keep("amount");
amount = val;
}
return amount;
}
public Flash getFlash() {
return flash;
}
public void setFlash(Flash flash) {
this.flash = flash;
}
public String gotoPayMethod() {
//do business job
return "SelectBank?faces-redirect=true";
}
}
Details from selectbank.xhtml:
<h:outputText value="Amount entered by user is #{bean.amount}" />
Your use case is not of simple request/response cycle, the life span is more than one request response which makes it candidate for session scope.
Using hidden variable or GET parameters in URL is not good practice especially for a banking application. Where security is so important dont compromise on small memory foot print.
If flash scope map simplifies the case you can use it, but I would not go for such a thing.
Update: Forgot to mention you can check Conversation scope too.
i have a strange behaviour in my app:
I use a SessionScope bean (Bean A) to hold users preferences. In my other Bean (Bean B), which is in RequestScope, I inject the SessionScope bean.
Bean B has a #PostConstruct method to retrieve a list of values from the database depending on the value in Bean A. The application gets confused when the user changes the value in Bean A and its value in Bean B is not correct at the time #PostConstruct method is invoked. I tested it with logs.
I think all setter methods will be updated before Invoke Application Phase?
Here is a code sample:
Bean A:
#Named
#SessionScoped
public class SessionBean implements Serializable {
private static final long serialVersionUID = -4214048619877179708L;
#Inject private Logger log;
private BankAccount selectedBankAccount;
public BankAccount getSelectedBankAccount() {
return selectedBankAccount;
}
public void setSelectedBankAccount(BankAccount selectedBankAccount) {
log.info("ba: " + selectedBankAccount);
this.selectedBankAccount = selectedBankAccount;
}
Bean B:
#RequestScoped
public class SubAccountListProducer {
#Inject private SessionBean sessionBean;
#Inject private EntityManager em;
#PostConstruct
public void retrieveAllSubAccount() {
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<SubAccount> criteria = cb.createQuery(SubAccount.class);
Root<SubAccount> account = criteria.from(SubAccount.class);
log.info("retrieveAllSubAccount: " + sessionBean.getSelectedBankAccount());
criteria.select(account).where(cb.equal(account.get("bankAccount"), sessionBean.getSelectedBankAccount()));
criteria.select(account).orderBy(cb.desc(account.get("name")));
entityList = em.createQuery(criteria).getResultList();
}
Sample logs:
ba: BankAccount [accountId=123456789, bankName=Ing DiBa, blz=50010517]
retrieveAllSubAccount: BankAccount [accountId=123456789, bankName=Ing DiBa, blz=50010517]
retrieveAllSubAccount: BankAccount [accountId=123456789, bankName=Ing DiBa, blz=50010517]
ba: BankAccount [accountId=987654321, bankName=Barclaycard Barclays Bank, blz=20130600]
As you can see... the first two logs are correct... if the user changes preferences (updates the SessionBean), the view will be rerendered with JSF and the last two logs are not in correct order and my app gets confused.
Thank you for help.
The #PostConstruct is not executed during invoke action phase. It is executed directly after bean's construction. The PostConstruct should only be used to preinitialize some stuff depending on injected dependencies directly after bean's construction. Because your bean is request scoped instead of conversation scoped (or view scoped), it will be constructed on every single request.
You need to do the updating/refreshing job in the real action method instead, which is the method you've specified in the <h:commandButton>/<h:commandLink>. E.g.
<h:commandButton value="Submit" action="#{bean.submit}" />
with
public void submit() {
// ...
retrieveAllSubAccount();
}
I also suggest to put your bean in the CDI conversation scope or JSF view scope, so that it don't unnecessarily get reconstructed on every postback to the same view.
I am new for JSF. In my project am using #ManagedBean, #RequestScoped. Am using 4 pages in my project. My problem was bean values not maintain in the second, third and fourth pages. Am using getter and setter properly. If i not use #ManagedProperty the bean value maintained properly. But i need to use the
#ManagedProperty. Could you please advise me how to solve this issue. I have copied some sample code for reference.
#ManagedBean
#RequestScoped
public class ArticlePrepToolManagedBean implements Runnable, Serializable {
#ManagedProperty (value="#{param.jidName}")
private String jidName;
#ManagedProperty (value="#{param.aidName}")
private String aidName;
private List<com.elsevier.ArticlePrepTool.db.ItemZipContains> usabilityDetailList = null;
public String getAidName() {
return aidName;
}
public void setAidName(String aidName) {
this.aidName = aidName;
}
public String getJidName() {
return jidName;
}
public void setJidName(String jidName) {
this.jidName = jidName;
}
public List<ItemZipContains> getUsabilityDetailList() {
return usabilityDetailList;
}
public void setUsabilityDetailList(List<ItemZipContains> usabilityDetailList) {
ArticlePrepToolManagedBean.usabilityDetailList = usabilityDetailList;
}
}
My project url is (http://localhost:8080/articlepreptool/) but input for my project is jidName=AEA aidName=10663. that input given by some other webpage that is if user trigger using the following href "PrepTool". Depends on the input i fetched some data in my project DB (using JPA) and list out the data in the first page. But if i goes to next page all previous data stored in that list which i got from DB was cleared that is all list values and variables which set in the bean becomes null. So could you please advise me how to solve this issue.That problem occured only if i used the #ManagedProperty. I used #ManagedProperty to fetch the input values comes through url, because the input values of my project comes through other web page.
A #ManagedProperty("#{param.foo}") basically sets the HTTP request parameter with name "foo" as a bean property directly after bean's construction. If you're retrieving null values for them, then it simply means that those parameters are not present in the HTTP request.
Assuming that you're navigating by a plain link, then you need to fix your links to include the request parameters:
<h:link value="Go to page2" outcome="page2">
<f:param name="jidName" value="#{bean.jidName}" />
<f:param name="aidName" value="#{bean.aidName}" />
</h:link>
This will result in something like:
<a href="page2.xhtml?jidName=foo&aidname=bar">
This way those parameters can be set as bean properties.
Alternatively, instead of #ManagedProperty you could also use <f:viewParam> on all pages and add includeViewParams=true to the outcome. See also ViewParam vs #ManagedProperty(value = "#{param.id}")
If you're navigating by a form submit, then there's really no reason to use them. Or you must be abusing forms instead of links for plain vanilla page-to-page navigation.