struts 2 name of field name conflict - struts2

I have a field in an object
public class MyObj {
private Date qGuardExpireDate;
...
public void setQGuardExpireDate(Date qGuardExpireDate) {
this.qGuardExpireDate = qGuardExpireDate;
}
public Date getQGuardExpireDate() {
return qGuardExpireDate;
}
....
I have a getter and setting in the action class (struts 2.3.36) for the myObj definition.
In the jsp I have
<s:hidden name="myObj.qGuardEffectDate" />
I have other dates in the dataobject that work fine in the same jsp. This one however will not populate the value in the jsp....
I was playing around and added this to the dataobject:
public Date getGuardExpireDate() {//removed q from name of getter
return qGuardExpireDate;
}
Change the jsp to show the
<s:hidden name="myObj.guardEffectDate" />
And that one works fine!!! Returning the same value from the object. Any ideas why that would be?? It seems to hate that variable name.

Related

Struts 2.5.12: Text tag displays name of the field from model class instead of value

I am uppgrading one of the project from Struts 2.3.3 to 2.5.12 and I see that in the upgraded project <s:text /> tag is unable to display the value of the field from model class and instead it just displays the name. It works perfectly fine when I use Struts 2.3.3.
My Action class is like:
public class MyAction {
private Member member;
public Member getMember() {
return member;
}
}
My model classes are like:
public class Member implements Serializable {
private Address address;
public Address getAddress() {
return address;
}
}
public class Address implements Serializable {
private String city;
public String getCity() {
return city;
}
}
In the JSP, I have:
<s:text name="member.address.city" />
The output I see on page is member.address.city, where as I am expecting the value of the city.
I see in debug mode that control is coming to the getCity() method of Address class and the city field has the value London, but still it doesn't get displayed in UI.
Does anyone know what the issue is?
According to the docs:
If the named message is not found in a property file, then the body of
the tag will be used as default message. If no body is used, then the
stack can be searched, and if a value is returned, it will written to
the output. If no value is found on the stack, the key of the message
will be written out.

Value is not setting from bean to form and form to bean-Struts

I am not able get value from form to bean in action class.
Here small piece of code :
in jsp,i have text box like show below:
<input type="text" name="objectID.Norder" id="objectID.Norder" />
In action class ,i have added one name and getter and setter method as shown below:
private String Norder;
public void setNOrder(String norder){
this.Norder=norder;}
public String getNOrder(){
return Norder;
}
This is small piece of code in action class.Value of Norder is null when i submit form with valid inputs and early it was working fine .When i am new jars it is not working .
Getters and setters should ideally be in your ActionForm(form bean) class rather than your Action class
public class LoginForm extends ActionForm {
private String nOrder;
public void setNOrder(String norder){
this.Norder=norder;}
public String getNOrder(){
return Norder;
}
}
Use struts tags for your jsps:
<html:text property="nOrder" />
Are you trying to pre-populate your form with any values are when do you want your jsp to show some values in the text box?

struts 2 checkboxlist doesn't save values

jsp content:
<s:checkboxlist list="list" name="values"/>
action content:
public List<Foo> getList() {
return list;
}
public String[] getValues() {
return values;
}
public void setValues(String[] values) {
this.values = values;
}
class Foo:
private String code;
public String getCode() {
return code;
}
public String toString() {
return code;
}
When I put breakpoint at getValues() method, I clearly see it's being called with some values there. But that values don't appear to be selected on a page.
What am I missing here?
I found a solution. I've added
<s:checkboxlist list="list" name="values" listKey="code" listValue="code />
to jsp and it all started working after that. It generates the same html, but it seems that despite rendering correctly, that properties are required by struts to check what values should be set. And there is no emphasis on that in struts docs.
EDIT:
It seems that only this is actually requited for this thing to work:
<s:checkboxlist list="list" name="values" listKey="code"/>

Bind input field to custom object instead of string

I'm using ASP.NET MVC 4 for an internal web application and I have a desire to bind HTML input fields to a custom object rather than string.
In the HTML I have input fields that will look like the following:
<input type="hidden" name="First" value="1;Simple" />
<input type="hidden" name="First" value="2;Sample" />
<input type="hidden" name="Second" value="1;Over" />
<input type="hidden" name="Third" value="22;Complex" />
<input type="hidden" name="Third" value="17;Whosit" />
This will happily bind to ViewModel properties like:
public string[] First { get; set; }
public string[] Second { get; set; }
public string[] Third { get; set; }
Each string is a delimited string of key+value that I'd love to have automatically parsed into a concrete object (I have one already defined.) Ideally I'd want it to bind exactly as above but using my object that would know how to split the delimited string into the proper properties.
I can't figure out how to get MVC to bind to a custom object. I've used constructors and implicit operator definitions but I can't get it to work with anything but string datatype.
I know I could get this to work if I pre-split the values into pairs in the HTML but I'm using a JavaScript library that doesn't give this ability. For instance I know repeating {name}.Label and {name}.Value would work to bind to the string properties on my complex object but this is prohibitive and a non-starter.
I have gotten this to work with a custom object to handle File Uploads but I suspect that worked only because it inherited from the same base object. I can't do this here since string is a sealed type and can't be extended.
My last resort is to find the default model binder code and reflect that to figure out how it's assigning the values to see if it teaches me anything that I can override. I'd prefer not to go the route of a custom binder I'd have to write myself and if it comes down to it I'll just have duplicate ViewModel fields and convert them myself but I'd really love to avoid this if there's already a capability for the model binder to do this for me.
Here is what you can do. Let's say your MyThing class is something like this:
public class MyThing
{
public int Id { get; set; }
public string Name { get; set; }
public override string ToString()
{
return string.Format("{0};{1}", this.Id, this.Name);
}
}
Then, you can create a custom model binder for it like below:
public class MyModelBinder : IModelBinder
{
public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
ValueProviderResult valueResult = bindingContext.ValueProvider
.GetValue(bindingContext.ModelName);
ModelState modelState = new ModelState { Value = valueResult };
object actualValue = null;
if (valueResult != null && !string.IsNullOrEmpty(valueResult.AttemptedValue))
{
if(valueResult.AttemptedValue.Contains(';'))
{
try
{
var attemptedValue = valueResult.AttemptedValue.Split(';');
int id = int.Parse(attemptedValue.First());
string name = attemptedValue.Last();
actualValue = new MyThing { Id = id, Name = name };
}
catch(Exception e)
{
modelState.Errors.Add(e);
}
}
else
{
modelState.Errors.Add("Invalid value.");
}
bindingContext.ModelState.Add(bindingContext.ModelName, modelState);
}
return actualValue;
}
}
You'll need to register your ModelBinder in Application_Start event of Global.asax like this:
ModelBinders.Binders.Add(typeof(MyThing), new MyModelBinder());
The question didn't get a single bite so I looked at the default model binder to see what was happening under the covers. There are a number of stages it goes through to see if a value can be converted to the ViewModel type but most of them are inaccessible to me. I did find a segment of code that fell back to using a type converter which I'd never used before.
Using this MSDN Type Converter how-to, I made a simple converter and decorated my class with the appropriate attribute and it just worked. I'm not sure what the performance implications are but it really simplifies my ViewModel code.
This example below is working for me. Keep in mind I'm only converting from the simple string type used by the DefaultModelBinder so it doesn't look like it's doing much but it solves my need and taught me a new feature of the framework.
public class MyThingConverter : TypeConverter
{
public override bool CanConvertFrom(ITypeDescriptorContext context,
Type sourceType)
{
if (sourceType == typeof(string))
return true;
return base.CanConvertFrom(context, sourceType);
}
public override object ConvertFrom(ITypeDescriptorContext context,
CultureInfo culture, object value)
{
if (value is string)
return new MyThing((string)value);
return base.ConvertFrom(context, culture, value);
}
}
[TypeConverter(typeof(MyThingConverter))]
public class MyThing
{
public MyThing(string combinedValue)
{
//Split combinedValue into whatever properties I need
...
}
public override string ToString()
{
return string.Format("{0};{1}", prop1, prop2);
}
...
}
And that's it. So far it's working as expected.

