binding map in tapestry 5 - java

in my action class i want to have a map of strings. and in my tml i want to access this map with textfield. something like
<t:form>
<t:textfield value="myMap['key1']"/>
<t:textfield value="myMap['key2']"/>
...
i don't insist on syntax, but is there anything like this currently in tapestry? if not, what do i need to create such conversion in the most easy way? type coercing? custom components? i'm starting to learn tapestry so feel free to be verbose :)

Another option is to bind your own tml prefix. There is an example of binding prefixes here.
We wrote our own prefix for map which allows us to get the value in the tml like this:
${map:myMap.key1}

ok, i figured it out. i did a simple component MapField:
#Parameter(required=true)
Map<String, String> map;
#Parameter(required=true, allowNull=false, defaultPrefix = BindingConstants.LITERAL)
String key;
public String getMapValue() {
return map.get(key);
}
public void setMapValue(String value) {
map.put(key, value);
}
tml:
<html xmlns:t="http://tapestry.apache.org/schema/tapestry_5_3.xsd">
<t:textfield value="mapValue"/>
</html>
that's it. now we can use it in other tml:
<t:mapField key="existingOrNot" t:map="myMap"/>
and in a page we need only myMap as a property:
#Property #Persist Map<String, String> myMap;
probably there are more things to be done, like passing all additional html parameters to the textfield, etc

you will need to create an accessor method in your java class.
the most straightforward way would be to add a single method:
getMapValue(String key){...}
you can then change your tml to use
value="getMapValue('key1')"

You should be able to loop through the key set like this:
<form t:type="Form">
<t:Loop t:source="myMap.keySet()" t:value="currentKey">
<input type="text" t:type="Textfield" t:value="currentValue"/>
</t:Loop>
</form>
You'll have to add some code in the class file that stores the current map key and gives access to the current value:
#Property
private Object currentKey;
#Persist
#Property
private Map<String,String> myMap;
public String getCurrentValue() {
return this.myMap.get(this.currentKey);
}
public void setCurrentValue(final String currentValue) {
this.myMap.put(this.currentKey, currentValue);
}
(This answer is adapted from one of my earlier answers.)

Related

How do I convert JSON to Obj for InputText

I have list of json List<String> and each string have json like this {"id":"22","name":"Name","order":"1"} And I want to fetch it to list of input box then when I save it I
I want to take all changing and convert them to JSON
#ManagedBean
#ViewScoped
public class LiveStreamController extends ProductController implements Serializable {
private static final long serialVersionUID = 5037909267669512508L;
private static final Logger LOGGER = LoggerFactory.getLogger(LiveStreamController.class);
private LiveStream liveStream;
....
}
public class LiveStream extends Product implements Serializable {
private List<String> jsons = new ArrayList<>();
...
}
and I wanna read it
<c:forEach items="#{liveStreamController.liveStream.jsons}"
var="json ">
<h:outputFormat value="#{json.name}" />
<p:inputText value="#{json.order}" />
</c:forEach>
Effectively you are not asking a JSF question but an EL question since the #{json.name} expression is just that... Expression Language.
About the 'issue'...
If the value of the var="json" is a String, which it in your case is, it will be resolved by the basic resolvers that will for sure know about a String. An attempt will be made to call the property name on the string via a getName() which obviously does not exist, resulting in a Property 'name' not found on type java.lang.String. All of this is also explained in Property 'someproperty' not found on type java.lang.String. Therefor using it like in your example will not work
About the solution(s) for reading...
There are basically 3 solutions for reading the JSON and displaying it
New String EL Resolver
You could create a custom EL resolver and put it first in the order of resolvers so that for every string that needs to be resolved checks if it is effectively JSON (minimally parsing it) and if you think it IS JSON, then parse the JSON fully and read/interpret the EL and try to apply it to the JSON object. All this is going to be rather expensive as far as I can see (but BalusC might have a different idea)
New JSON EL Resolver
The second, a little better solution, is converting the String to a JSON object of you choice (there are many in Java, including a standardized one, 'JSON-P', since Java EE 7 and a newer version for Java EE 8). There is as far as I know no default included EL resolver that knows how to handle these in EL, but examples exist for the non standardized EL formats No idea how this would perform though, testing is knowing.
Existing EL Resolver
The third option is to convert the JSON to Normally strongly typed objects and then have the normal existing resolvers act on them. This can be Default existing java types like Map, List, String and other normal values, but it could even be more strongly typed like Person , Order, Converting JSON Strings to strongly typed object was a feature that existed in the non-standardized JSON libraries but not in default Java(EE) until Java EE 8, JSON-B. This has an additional advantage that code completion works and validations work in an IDE
About the solution(s) for writing...
Since you do seem to wanting to write results back to the JSON String (you have an input in your example), the first solution for reading is very hard to extend to writing, and if possible, it would totally break the advantage you seem to want to get of not writing code to be able to use this. The second and third solution both might work but the third has the advantage that input validations can be implemented, like bean validation and more.
create a class:
public class JsonWrapper {
JsonObject o;
public JsonWrapper(String s) {
this(new JsonParser().parse(s).getAsJsonObject());
}
public JsonWrapper(JsonObject o) {
this.o = o;
}
public String getJsonText() {
return o.getAsString();
}
public DataWrapper get(String field) {
return new DataWrapper(field);
}
public class DataWrapper {
String field;
public DataWrapper() {
}
public DataWrapper(String field) {
this.field = field;
}
public String getData() {
return o.get(field).getAsString();
}
public void setData(String s) {
o.add(field, new JsonPrimitive(s));
}
}
}
convert your live stream to object like this:
List<JsonWrapper> jwList = s.stream().map(s1 -> new JsonWrapper(s1)).collect(Collectors.toList());
setter & getter
Use it in xhtml
<c:forEach items="#{liveStreamController.liveStream.jwList}" var="jw">
<h:outputFormat value="#{jw.get('name').data}" />
<p:inputText value="#{jw.get('order').data}" />
</c:forEach>
Use modified data with:
List<String> jsonList = jw.stream().map(JsonWrapper::getJsonText).collect(Collectors.toList());

