How can I use <s:property> to access a map using OGNL - struts2

I am trying to build a time tracking page where I display the project name, tasks and the hours billed for each day.
Here is the object I have on the Java side:
ProjectTO project1 = new ProjectTO();
project1.setProjectName("Project ABC");
TreeMap tasks = new TreeMap();
tasks.put("100_t1", "Requirement");
tasks.put("100_t2", "Development");
project1.setTasks(tasks);
TreeMap hours = new TreeMap();
hours.put("100_t1:Mon", "8");
hours.put("100_t1:Wed", "7");
project1.setHours(hours);
I'll need to look through this object to display values on the screen. Here is what I have on the JSP Page:
<s:iterator value="activeProjects">
<tbody>
<tr>
<td><p><s:property value="projectName"/></p></td>
</tr>
<s:iterator value="tasks">
<tr>
<td><s:property value="value"/></td>
<td><s:property value="hours[%{key+':Mon'}]"/></td>
<td><s:property value="hours[%{key+':Tue'}]"/></td>
..........
</tr>
</s:iterator>
</tbody>
</s:iterator>
The problem is figuring out how to display the hours. The code snippet shown above does not work. I can get it to work by using <s:set var="mon" value="%{key+':Mon'}"> and referencing it in my td tags - hours[#mon]).
Is there a better solution?
Edit:
Thanks Dave. You are right. I am looking for hours["100_t1:Mon"].
When I am iterating over the object in the view layer, the '100_t1' will need to be dynamically inserted based on the key of the task row i am iterating over. 'Mon', 'Tue' can be hardcoded.
I am going to see if I can do this better outside of the View layer as you recommended. As of now this is what I have working for me.
<s:iterator value="activeProjects">
<tbody>
<tr>
<td><p><s:property value="projectName"/></p></td>
</tr>
<s:iterator value="tasks">
<s:set var="mon" value="%{key+':Mon'}"/>
<s:set var="tue" value="%{key+':Tue'}"/>
<s:set var="wed" value="%{key+':Wed'}"/>
<s:set var="thu" value="%{key+':Thu'}"/>
<s:set var="fri" value="%{key+':Fri'}"/>
<s:set var="sat" value="%{key+':Sat'}"/>
<s:set var="sun" value="%{key+':Sun'}"/>
<tr>
<td><s:property value="value"/></td>
<td><s:property value="hours[#mon]"/></td>
<td><s:property value="hours[#tue]"/></td>
<td><s:property value="hours[#wed]"/></td>
<td><s:property value="hours[#thu]"/></td>
<td><s:property value="hours[#fri]"/></td>
<td><s:property value="hours[#sat]"/></td>
<td><s:property value="hours[#sun]"/></td>
</tr>
</s:iterator>
</s:iterator>
Final Edit:
The following statement helps me avoid having to use s:set tags. I should have surrounded hours[key + ':Mon'] within %{}.
value="%{hours[key + ':Mon']}"/>
also this works too
value="hours[key + ':Wed']"

You're short-circuiting the OGNL evaluation you actually want.
The full OGNL expression you're looking for is actually hours["100_t1:Mon"]:
<s:property value="%{hours[key + ':Mon']}"/>
(IMO) you should be attach the hours to a task more concretely; as it is you're kind of subverting OOP paradigms by using maps and ad-hoc relationships.
Also, you know precisely how many days there are in a week, present the JSP with a list of daily hours. This avoids doing work in the view layer where testing is more difficult.
If you choose to not follow OOP/MVC norms, you can eliminate some redundancy like this:
<s:iterator value="{'Mon', 'Tue', ...etc...}" var="d">
<s:set var="hrs" value="%{hours[key + ':' + #d]}"/>
<td><s:property value="%{#hrs ? #hrs : 'N/A'}"/></td>
</s:iterator>
Again, I feel pretty strongly this is the wrong place to do this type of work.

Related

Recommended approach to implementing inline editing for a MVC grid please?

I am using MVC3, C#, Razor, EF4.1
I have implemented grids in their most simple form ie Razor Tables. At present I have implemented editing of record fields off page ie Click "Edit" and the edit page appears, one then fills in data then save which returns user to main grid page.
I need an inline solution where only 1 or 2 fields need updating. Typically the user would either click on the row or on "edit" link and the row would change to "edit mode". One would then edit the data. One would then click on "Save" and the row would resort to read only, or the grid would refresh. Can you recommend a simple and robust solution for this. At present I am not thinking about 3rd party component solutions such as Telerik Kendo UI Grids , although in the near future I will no doubt upgrade to something like this. At present I want to keep it really simple.
Thoughts, wisdom, recommendations appreciated.
Many thanks.
EDIT:
Thanks all. I am going to give these suggestions a try.
Here is simplest way of doing it, see fiddle.
Save all your data using JSON web service. You'll end up having either array of cells or array of array of cells. (Alternatively you can put JSON in a hidden input box)
Use $.data api and put all information needed for server to save in data attributes.
You'll endup having something simple as
var f=$('#myform')
, t = $('table')
, inputs = t.find('input')
, b1 = $('button.save1')
, b2 = $('button.save2')
, ta = $('#save')
// update data-val attribute when value changed
t.on('change', 'input', (e) => $(e.target).data('val', e.target.value))
// store everything in $.data/data-* attributes
b1.on('click', () => {
var data = []
inputs.each((i,inp) => data.push($(inp).data()) )
ta.text(JSON.stringify(data))
})
// use $.serialize
b2.on('click', () => {
var data = f.serializeArray()
ta.text(JSON.stringify(data))
})
input {border : 1px solid #fff;margin:0; font-size:20px; }
input:focus { outline: 1px solid #eee; background-color:#eee; }
table { border : 1px solid #999; border-collapse:collapse;border-spacing:0; }
table td { padding:0; margin:0;border:1px solid #999; }
table th { background-color: #aaa; min-width:20px;border:1px solid #999; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<form name='myform' id='myform'>
<table>
<tr>
<th></th>
<th>A</th>
<th>B</th>
<th>C</th>
</tr>
<tr data-row="0">
<th>1</th>
<td><input type="text" data-row="0" data-col="0" data-val="a" value="a" name='data[0][0]'/></td>
<td><input type="text" data-row="0" data-col="1" data-val="b" value="b" name='data[0][1]'/></td>
<td><input type="text" data-row="0" data-col="2" data-val="c" value="c" name='data[0][2]'/></td>
</tr>
<tr data-row="1">
<th>2</th>
<td><input type="text" data-row="1" data-col="0" data-val="d" value="d" name='data[1][0]'/></td>
<td><input type="text" data-row="1" data-col="1" data-val="e" value="e" name='data[1][1]'/></td>
<td><input type="text" data-row="1" data-col="2" data-val="f" value="f" name='data[1][2]'/></td>
</tr>
<tr data-row="2">
<th>3</th>
<td><input type="text" data-row="2" data-col="0" data-val="g" value="g" name='data[2][0]' /></td>
<td><input type="text" data-row="2" data-col="1" data-val="h" value="h" name='data[2][1]' /></td>
<td><input type="text" data-row="2" data-col="2" data-val="i" value="i" name='data[2][2]' /></td>
</tr>
</table>
</form>
<div name="data" id="save" cols="30" rows="10"></div>
<button class='save1'>Save 1</button>
<button class='save2'>Save 2</button>
Given that you generate your table in Razor view and don't need to load data into table. So you "loading" data on the server and saving changes with tiny JS snippet above.
You can also style your input cells in the table so they would look different when with focus and not, making it look like Excel spreadsheet (without fancy Excel features though, just look).
Well in that case I will suggest you to add a div with a unique id with each grid row.
and on the click of edit button insert a row having text boxes with value using java script.
Using knockout.js is my preferred approach, and in my opinion, is simple to get started with but flexible enough to keep up with project demands.
Here are examples:
http://www.knockmeout.net/2011/03/guard-your-model-accept-or-cancel-edits.html
http://knockoutjs.com/examples/gridEditor.html
If you think this is for you then take an hour or two and go through the tutorials, it's well worth the time:
http://learn.knockoutjs.com/
I have implemented exactly what you are asking for, but I cannot assure you that it is robust. It definitely is not simple. Based on the article Get the Most out of WebGrid in ASP.NET MVC by Stuart Leeks I have created an MVC project which I have heavily modified with my own javascript. In the end I have come up with a solution that works but could be vastly improved. Took me at least a week to implement.
I write tutorial for implementing inline editable grid using mvc, knockoutjs with source code:
http://www.anhbui.net/blog?id=kojs-1

Paginated, sortable, collapsible datarows (tables) in mobile application

I want to implement the Demo shown at http://tablesorter.com/docs/example-pager.html in a mobile application. I want each row in the Demo to be collapsible/expandable. What is the best way to achieve it? To my knowledge tables don't work well in mobile applications. Correct me if I am wrong. Thanks.
== EDIT ==
This is almost all I want
http://stokkers.mobi/jqm/tableview/demo.html
I'm not sure how this will do in a mobile application, but check out this demo. No need to download that mod as it was added to tablesorter 2.0.5.
The demo shows how to use an undocumented option named cssChildRow which contains the class name expand-child. This class name is added to a table row tr to attach it to the row before it (example code from that demo):
<tr>
<td rowspan="2" class="collapsible"></td>
<td rowspan="2" class="collapsible_alt">SO71774</td>
<td>Good Toys</td>
<td>PO348186287</td>
<td>Jul 20, 2007</td>
<td>$972.78</td>
</tr>
<tr class="expand-child">
<td colspan="4">
<div class="bold">Shipping Address</div>
<div>
99700 Bell Road<br /> Auburn, California 95603
</div>
</td>
</tr>
The code needed to make the rows collapsible in in this file: jquery.tablesorter.collapsible.js

Struts2 : Double Iteration over Parameters does not work?

Hello I am a young Software developer,
and I struggled the last 5 days with my code.
Here is my code in JSP:
<s:iterator value="getListeDanach()" status="stat">
<li>
<s:url id="URL_ListeDanach" action="uebersicht_umblaettern">
<s:param name="angeklickteSeitenzahl" value="getListeDanach()[#stat.index]" />
<s:bean name="org.apache.struts2.util.Counter" var="counter">
<s:param name="last" value="3" />
</s:bean>
<s:iterator value="#counter" status="stat1">
<s:property value="#stat1.index" />
<s:param name="%{optionaleParamName4}" value="#optionaleParamValue4" />
</s:iterator>
</s:url>
<s:a href="%{URL_ListeDanach}" cssClass="naviTab">
<s:property value="getListeDanach()[#stat.index]" />
</s:a>
</li>
</s:iterator>
My problem is, the first Iteration works great but the 2nd Iteration works half. In the 2nd case the property works, but the param doesn´t work! Al Variables are available. If i take the param Tag of the 2nd Iteration and place it in the first, it works great! But that isn´t what I want.
This is not an answer.
Here's the JSP, cleaned up, and using more S2 functionality. It was impossible to read the original.
<s:iterator value="listeDanach" status="stat" var="outerItem">
<li>
<s:url id="URL_ListeDanach" action="child">
<s:param name="angeklickteSeitenzahl" value="outerItem" />
<%-- What are you trying to do here? --%>
<s:bean name="org.apache.struts2.util.Counter" var="counter">
<s:param name="last" value="3" />
</s:bean>
<%-- What are you trying to do here? There's nothing to iterate over. --%>
<s:iterator value="#counter" status="stat1">
<s:property value="#stat1.index" />
<s:param name="%{optionaleParamName4}" value="#optionaleParamValue4" />
</s:iterator>
</s:url>
<s:a href="%{URL_ListeDanach}">
<s:property value="outerItem" />
</s:a>
</li>
</s:iterator>
In the bean i have a loop with 3 "rounds", in the 2nd iterator i use the var=counter to iterate three times over the property and over the dynamic parameter.
The property shows in HTMl in every loop of the first iterator this result : 0 1 2;
This is how it should work(the property is just there, to test the functionality of the 2nd itarator.)
But in the 2nd case, the parameter-Tag is fully ignored or something like that. For those who want to know the logic behind the code. It is a side navigation bar. listeDav or = list behind the actual number, and listeDanach= list after the actual number. 1 2 3 4 [5] 6 7 8 9.... When the param Tag in the 2nd itarator functions well, I would make the param tag dynamically with the iterated index.
SO in short, what I want is: Every time the first Iterator has his loop, I want to create dynamic parameters. This parameters are defined in the JSP before and are fully supported! I want to use the index "#stat1.index" to make it work.
Something like this :
s:param name="%{optionaleParamName[#stat1.index]}" value="#optionaleParamValue[#stat1.index]" />.....
i have already defined the String behind "#optionaleParamValue[0], behind #optionaleParamValue[1] and behind #optionaleParamValue[2] and soo on... ... and all this is for reusing the actual JSP.
As you can mention, a side navigation bar, can be used in many other cases in the programm.
Greetings

Struts 2 iterator tag status index is not working

<s:iterator value="podTemplate.subTypeTemplates" status="subTemplate">
<s:textfield id='subType_type_#subTemplate.index' key="subType" label="Name"/>
</s:iterator>
#subTemplate.index is not getting replaced by index. However if I am doing
<s:property value="#subTemplate.index"> is working
That's because id attribute of textfield is of string type and string-type attributes are not interpreted as OGNL by default. As Steven said you have to force the interpretation by using %{} in your case subType_type_%{#subTemplate.index}.
My iterator looks like this:
<s:iterator id="list" value="optionList" status="rowStatus" >
I tried:
<td>
<input class="textInput required"type="text" name="optionList[${rowStatus.index}].option_id"/>
</td>
and it's the only one that worked.
All the following ones failed:
name="optionList[%{#rowStatus.index}].option_id"
name="%{optionList[#rowStatus.index].option_id}"
even
name="%{#rowStatus.index}"

Struts2 Customizing error message

I am using struts2 in my application and trying to diplay error message using "s:actionerror/". It diaplays fine but a dot(.) also appears with the error message which looks ugly and is displayed like list.
Is there any way to cuatomize the error message in struts2.
Thanks in advance.
Brian Yarger's answer is the most complete solution. The easiest solution on the other hand is to just use CSS and modify the li element.
JSP:
<s:if test="hasActionErrors()">
<div class="errors">
<s:actionErrors/>
</div>
</s:if>
CSS:
li .errors { list-style: none; }
Another solution is to override the template for the default actionError output.
You can find the default templates in the struts2 core jar. If you pull out template.simple/actionerror.ftl, you can customize that. You can either come up with your own template and reference it in the s:actionerror tag with the template attribute, or you can keep the same name and put it in /template/simple and it will be used as the default.
Most of the templates are in freemarker, although there are still some of them in velocity depending on your struts2 version. Both are pretty easy to work with templating engines.
I guess the kozmic's solution should be:
JSP:
<s:if test="hasActionErrors()">
<div class="errors">
<s:actionErrors/>
</div>
</s:if>
CSS:
div .errors li { list-style: none; }
It didn't work for me with his CSS code.
Hi I found the solution for getting rid of the dot.
<table align="center" width="70%" class="stats">
<tr>
<s:if test="hasActionErrors()">
<s:iterator value="actionErrors">
<tr>
<td class="error">
<img alt="error message" src="./images/cross.gif" width="10" height="10"/> <s:property escape="false" />
</td>
</tr>
</s:iterator>
</s:if>
Try this out.
I know this is an old question, but I want to share my simple solution. This just prints out each error message without any extra generated html markup. Then you can wrap the <s:property value="%{error}"/> in some custom html if you want.
<s:if test="hasActionErrors()">
<s:iterator var="error" value="%{actionErrors}">
<s:property value="%{error}"/>
</s:iterator>
</s:if>

Resources