struts 2 checkboxlist doesn't save values - struts2

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"/>

Related

Struts 2 <s:if> tag issue

I have a pojo class as below:
public class HelpBean {
private String title;
public String getTitle(){
return title;
}
public void setTitle(String title){
this.title = title;
}
}
An action class as follows:
public class MyAction implements Action {
private HelpBean bean;
//other fields
public HelpBean getBean() {
return bean;
}
public void setBean(HelpBean bean) {
this.bean = bean;
}
}
In my jsp page, I am trying to check for below condition:
<s:if test=%{!bean.getTitle().trim().contains("bad_title")}>
//execute good code
</s:if>
I also tried below, but this one also doesn't work:
<s:if test=%{bean.getTitle().trim().contains("bad_title") == false}>
//execute good code
</s:if>
Not sharing the xml, as routing and everything works fine. I only have problem checking if my title does not contains "bad_title".
There are many different titles so I can't compare for contains() here, hence checking for !contains()
What am I missing here? Can someone pls point out. Before down voting, please explain if something is missed.
IMO the check should be
<s:if test="%{!bean.getTitle().trim().contains('bad_title')}">

JSF Navigation outcome constants

I wonder if i can replace "success" with a Constants value from a JSF library.
Backing Bean Method:
#Override
public String save() {
LOG.info("Called");
return "success";
}
For your issue, you'll find Omnifaces' <o:importConstants /> so useful (that's what I use in my own projects). That way you can import your constants file in your JSF page (I use my master page template for that).
<o:importConstants
type="com.mycompany.NavigationResults" />
This way you can access your NavigationResults values both from Java code and JSF tags (EL scope).
public abstract class NavigationResults {
public static final String SUCCESS = "success";
public static final String HOME = "home";
}
Use it in your managed beans:
public String save() {
LOG.info("Called");
return NavigationResults.SUCCESS;
}
In your buttons or links:
<h:button value="Go home" outcome="#{NavigationResults.HOME}" />

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.

update a list of value in textfield in struts2

I am facing a problem in Struts2, In my sample application I have Array of objects (Say person name) , I need to display these names as a editable text fields , I am using Iterators for this , I am successful in diaplying, But When I cange the same value and submit the form. I am getting the entire array which holds null value.
Say for Exaple in my form bean I have a property
Name [] names;
In my JSP I have the iterator as
If there are 3 names then I can get these names on the UI if I can initialized them with some dummy value but when I edit and sumbit, then "names" array is not getting updated. Please help me in this regard
In Struts2, when you need to repopulate the list of bean items, you need to refer them through indices. Please refer the example below:
Bean class:
public class Person {
private int id;
private String name;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Action class:
public class PersonAction extends ActionSupport {
private List<Person> persons;
public List<Person> getPersons() {
return persons;
}
public void setPersons(List<Person> persons) {
this.persons = persons;
}
//Initial Load method
#Override
public String execute() {
persons = new ArrayList<Person>();
int alpha = 65;
for(int i = 0; i < 3 ; i++) {
Person person = new Person();
person.setId(i);
person.setName(String.valueOf((char)alpha++));
persons.add(person);
}
return SUCCESS;
}
//Function that handles the form submit
public String updatePerson() {
for(Person person : persons) {
System.out.println(person.getId() + ":" + person.getName());
}
return SUCCESS;
}
}
Page:
<s:form action="doUpdate">
<s:iterator value="persons" status="stat" var="person">
<s:textfield value="%{#person.name}" name="persons[%{#stat.count}].name"/><br/>
</s:iterator>
<s:submit value="Submit"/>
</s:form>
When you submit the above form, the url would look like doUpdate?persons[0].name=A1&persons[1].name=B1&persons[2].name=C1. Similarly if you need to update id of the first person object, you will append persons[0].id=3 to the url using form. In the <s:textfield value="%{#person.name}" name="persons[%{#stat.count}].name"/>, you tell that the predefined value is person's name, for each object. The name attribute is to set the rendered html input element; the name which will be referenced in the url when the form is submitted. You will get a clear idea if you look into the generated html.
Use the iterator tag of struts2, if list is not empty then use :
<s:iterator value="names" status="namesStatus">
<s:property/><br/>
</s:iterator>

JSF 2.0 + Primefaces richtext editor

<p:editor value="#{editorBean.value}" widgetVar="editor" width="686"
height="390" language="en" align="center">
</p:editor>
Following is my rich-text editor bean picked up from primefaces
#ManagedBean(name = "editorBean")
#SessionScoped
public class EditorBean {
private static final String MANAGED_BEAN_NAME = "editorBean";
private String value;
public static EditorBean getCurrentInstance() {
return (EditorBean) FacesContext.getCurrentInstance()
.getExternalContext().getRequestMap().get(MANAGED_BEAN_NAME);
}
public void setValue(String value) {
this.value = value;
}
public String getValue() {
return value;
}
}
Apart from this I have another bean say A. I have a method inside A that populates a HTML table. What I want is when the user opens the editor, it should be pre-populated with that HTML table data and of course the changes should get reflected into (String: value). Therefore, you can say that I am trying to tie up both the values together. I think it needs to be done with DI but somehow its not working. If someone can guide or quote an example, it would be really helpful.
One way to do it is rewrite your getValue() method to pick up the value from bean A.
And yes, the reference to your A bean should come from DI:
//injecting a reference to A
#ManagedPropery(value="#{A}") //or whatever is the name of your bean
private A beanA;
public void setBeanA(A beanA) {
this.beanA = beanA;
}
Or, with CDI, just:
#Inject private A beanA
Finally, your getValue method
public String getValue() {
return beanA.getValue()
}

Resources