java play framework access HashMap in template

I have a tag like this
public class AcsTag {
public static String getStyles(String paramter) {
return hashMap<String, String>()
}
}
}
I can access this tag in template (scala.html) like this
#import com.twago.fms.shared.ui.AcsTag
#AcsTag.getStyles(paramter)
getStyles method return a HasMap, I want to store that hashMap in a variable and then later get value from this hash map by key. I do want to iterate over map . I specifically want to access values by key.
following code i tried but always give error
"map not defined"
map =#{AcsTag.getStyles(paratmeter))}
#{map.get("themeColor")}
error "map not defined"
To declare a variable, you have to set at the top of your template:
#yourValue = #{yourExpression}
So, to declare a map value, you should do:
#map = #{AcsTag.getStyles(parameter)}
Then you'll be able to use yout map value anywhere in your template. Eg:
<div class="#map.get("themeColor")">...</div>

Update Java object with a Hashmap

I have a model with some properties e.g.
public class Example
{
long id;
String a, b;
int c, d;
boolean e;
}
Now I want to create a method like this
public void update(long id, Map<String, Object> properties)
{
....
}
in this properties map I want to have sth like
properties.put("a","Test");
properties.put("c", 8);
I'm not exactly sure on how to achieve this.
at the end I want to do sth like this:
Example e = new Example(....);
.....
e.update(5L,properties);
can some1 point me to the correct path? I cant figure out a searchterm that doesnt lead me to the Properties or HashMap entries.
thanks in advance
You are searching for the keyword reflection. With reflective access you would write your update method like that:
public void update(long id, Map<String, Object> properties) {
Object obj = getObjectById(id); // you have to implement that method
for (String property : properties.keySet()) {
Field field = obj.getClass().getField(property);
field.set(obj, properties.get(property));
}
}
Note, that I did not declare or handle any exceptions that come along with reflection.
A completely other issue: Why do you want to do it this way? Using reflection to update fields of an object smells like a real design issue. You really should consider another model.

Walking a hashmap of hashmaps to retrieve a string value in Java

