ZK: loading value/text into selectbox - zk

I have a selectbox and want to load the value and text into the template, similar to an HTML dropdown box. I am using ZK framework with Java on the back end.
<selectbox id="buListbox" model="${$composer.buModel}" >
<template name="model">
<label value="${each}" />
</template>
</selectbox>

When using ZK, you don't need the value to identify the selected object like in HTML.
When using the MVC pattern, binding a model via model attribute, the selected item is also stored in that model and can be retrieved in java via model.getSelection().
Furthermore, a model is not restricted to lists of String, but it can hold any object type you want. In the template section, you can display any property of that object. Then the properties' toString() method is used to get the value which is displayed. This also applies to ${each} itself.
Example:
Assuming your model is a ListModelList of type ValueType:
public class ValueType {
private String value;
private String text;
public ValueType(String value, String text) {
this.value=value;
this.text=text;
}
public String getText() {
return this.text;
}
public String getValue() {
return this.value;
}
}
private ListModelList<ValueType> typesModel;
public ListModelList<ValueType> getTypesModel() {
return typesModel;
}
You than can use the selectbox's model/template to display it's text property:
<selectbox id="typesSelectbox" model="${$composer.typesModel}">
<template name="model">
${each.text}
</template>
</selectbox>
In java, you then get the selected item via typeModel.getSelection() .
Here you can find a working ZKFiddle example.

Related

Spring form path with multiple model attributes with the same property name

The problem is that I have a spring form and 2 #ModelAttribute params with the same properties in my controller. The 'commandName' parameter of the the form is set to one of my modelAttributes names. I was surprised that the maps the property not only to the model attribute specified with 'commandName', but also to the second one.
I haven't found the exact solution here, except the similar to mine: Spring-form multiple forms with same model atribute name properties
But in my case I can't see any 'strange things', I have one form, one Model attribute to bind this form, and one model attribute to have accsess to controller scoped #SessionAttribute.
I've also tried to use form's 'modelAttribute' parameter (Actually I can't see any difference between them), but it didn't help.
My code example:
view.jsp:
<form:form name="form" action="/myAction" method="POST" commandName="model1">
<form:input path="property"/>
....
<input type="submit" value="Submit"/>
</form:form>
Controller.java
#SessionAttributes("model2")
class Controller {
#RequestMapping(value = "/myAction", method = POST)
public String submitEditSite(final #ModelAttribute(value = "model1") Model1 model1,
final #ModelAttribute(value = "model2") Model2 model2) {
....
return "redirect:/home";
}
}
Model1.java Model2.java
class Model1 {
private String property;
}
class Model2 {
private String property;
}
Where am I wrong?
If I understand you correctly you want to prevent the setting of any property on model2, right?
Then this should do:
#InitBinder("model2")
public void initBinder(WebDataBinder binder) {
binder.setDisallowedFields("*");
}

Number format in ZKOSS label

is there any way to format number in ZK label component that looks like like
<label value="${each.value}" /> ? Values are doubles and I want to separate thousands etc... I know that doublebox for example has format property but what if I just want to display number as label? Thanks for any help.
Feature Request
First of all I have opened a feature request for this on ZK's tracking system you can find it here. Please follow this if you want updates.
Ways of implementing
There are in fact ways of implementing this depending on what pattern & techniques you are using.
MVC & EL
You can create an EL function which will do the formatting for you in your ZUL file. First of all create a class such as this:
public class FormatElNumber {
public static String formatStock(double stock) {
final NumberFormat nf = NumberFormat.getInstance();
nf.setMaximumFractionDigits(2);
return nf.format(stock);
}
}
This will output numbers with two decimal places. Secondly you need to add this to the top of your zul file:
<?xel-method prefix="c" name="formatStock" class="demo.grid.hierarchy.FormatElNumber"
signature="java.lang.String formatStock(double)"?>
Then when you have a label you can do as follows:
<label style="color:red;" value="${c:formatStock(each.averageHigh)}" />
More infomration on this technique is available here.
MVVM
The MVVM is actually easier to implement, you create what's called a Converter, for example (please note this class is untested, but you get the idea).
public class NumberFormatConverter implements Converter {
#Override
public Object coerceToBean(Object val, Component comp, BindContext ctx) {
return null;
}
#Override
public Object coerceToUi(Object val, Component comp, BindContext ctx) {
if(!(val instanceof Integer)) {
throw new IllegalArgumentException("The argument must be a number!");
}
final Object tmp = ctx.getConverterArg("length");
int length = 0;
if(tmp instanceof Integer) {
length = (Integer)tmp;
}
final NumberFormat nf = NumberFormat.getInstance();
nf.setMaximumFractionDigits(length);
return nf.format(val);
}
}
Then all you do in your zul file is specify you would like to use a converter on the value, for example:
<label value="#load(vm.message) #converter(vm.myConverter)"/>
For more information on this technique you can refer here.
From the docs: http://books.zkoss.org/wiki/ZUML_Reference/EL_Expressions/Core_Methods/formatNumber
<?taglib uri="http://www.zkoss.org/dsp/web/core" prefix="c"?>
<zk>
<label value="${c:formatNumber(2332315231, '$ ###,###,###.00')}" />
</zk>

Aliased Form Names with HtmlHelpers

tl;dr
How do I modify form element names that are generated to use aliased names that come from ModelMetadata.AdditionalValues instead? Preferably with a custom (or not custom) html helper.
I have a search view model with some custom object properties. Each property goes two more properties deep in my view. I also use editor templates in my view. My form ends up with elements like the following:
HTML
<input name="Field1.Field.Id" id="..." type="..." />
<input name="Field1.Field.Label" id="..." type="..." />
<input name="Field2.Field.Id" id="..." type="..." />
<input name="Field2.Field.Label" id="..." type="..." />
Search.spark (.aspx)
${ Html.EditorFor(m => m.Field1) } // Field1 is of type SearchField
${ Html.EditorFor(m => m.Field2) } // Field2 is of type SearchField
etc...
SearchField.spark (.ascx)
<viewdata model="SearchField" />
// *snip*
${ Html.EditorFor(m => m.Field) } // Field is of type SpecialField
SpecialField.spark (.ascx)
<viewdata model="SpecialField" />
${ Html.HiddenFor(m => m.Id) } // Id is of type int?
${ Html.TextBoxFor(m => m.Label } // Label is of type string
Because the form uses GET, I end up with very long query strings. What I'd like to do is provide aliases for the properties. I already have an attribute, a model metadata provider to populate AdditionalValues and a custom model binder to take care of the aliases, but I'm stuck on generating the proper html.
I thought I'd just write a custom html helper, but got stuck pretty fast.
public static MvcHtmlString HiddenForA<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression)
{
var metadata = ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData);
if (metadata.AdditionalValues.ContainsKey("Alias"))
{
string alias = (string)metadata.AdditionalValues["Alias"];
// what to do now?
}
}
You have the alias that you want to use, the value is available from metadata.Model then all you need to do is generate your HTML. You could use a tag builder, string.format or what ever you prefer, for the sake of readability I'll use string.format and have the properties in ' rather than ".
public static MvcHtmlString HiddenForA<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression)
{
var metadata = ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData);
if (metadata.AdditionalValues.ContainsKey("Alias"))
{
string alias = (string)metadata.AdditionalValues["Alias"];
string value = Convert.ToString(value, metadata.Model);
string html = string.Format(#"<input type='hidden' name='{0}' value='{1}' >", alias, value);
return MvcHtmlString.Create(html);
}
}
However, this will only handle the issue of your long URLs, this will mean that your model binding will not properly bind these on your action. To do this you'll need to either create a custom model binder or have your action take a viewmodel with properties of these names then either create a new object of your desired model and fill in its properties or use the viewmodel as is.