My h:inputText value is not validated when it is bound to a field of some instance of a parametrized class gathered from a HashMap?

I am trying to do some "custom lightweight" JSF component binding. In my bean (controlling some JSF page) i'm declaring a HahsMap where keys ranging over my h:inputText ids (appearing in the page) map these ids to custom HInputText<T> objects (T in the example given below is Long). Then i am using HInputText<T> objects to hold a subset of corresponding h:inputText attributes : value supposed to be of type T, rendered, required, etc. In this direction fields from the HInputText<T> objects give values to the h:inputText attributes.
My problem is that when using such an h:inputText inside a h:form, JSF validation does not take place : i can type alphanumeric chars in the h:inputText (supposed to hold a Long value) and my form submits without showing any errors. Note that required and rendered attributes are managed correctly (since required is set to true, i have an error when i leave the h:inputText field empty) and that when i try to display back the value of the h:inputText in the page using some h:outputText the alphanumeric chars are displayed.
Is there some trick to make JSF validation work without having to explicitly define a custom Validator for each h:inputText?
HInputText.class:
public class HInputText<T>
{
private String id;
private boolean rendered;
private boolean required;
private T value;
private Class<T> myClass;
// getters and setters for all fields
public HInputText(Class<T> myClass, String id)
{
this.myClass = myClass;
this.id = id;
this.rendered = true;
this.required = true;
}
}
Code snippet from my Managed Bean :
#ManagedBean(name="saisieController")
#SessionScoped
public class SaisieController
{
...
private HashMap<String,HInputText<Long>> htagLongInputTexts;
public HashMap<String, HInputText<Long>> getHtagLongInputTexts()
{
return htagLongInputTexts;
}
public void setHtagLongInputTexts(HashMap<String, HInputText<Long>> hLongInputTexts)
{
this.htagLongInputTexts = hLongInputTexts;
}
public void addHtagLongInputText(HInputText<Long> hLongInputText)
{
getHtagLongInputTexts().put(hLongInputText.getId(), hLongInputText);
}
public HInputText<Long> getHtagLongInputText(String hLongInputTextId)
{
return(getHtagLongInputTexts().get(hLongInputTextId));
}
#PostConstruct
public void init()
{
setHtagLongInputTexts(new HashMap<String, HInputText<Long>>());
addHtagLongInputText(new HInputText<Long>(Long.class, "HIT_LongTestHIT"));
}
public String doNothing()
{
return null;
}
}
and finally a snippet from my jsf page:
<h:form>
<h:inputText
id = "HIT_LongTestHIT"
value = "#{saisieController.htagLongInputTexts['HIT_LongTestHIT'].value}"
rendered = "#{saisieController.htagLongInputTexts['HIT_LongTestHIT'].rendered}"
required = "#{saisieController.htagLongInputTexts['HIT_LongTestHIT'].required}"
/>
<h:message for = "HIT_LongTestHIT" styleClass = "error-text" />
<h:commandButton value = "submit" action = "#{saisieController.doNothing()}" />
<h:outputText value = "#{saisieController.htagLongInputTexts['HIT_LongTestHIT'].value}" />
</h:form>
Is there some trick to make JSF validation work without having to explicitly define a custom Validator for each h:inputText?
No, that's not possible. Due to type erasure the generic type information is not available during runtime. JSF/EL has no idea that T is actually a Long, let alone that there's actually some T. You really need to explicitly specify a converter which is in this particular example the LongConverter with a converter ID of javax.faces.Long.
<h:inputText ... converter="javax.faces.Long">
or, dynamically so you want
<h:inputText ...>
<f:converter converterId="#{saisieController.htagLongInputTexts['HIT_LongTestHIT'].converterId}" />
</h:inputText>
You can find an overview of all available standard JSF converters in the javax.faces.convert package summary. You can find out their converter IDs by navigating to the Constant field values link on the public constant CONVERTER_ID.

Resources