Aliased Form Names with HtmlHelpers - forms

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.

Related

ZK: loading value/text into selectbox

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.

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>

How to get the form name for a property

In Razor I know that if you write
#Html.HiddenFor(x => x.PropertyX.PropertyY)
it will generate HTML like:
<input type="hidden" name="PropertyX.PropertyY" value="...">
And (especially) if this was in an Editor Template it might generate this HTML:
<input type="hidden" name="ParentProperty[12].PropertyX.PropertyY" value="...">
How do I get a name for an arbitrary property? I'm assuming there must be some way to do this using MVC infrastructure (perhaps some method or class?)
You could write a custom helper for that:
public static class NameExtensions
{
public static string NameFor<TModel, TProperty>(
this HtmlHelper<TModel> html,
Expression<Func<TModel, TProperty>> expression
)
{
var partialName = ExpressionHelper.GetExpressionText(expression);
return html
.ViewContext
.ViewData
.TemplateInfo
// You could do the same with GetFullHtmlFieldId
// if you were interested in the id of the element
.GetFullHtmlFieldName(partialName);
}
}
and then:
#Html.NameFor(x => x.PropertyX.PropertyY)

ASP.Net MVC 2 - ModelBinding and Dictionary<int, int>

In my interface I have a list of text boxes, something like this :
http://screencast.com/t/YjIxNjUyNmU
The number of textboxes is unknown as each of them is associated with a Template.
In my page, the goal is to associate a number to some of those templates.
Here is a sample HTML code :
<% // loop on the templates
foreach(ITemplate template in templates)
{
// get the content from the input dictionary
int val;
content.TryGetValue(template.Id, out val);
// convert it as a string
string value = ((val > 0) ? val.ToString() : string.Empty);
// compute the element name/id (for dictionary binding)
string id = ??????????
string name = ??????????????
%>
<label for="<%= name %>"><%= template.Name %></label>
<input type="text" id="<%= id %>" name="<%= name %>" value="<%= value %>" />
<br />
<% }
%>
What I expect, in my controller, is to get a IDictionary where the first int is the template ID , and the other is the count given by the user.
Here is what I want :
public ActionResult Save(int? id, Dictionary<int, int> countByTemplate)
I tried a lot of things but nothing worked. I tried to read the sources but it's a maze, and I'm getting a headhache trying to get information about model binding.
Questions :
is there a good ressource on how the modelbinding works ?
I'd like someting exhaustive, I'm tired of the 84093043 blogs that talk about a given specific example.
how can I build my HTML, using to get a IDictionary (or even a IDictionary in my controller's action ?
Thanks a lot for your help
Information on how to write your input elements for binding to arrays, dictionaries, and other collections can be found at http://www.hanselman.com/blog/ASPNETWireFormatForModelBindingToArraysListsCollectionsDictionaries.aspx.
Ok... Thanks to Levi I was able to get on a solution.
Not the cleaner one, but it works.
The HTML should be written this way:
<%
int counter = 0;
// loop on the templates
foreach(ITemplate template in templates)
{
// get the value as text
int val;
content.TryGetValue(template.Id, out val);
var value = ((val > 0) ? val.ToString() : string.Empty);
// compute the element name (for dictionary binding)
string id = "cbts_{0}".FormatMe(template.Id);
string dictKey = "cbts[{0}].Key".FormatMe(counter);
string dictValue = "cbts[{0}].Value".FormatMe(counter++);
%>
<input type="hidden" name="<%= dictKey %>" value="<%= template.Id %>" />
<input type="text" id="<%= id %>" name="<%= dictValue %>" value="<%= value %>" />
<label for="<%= id %>"><%= template.Name %></label>
<br />
<% }
%>
I had to add a hidden field to store the value.
I introduced a 'fake' counter just to loop over the dictionary the way ASP.Net MVC wants it.
As a result I got a dictionary filled with my values and '0' when the textbox is empty.
Another problem appeared: the ModelState was considered not valid because "a value is required". I don't want my values to be required, but looking at the modelbinder code, I did not found a way to tell the binder that a value is NOT required.
So I tricked the ModelState in my controller, removing all error, like this:
public ActionResult Save(int? id, Dictionary<int, int> cbts)
{
// clear all errors from the modelstate
foreach(var value in this.ModelState.Values)
value.Errors.Clear();
Well... I effectively got a solution, but the HTML is kind of ugly now, and counterintuitive (using an index to loop over a non-indexed collection ??).
And I need to trick each time I'll use this kind of binding to have it all work properly.
So I will now open a new post to make dictionary binder something better.
Here it is: ASP.Net MVC 2 - better ModelBinding for Dictionary<int, int>
EDIT - There is a cleaner solution, thanks to Pavel Chuchuva.
In the controller code, use a nullable int as value for the dictionary.
A bit more code to add, but much cleaner.
public ActionResult Save(int? id, Dictionary<int, int?> cbts)
{
// this is our final dictionary<int, int>
Dictionary<int, int> cbtsFinal = new Dictionary<int, int>();
// loop on the dicitonary with nullable values
foreach(var key in cbts.Keys)
{
// if we have a value
if(cbts[key].HasValue)
// then put it in the final dictionary
cbtsFinal.Add(key, cbts[key].Value);
}