I'm new to Java (and not too comfortable with strong typing) and I have a method that takes in a HashMap. A key in this hashmap contains a key, which has a hashmap for value, which also points to a hashmap, etc, until we reach a string:y
HashMap1->HashMap2->HashMap3->HashMap4->String
I am trying to access it as follows:
HashMap1
.get("aKey")
.get("anotherKey")
.get("yetAnotherKey")
.get("MyString");
But then I get an error,
Object does not have a method "get(String)
Here is the method, simplified:
public HashMap<String, HashMap> getMyString(Map<String, HashMap> hashMap1) {
String myString = hashMap1
.get("aKey")
.get("anotherKey")
.get("yetAnotherKey")
.get("MyString");
// do something with myString.
return hashMap1;
}
How would someone properly define the method and the parameters to access nested elements easily?
Thank you,
Simple as that
HashMap1.get("aKey") -- > return hashMap2
.get("anotherKey") --> return hashMap3
.get("yetAnotherKey") --> return hashMap4
.get("MyString"); --> return String
There is something wrong with the adding part.
Now you have structure like below.
hashmap1 --> hashmap2 --> String
String myString = hashMap1.get("aKey").get("MyString");
That is how it should be.
You made too many .get calls. Probably the last one is not needed.
Can you just create class CompoundKey with arbitrary number of String fields and use it as a key? It would simplify your design.
To use it properly in Java you need to override hashCode and equals methods.
You should first of all use interfaces not implementation, therefore use Map (and not HashMap) where possible.
And second, you should repair your Generics and use all levels. Now the compiler can help you and possible show your error.
// i suppose you want to return a String, at least the method name tells it
public String getMyString(Map<String, Map<String, Map<String, Map<String, String>>>> hashMap1) {
String myString = hashMap1
.get("aKey")
.get("anotherKey")
.get("yetAnotherKey")
.get("MyString");
return myString;
}
Yet i suggest that you use a different data structure.

How to make Struts radio tag create a vertical list of radio buttons

I'm using a struts radio tag that is being populated with a list of objects that have two fields:
class MyAction {
List<MyObject> myList;
String selectedId
public String execute() {
...
myList = new ArrayList<MyObject>();
myList.add(new MyObject("1","first object");
myList.add(new MyObject("2","second object");
myList.add(new MyObject("3","second object");
...
}
// Getters and Setters for myList & selectedId
...
}
class MyObject {
String id;
String name;
MyObject(String id, String name) {
this.id = id;
this.name = name;
}
// Getters and Setters for id & name
...
}
Here's what I was using on my page to display the list of radio buttons
<s:radio key="selectedId" list="myList" listKey="id" listValue="name"/>
However, this yields a horizontal list of radio buttons. I tried adding a css style to them:
<style>
.vertical input { display: block; }
</style>
But this causes the labels and the radio buttons to show up on separate lines as well, instead of the radio button and label on the same line:
first object
second object
third object
what I want is:
first object
second object
third object
its actually simple, i mean use theme simple :)
<s:iterator value="myList">
<s:radio theme="simple" name="someNameToSubmit" list="#{id:name}"/><br>
</s:iterator>
This will make name as a label and id as the property to submit
after some googling around a bit... I found a few solutions:
Modify extend the theme and modify the struts FTL for radio buttons: Instructions here. This seemed overkill for me - or at least I'm too lazy for that :)
Use an iterator tag, iterate over each list item, and output one radio button and line break for each list element. Answer came from here
I chose option two (because I'm lazy primarily), although option one would make for a good exercise.
Here's what my struts tag looks like now:
<s:iterator value="myList">
<s:radio key="selectedId" list="{myObject}" listKey="id" listValue="name"/><br/>
</s:iterator>
So the iterator works on a List, so you set the list attribute of the radio tag to be a list of containing only the current myObject. The listKey and listValue are then myObject.id and myObject.name
I have a simple solution. In the list, add <br> to each item such as,
first object <br>
It works though looks like a hack.
You could also just use a map in the Java code. That will also have the benefit of getting rid of the MyObject class. Something like:
class MyAction {
Map<String, String> myMap;
String selectedId;
public String execute() {
// ...
myMap = new HashMap<String, String>();
myMap.put("1","first object");
myMap.put("2","second object");
myMap.put("3","second object");
// ...
}
// Getters and Setters for myMap & selectedId
// ...
}

Categories

Resources