JSF: store form data to a map - forms

I'm developing a JSF page that contains a form with a lot of input string values. I don't want to create a corresponding field in the bean for each input . Is it possible to use a map instead.
Here's what i want my form input element to look like:
<h:inputText value='#{myBean.data["key"]}' /> // or something like this
And the bean contains the map as follows:
class myBean {
Map data;
...
}

What should getter and setter for map operation look like if I create such a code?
Nothing special. Just a standard getter as you should always use for model properties.
public Map<String, Object> getData() {
return data;
}
A setter is not mandatory as it won't be used anyway. EL will use map's own put() method for that. You only need to make sure that the map is already precreated in bean's (post)constructor, JSF/EL won't do that for you.
public MyBean() {
data = new HashMap<String, Object>();
}

Related

How to Set View Model Class Programmatically when Creating Components

I am experimenting with ZK framework and look for ways to load a zul-defined view programmatically via a utility class that uses Executions to load a view definition programmatically.
I do however fail to find a way to set the view model class programmatically, which would be very convenient and would allow some very simple way of reusing a view with different view model classes.
I.e. the code to load the view looks like this:
public static Component loadComponent(Class<?> modelClz, String zulFile, Component parent, Map<String,Object> params) {
Execution exec = Executions.getCurrent();
PageDefinition page = exec.getPageDefinitionDirectly(
new InputStreamReader(modelClz.getResourceAsStream(zulFile)),
null
);
return exec.createComponents(
page,
// no (previous parent)
parent,
params
);
}
I thought about "forcing" the view model by setting annotations programmatically on the top component info from the page definition, like so:
public static Component loadComponent(Class<?> modelClz, String zulFile, Component parent, Map<String,Object> params) {
Execution exec = Executions.getCurrent();
PageDefinition page = exec.getPageDefinitionDirectly(
new InputStreamReader(modelClz.getResourceAsStream(zulFile)),
null
);
if (!page.getChildren().isEmpty()) {
ComponentInfo top = (ComponentInfo) page.getChildren().get(0);
AnnotationMap annotationMap = top.getAnnotationMap();
String viewModel = "viewModel";
if (annotationMap==null || !annotationMap.getAnnotatedProperties().contains(viewModel)) {
// no view model set on top declaration,
// force ours
Map<String,String[]> id = new HashMap<>();
id.put(null, new String[]{"vm"});
top.addAnnotation("viewModel","id",id, null);
Map<String,String[]> init = new HashMap<>();
init.put(null, new String[]{String.format("%s", modelClz.getName())});
top.addAnnotation("viewModel","init",init, null);
top.enableBindingAnnotation();
}
}
return exec.createComponents(
page,
// no (previous parent)
parent,
params
);
}
This did not work however. Maybe it was too late in the process. Or there is some really simple way of doing this but I missed it. Or maybe I should "apply" some BindComposer, but I am not sure how to do that.
Any helpful idea would be great!
Just to make sure I've understood:
You have some zul fragment (such as a reusable UI structure)
This fragment uses the MVVM pattern, but you want to choose a viewModel object when you are instantiating that fragment, instead of declaring a class name in the zul viewModel="#id()#init()" attribute
You want to initialize that fragment from a java class that has access to the UI (using Execution#createComponents to load the fragment and attach them to the page)
Does that sound correct?
If that's the case, you can make this way simpler
The attribute can be written as viewModel="#id('yourVmId')#init(aReferenceToAnAlreadyInstantiatedObject)".
Important note here: Notice that I have NOT put quotes around the object in the #init declaration. I'm passing an actual object, not a string containing a reference to a class to be instantiated.
When you invoke execution.createComponents() you may pass a map<String, Object> of arguments to the created page. You can then use the name of the relevant passed object when you create bind the VM.
have a look at this fiddle (bit rough, but it should make sense): https://zkfiddle.org/sample/2jij246/4-Passing-an-object-through-createComponents-as-VM#source-2
HashMap<String, Object> args = new HashMap<String, Object>();
args.put("passedViewModel", new GenericVmClass("some value in the passed VM here"));
Executions.createComponents("./fragment.zul", e.getTarget().getPage(),null, args);
FYI if you are using ZK shadow-elements, you can also pass that object to the fragment from an apply with a source in pure MVVM pattern.
The <apply> shadow element for example can pass objects to the created content with a variable name, and you can use that variable name when initializing the VM.
Regarding BindComposer:
You need to instantiate BindComposer up to ZK 7.X
In ZK 8.X and above, BindComposer will be instantiated automatically when you use the viewModel="..." attribute on a ZK component.

Is there a JPA annotation equivalent of jackson's #JsonAnyGetter/#JsonAnySetter?

With jackson, I can use #JsonAnyGetter and #JsonAnySetter to serialize/deserialize a Map<String, Object> into extra fields of a json object. Is there a JPA annotation that will do similar things with extra db column values being get/set from/into a member Map?
Specifically, I'd like to use jooq's .fetchInto(Pojo.class) to hydrate a java object. I can manually use .fetch(RecordMapper<Record, Pojo>) to get the results I want by hydrating the Map member from the Record fields manually, but wondering if there's a more automatic way of doing this. Pojo code could look something like the following (use lombok's #Data to make it concise):
#Data
public class Pojo {
#Column("field1")
private int field1;
#Column("field2")
private String field2;
#JsonAnyGetter // works for json serialization,
#JsonAnySetter // is there an equivalent for JPA?
private Map<String, Object> extraFields;
}
You can register a RecordMapperProvider with your jOOQ configuration in order to override how various methods, including fetchInto(Class) apply mapping:
https://www.jooq.org/doc/3.11/manual/sql-execution/fetching/pojos-with-recordmapper-provider/

mapStruct: map list to other list?

I have a list List<Payment> which I'd like to map to another list List<PaymentPlan>. These types look like this:
public class Payment {
#XmlElement(name = "Installment")
#JsonProperty("Installment")
private List<Installment> installments = new ArrayList<>();
#XmlElement(name = "OriginalAmount")
#JsonProperty("OriginalAmount")
private BigDecimal originalAmount;
//getters setters, more attributes
}
and....
public class PaymentPlan {
//(Installment in different package)
private List<Installment> installments;
#XmlElement(name = "OriginalAmount")
#JsonProperty("OriginalAmount")
private BigDecimal originalAmount;
//getters setters, more attributes
}
I expect that something like this is working...
#Mappings({
#Mapping(//other mappings...),
#Mapping(source = "payments", target = "paymentInformation.paymentPlans")
})
ResultResponse originalResponseToResultResponse(OrigResponse originalResponse);
...but I get:
Can't map property java.util.List<Payment> to java.util.List<PaymentPlan>.
Consider to declare/implement a mapping method java.util.List<PaymentPlan> map(java.util.List<Payment> value);
I don't know how to apply this information. First I though I need to declare some extra mapping (in the same mapper class) for the lists, so MapStruct knows how to map each field of the List types like this:
#Mappings({
#Mapping(source = "payment.originalAmount", target = "paymentInformation.paymentPlan.originalAmount")
})
List<PaymentPlan> paymentToPaymentPlan(List<Payment> payment);
...but I get error messages like
The type of parameter "payment" has no property named "originalAmount".
Obviously I do something completely wrong, since it sound like it does not even recognize the types of the List.
How can I basically map from one List to another similar List? Obviously I somehow need to combine different mapping strategies.
btw: I know how to do it with expression mapping, like...
#Mapping(target = "paymentPlans",expression="java(Helper.mapManually(payments))")
but I guess MapStruct can handle this by iself.
I presume you are using version 1.1.0.Final. Your extra mapping is correct, the only difference is that you need to define a mapping without the lists MapStruct will then use that to do the mapping (the example message is a bit misleading for collections).
PaymentPlan paymentToPaymentPlan(Payment payment);
You don't even need the #Mappings as they would be automatically mapped. You might also need to define methods for the Instalment (as they are in different packages).
If you switch to 1.2.0.CR2 then MapStruct can automatically generate the methods for you.

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("*");
}