XStream different alias for same class (Map.class) for different fields

I am using XStream to convert a Java class which has fields of the type java.util.Map. I have a converter for java.util.Map which displays the key of the Map as an xml element and the value of the map as the value for the xml element. I have registered the converter using registerConverter method. When I perform the marshalling, I get the following output.
<cart account_id="123" shift_id="456" account_postings_id="641">
<supervisor_id>555</supervisor_id>
<payments>
<map sequence="1">
<amount>123.45</amount>
<billing_method>12345</billing_method>
<form>card</form>
<delivery_mode>Q</delivery_mode>
</map>
<map sequence="2">
<amount>123.45</amount>
<person_id>2333</person_id>
<form>cash</form>
<delivery_mode>Q</delivery_mode>
</map>
</payments>
<items>
<map sequence="3">
<amount>1.00</amount>
<type>pay_toll</type>
<toll_id>1234</toll_id>
</map>
</items>
</cart>
Instead of the map tags appearing, I would want to use different tags based on the field name in the class. For example, the Payments list will have tag name payment and the Items list will have a tag name item for each Map element.
How do we dynamically set alias based on field in the same class?
-Anand
I used XStream to create atom feed reports. Entries in the contents can be of different object classes and I wanted to use the class name dynamically. Here is my solution. I created a ObjectContentConverter and passed the XStream on, and then use xstream.aliasField() with getClass().getSimpleName().
private class ObjectContentConverter implements Converter {
XStream xStream;
private ObjectContentConverter(XStream xStream) {
this.xStream = xStream;
}
#Override
public void marshal(Object source, HierarchicalStreamWriter writer, MarshallingContext context) {
String className = WordUtils.uncapitalize(source.getClass().getSimpleName());
xStream.aliasField(className, Content.class, "objectContent");
xStream.marshal(source, writer);
}
#Override
public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) {
return null; //To change body of implemented methods use File | Settings | File Templates.
}
#Override
public boolean canConvert(Class type) {
return true; //To change body of implemented methods use File | Settings | File Templates.
}
}
xStream.registerLocalConverter(Content.class, "objectContent", new ObjectContentConverter(xStream));

Default value of form object's field

I have a form where depending on the select state ([checkboxOn/checkboxOff]) a check box should appear or not. When the check box is displayed it should be checked by default.
How to handle that situation, taking into account that
when select is in 'checkboxOff'
state, I would have
MyFormObject.isCheckboxOn == false;
when select is in 'checkboxOn' state,
the value should be as in request?
All of this should work also on Validation errors postback, and when new form is shown and on valid form case.
Also, I would like to avoid using JavaScript on client side.
Here is some code, which needed to be extended:
class MyFormObject {
private String selectValue;
private boolean isCheckboxOn;
...
}
and two Spring controller's method:
#RequestMapping(method = RequestMethod.GET)
public ModelAndView showForm() {
return new ModelAndView('/form.jsp', 'command', new MyFormObject());
}
#RequestMapping(method = RequestMethod.POST)
public ModelAndView processSubmit(BindingResult result, MyFormObject command) {
if (result.hasErrors()) {
return new ModelAndView('/form', 'command', command);
}
...
return new ModelAndView('redirect:/success.jsp');
}
If you want to exclude the checkbox field from your view when isCheckboxOn is set to false, you can surround the checkbox field with a c:if in your JSP:
<c:if test="isCheckboxOn">
(your checkbox <input> tag goes here)
</c:if>
To check the box by default, you can use Spring's form tag library to bind directly to the checkbox field in the model, or you can use another c:if to add a checked to your <input> tag.