Read DataAnnotations from a collection of models in an MCV2 view

In my MVC2 AdminArea I'd like to create an overview table for each of my domain models.
I am using DataAnnotations like the following for the properties of those domain model objects:
[DisplayName("MyPropertyName")]
public string Name { get; set; }
Now my question is: How can I access the DisplayName Attribute if my view receives a collection of my domain models? I need this to build the table headers which are defined outside of the usual
<% foreach (var item in Model) { %>
loop. Inside this loop I can write
<%: Html.LabelFor(c => item.Name) %>
but is there any way to access this information using the collection of items instead of a concrete instance?
Thanks in advance!
There is a ModelMetaData class that has a static method called FromLambdaExpression. If you call it and pass in your property, along with your ViewData, it will return an instance of ModelMetaData. That class has a DisplayName property that should give you what you need. You can also get other meta data information from this object.
For example, you can create an empty ViewDataDictionary object to get this information. It can be empty because the ModelMetaData doesn't actually use the instance, it just needs the generic class to define the type being used.
//This would typically be just your view model data.
ViewDataDictionary<IEnumerable<Person>> data = new ViewDataDictionary<IEnumerable<Person>>();
ModelMetadata result = ModelMetadata.FromLambdaExpression(p => p.First().Name, data);
string displayName = result.DisplayName;
The First() method call doesn't break even if you have no actual Person object because the lambda is simply trying to find the property you want the meta data about. Similarly, you could d this for a single Person object:
//This would typically be just your view model data.
ViewDataDictionary<Person> data = new ViewDataDictionary<Person>();
ModelMetadata result = ModelMetadata.FromLambdaExpression(p => p.Name, data);
You could clean this up significantly with a helper or extension method, but this should put you on the right path.
Alright, I followed sgriffinusa's advise (thanks again!) and created a strongly typed HtmlHelper:
public static MvcHtmlString MetaDisplayName<TModel, TProperty>(this HtmlHelper<TModel> helper, Expression<Func<TModel, TProperty>> expression) where TModel : class
{
ModelMetadata metadata = ModelMetadata.FromLambdaExpression(expression, helper.ViewData);
return MvcHtmlString.Create(metadata.GetDisplayName());
}
Of course TModel still is a collection of domain models like stated in my inital question but we can call the helper in the view like this:
<%: Html.MetaDisplayName(p => p.First().Name) %>