Struts + Back Button - struts2

My MVC is Struts 2
My requirement is to clear some error message in the leading page AFTER the back button is clicked in the browser.
The sequence is:
Page A --> Page B --> Page C
The Page B can be navigated from Page A or from Page C (by clicking the back button). If Page B has an error message, and back button from Page C is clicked, this error message has to be removed. But I have no way of determining how the user navigated to Page B (considering there are 2 ways as mentioned above).
Searched everywhere for a solution. Found out that window.history is not a reliable way to parse the previous page.
Also, the window.location is not changing because of the Struts 2 quirks with tiles.
The frustrating thing is browser back button does not hit the server. Neither does it process JSP or struts tag. Luckily, though, it runs the javascript.
So I came up with a custom solution that uses server and client timestamps. It seems to work in all browsers.
I would like to know if there is an easier way.
<%
if (session.getAttribute("myTime") != null) {
//server sends the time in millis on the first invocation
Long tmStamp = Long.parseLong((String) session.getAttribute("myTime"));
// out.println(tmStamp);
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date javaDt = new Date();
javaDt.setTime(tmStamp);
// out.println(" " + sdf.format(javaDt));
Date clientDt = new Date();
Long clientMillis = clientDt.getTime();
Long timeDiff = clientMillis -tmStamp;
%>
<script>
var currDt = new Date().getTime();
var dt = new Date(<%= tmStamp%>);
dt.setTime(<%=tmStamp%>);
var jsDt = new Date();
jsDt.setTime(currDt);
//alert( "<%= tmStamp %>" + " javascript time=" + (currDt - dt.getTime()) + ' timeDiff=' + (1 * <%= timeDiff%>) + " " + jsDt + " " + dt);
//we expect the server to repond within 5 seconds
if (currDt > (<%= tmStamp %> + (1*<%= timeDiff %>) + 5*1000)) {
//alert("referer is summary" )
clearError();
} else {
//alert("referer is not summary");
<s:if test="flag == 'none'">
$('<tr><td align="center"> <font color="red" style="font-size:14px;font-family:"sans-serif, Arial, Helvetica";height:20px;width:180px; font-weight:bold;" ><blink>No Result found for the given Search Criteria</blink></font></td></tr>')
.insertAfter("#lastRow");
$('<tr><td align="center"> <font style="font-size:14px;font-family:"sans-serif, Arial, Helvetica";height:20px;width:180px; font-weight:bold;" ><blink>No Result found for the given Search Criteria</blink></font></td></tr>')
.insertBefore("#firstRow");
</s:if>
}
</script>
<% } else {
out.println("No session attribute for myTime");
}
%>

Related

Create jQuery ui dialog box for each row in a table

I am trying to append rows to a table using an array called searchResults. Everything works as expected until I introduce the jQuery UI dialog box. The problem is I need a new dialog box for each row in the first column. I'm pretty new to all of this so I'm pretty sure I'm using the index incorrectly at times. This is just to give you an idea of what I'm trying to accomplish. Any ideas how to do this correctly?
for (var i = 0; i < searchResults.length; i++)
{
$('#patientFileDialog[i]').dialog();
$'#openPFDialog[i]').click(function() {
$('#patientFileDialog[i]').dialog('open');
});
var dialog[i] = $(`<div id="patientFileDialog[i]" title="Patient File">${searchResults[i].patientWebLink}</div>`);
body.append('<tr>'+
`<td><button id="openPFDialog[i]">Click Here</button></td>` +
`<td>${searchResults[i].patientFirstName}</td>` +
`<td>${searchResults[i].patientLastName}</td>` +
`<td>${searchResults[i].patientDateOfBirth}</td>` +
`<td>${searchResults[i].patientDataPulseID}</td>` +
`<td>${searchResults[i].patientLaserFicheID}</td>` +
'</tr>')
}
After looking at your code a bit more I think I can see what you are trying to do. Working JSFiddle, with some faked searchResults so we can see it in action.
There are a few problems with the code in your question:
Using selectors like $('#patientFileDialog[i]') and $'#openPFDialog[i]') will try to match elements on the page with those IDs. AFAICT those don't actually exist yet, you are trying to create them.
var dialog[i] = ... sets up some divs as strings, but those are never added to the page;
As I mentioned in my comment, there are some syntax errors, maybe just typos and mixed up formatting here on SO;
Here's an updated version of the code. Notable changes:
Instead of adding an event handler for every individual openPFDialog button, it is better practice to add just one which matches them all. That single handler can then work out which button was clicked, and take the right action for just that one, not all of them. In this case if you have all your buttons use IDs that match openPFDialog-X, where X is a number, you can target anything matching that pattern (using a starts with selector, and find the X by removing the openPFDialog- part with replace.
There's an added complication with the above though. Selectors parsed at page load will only match elements that exist at that time. In this case, you're adding new elements to the page, and a selector defined at page load won't match them. The solution is to select instead some parent element which does exist at page load, and filter. This is called event delegation (search for the paragraph starting with "Delegated event handlers").
Working from what you have, I am guessing the patientFileDialogs you create should be placed inside some parent element which is not displayed on the page? That's what I've done.
Here's the code (and working JSFiddle):
var dialog, i;
// Single click handler for anything that starts with "openPFDialog-".
// Since those elements don't exist on the page yet, we need to instead
// select a parent object, say the body, and filter for clicks on our
// elements starting with our pattern
$('body').on('click', '[id^=openPFDialog]', function() {
// We need to find the "i"
i = $(this).attr('id').replace(/openPFDialog-/,'');
console.log('clicked on id', i);
$('#patientFileDialog-' + i).dialog();
});
for (var i = 0; i < searchResults.length; i++) {
// Create a new div with ID like "patientFileDialog-1", using the current
// search result
dialog = $('<div id="patientFileDialog-' + i + '" title="Patient File">' + searchResults[i].patientWebLink + '</div>');
// Add it to the page. I've use a div with ID dialogs which is hidden
$('#dialogs').append(dialog);
$('table').append('<tr>'+
'<td><button id="openPFDialog-' + i + '">Click Here</button></td>' +
'<td>' + searchResults[i].patientFirstName + '</td>' +
'<td>' + searchResults[i].patientLastName + '</td>' +
'<td>' + searchResults[i].patientDateOfBirth + '</td>' +
'<td>' + searchResults[i].patientDataPulseID + '</td>' +
'<td>' + searchResults[i].patientLaserFicheID + '</td>' +
'</tr>');
}
Update
One last suggestion - manipulating the DOM by adding/removing elements is slow. If you need to do that for each element in an array, it is best to avoid actually adding your content on each iteration, and rather just build up a string. Then once you're done iterating, just add the big single string, so you're chaning the DOM just once. Here's the basic changes needed to do that:
// Add some new variables to hold our big strings
var dialog, dialogs, row, rows, i;
// ... your code ...
for (var i = 0; i < searchResults.length; i++) {
// Create the dialog ...
dialog = ...
// Append it to our big string of all dialogs
dialogs += dialog;
// Same approach for rows
row = '<tr>'+ ... all that stuff
rows += row;
}
// Finished iterating, nothing added to DOM yet. Do it all at once::
$('#dialogs').append(dialogs);
$('table').append(rows);
Here is what I finally ended up having to do:
$(document).ready(function(){
if ($('[attr="searchResultsJson"]').length)
{
$('.approval-outer-wrap').prepend(drawTable());
$('.approval-outer-wrap').append('<div id="result-details" title="Search Result Detail"><p></p></div>')
}
$('body').on('click', '[id^=openPFDialog]', function() {
var result = $(this).parents('tr').data('result');
$('#result-details p').html(result.patientFirstName);
$('#result-details').dialog();
});
});
function drawTable(){
var table = $('<table id="search-results" />');
var header = $('<thead />');
table.append(header);
header.append('<tr><th>Patient File</th><th>First Name</th><th>Last Name</th><th>Date of Birth</th><th>Data Pulse ID</th><th>Laserfiche ID</th></tr>');
var body = $('<tbody />');
table.append(body);
var json = $('[attr="searchResultsJson"] [type="text"]').text();
var searchResults = JSON.parse(json);
for (var i = 0; i < searchResults.length; i++) {
body.append(`<tr data-result='${JSON.stringify(searchResults[i])}'>`+
`<td><button id="openPFDialog-` + i + `">🔍</button></td>` +
`<td>${searchResults[i].patientFirstName}</td>` +
`<td>${searchResults[i].patientLastName}</td>` +
`<td>${searchResults[i].patientDateOfBirth}</td>` +
`<td>${searchResults[i].patientDataPulseID}</td>` +
`<td>${searchResults[i].patientLaserFicheID}</td>` +
'</tr>');
}
return table;
}
Consider the following code.
function showPatientDialog(cnt){
$("#patient-file-dialog").html(cnt).dialog("open");
}
var d = $("<div>", {
id: "patient-file-dialog",
title: "Patient File"
})
.appendTo("body")
.dialog({
autoOpen: false
});
$.each(searchResults, function(i, result) {
var row = $("<tr>").appendTo(body);
$("<td>").appendTo(row).html($("<button>", {
id: "open-pdf-dialog-" + i
}).click(function() {
showPatientDialog(result.patientWebLink);
}));
$("<td>").appendTo(row).html(result.patientFirstName);
$("<td>").appendTo(row).html(result.patientLastName);
$("<td>").appendTo(row).html(result.patientDateOfBirth);
$("<td>").appendTo(row).html(result.patientDataPulseID);
$("<td>").appendTo(row).html(result.patientLaserFicheID);
});

How to count time on rails

<h1>TIME: <%= Time.now.strftime("%H:%M:%S") %></h1>
In the template, my time is stopped. I want to animate it time like a digital watch.
The time is changed when I refresh a page.
For example, I'm going to index page and now the time shows: 12:30:50 (HH:MM:SS). I refresh again and the time shows: 12:31:10.
I want to show my seconds like this: 50 51 52 53 54.....
function displayTime() {
var timer=document.getElementById('timer');
var currentdate = new Date();
var hours = currentdate.getHours();
var minutes = currentdate.getMinutes();
var seconds = currentdate.getSeconds();
timer.innerHTML = hours + ':' + ("0" + minutes).slice(-2) + ':' + ("0" + seconds).slice(-2);
};
setInterval(displayTime, 1000);
<h1>Time :
<p id="timer">
<%= Time.now.strftime("%H:%M:%S") %>
</p>
</h1>
First, let's create a function that displays the current date. For this, we just have to find the p tag we are interested in (using getElementById), and put use innerHTML to write in it. That's the function displayTime.
Then, we want to run this function every second, so we use : setInterval.
Note I keep <%= Time.now.strftime("%H:%M:%S") %> so that if javascript is not enabled there is still a date. If that's not the intended behavior then you can replace it.

jump to search result in a repeat control with a pager control in a xpages

I created a search control in xpages, that works well. My problem now is how to create the link to open the result. Thats also working fine except when the result is in a xpages repeat control.
So my question is how can I jump directly to a specific site and to a specific doc on that site e.g. page-site 3 and there the 5th doc.
Is it possible to execute multiple anchor links (open the site, jump to the page in the repeat control and than to the doc ...).
If have the same 'issue' with my deep-links and with my tag-links.
Thanks
Armin
The code of the repeat looks something like this (I cleaned some bootstrap stuff out, hopefully not to much)
<xp:dominoView var="viewSC" sortColumn="sorting"
sortOrder="descending">
<xp:this.viewName><![CDATA[#{javascript:if (sessionScope.level2UNID == ""){
return "HV3101";
}else{
return "HV3201";
}}]]></xp:this.viewName>
<xp:this.search><![CDATA[#{javascript:var qString:String;
if (sessionScope.level2UNID != null & sessionScope.level2UNID != ""){
qString = "(Field level2Key = \"" + sessionScope.level2UNID + "\")";
}else{
qString = "(Field level1Key = \"" + sessionScope.level1UNID + "\")";
}
sessionScope.l3Query = qString;
return qString;}]]></xp:this.search>
</xp:dominoView>
<xp:panel id="contentRepeat" styleClass="content">
<xp:repeat id="level3" rows="2" var="level3List"
disableOutputTag="true" value="#{viewSC}" indexVar="L3X">
<xp:panel id="repeatFrame" disableOutputTag="true">
<xp:this.data>
<xp:dominoDocument var="docL3" action="openDocument"
documentId="#{javascript:level3List.getNoteID()}"
ignoreRequestParams="true">
</xp:dominoDocument>
</xp:this.data>
<xp:panel id="anchor" tagName="a">
<xp:this.attrs>
<xp:attr name="name">
<xp:this.value><![CDATA[#{javascript:try{
docL3.getItemValueString("UniqueID");
}catch (e){
sessionScope.L3Err = e;
}}]]></xp:this.value>
</xp:attr>
</xp:this.attrs>
</xp:panel>
<xp:panel id="bsAccordion" styleClass="accordion">
<xp:div styleClass="accordion-group">
<xp:div styleClass="accordion-heading">
<xp:link escape="true" styleClass="accordion-toggle" id="toggleSubContent">
<xp:this.attrs>
<xp:attr name="data-toggle" value="collapse">
</xp:attr>
<xp:attr name="data-parent" value="##{id:bsAccordion}">
</xp:attr>
</xp:this.attrs>
<xp:this.text><![CDATA[#{javascript:try{
if(level3List != null){
var nd:NotesDocument = level3List.getDocument();
if(nd != null){
if(nd.getItemValueString("dspCreated") == "1"){
var creationDate:NotesDateTime = nd.getItemValueDateTimeArray("created")[0];
return " " + nd.getItemValueString("title") + " (Erstellt: " + creationDate.getDateOnly() + ")";
}else{
return " " + nd.getItemValueString("title");
}
}
}
}catch(e){
sessionScope.errorEx = e;
}}]]></xp:this.text>
<xp:this.value><![CDATA[#{javascript:x$(getComponent("accordionCollapse").getClientId(facesContext))}]]></xp:this.value>
<i class="icon-minus"></i>
</xp:link>
<xp:link escape="true" id="link1" value="##{id:pager1}">
<xp:this.text><![CDATA[#{javascript:"aP" + L3X}]]></xp:this.text>
</xp:link>
</xp:div>
<xp:panel styleClass="accordion-body collapse in" id="accordionCollapse">
<xp:div styleClass="accordion-inner">
<xp:panel id="subRTContent" styleClass="l3Content">
<xp:inputRichText id="dspLevel3" value="#{docL3.content}"
readonly="true" styleClass="l3Content">
</xp:inputRichText>
</xp:panel>
<xp:panel styleClass="contentFooter" id="contentSFooter">
</xp:panel>
</xp:div>
</xp:panel>
</xp:div>
</xp:panel>
</xp:panel>
</xp:repeat>
</xp:panel>
A few pointers that might help you to go ahead:
stop paging in repeats. The extlib has a nice sample of "show more", so instead of showing 1-20, 21-40 etc. you show until the record you found (might not work for large datasets)
Open the search page with URL parameters that indicate the position number of the document (e.g. 498). Then use that parameter to let the repeat control figure out the starting position (which depends on the number of documents the repeat is configured for (default is 30, but you don't know). Just make sure not to use the passed parameter unchecked.
you can add <a name="[unid]"> (or id=) tags to the repeat and then [directly navigate] (http://reference.sitepoint.com/html/a) to them ....nsf/searchResults.xsp#unid?moreParameters
Let us know how it goes

Display dijit toolTip with Dojo DataGrid and JSonRestStore

Is there a more efficient way for displaying a tool tip once a cell is hovered? Using the structure attribute to format the datagrid, is there a way to use formatter to display a dijit toolTip rather than using the html title attribute.
Here is the column in which the toolTip is displaying.
var subscriberGridLayout = [
{
name: " ",
field: "ExpirationDate",
formatter: function(value){
if(value){
expDate = formatDateIE(value);
return toolTip();
}
else
return " ";
},
styles: "text-align: center;",
width: "30px"
},
Here is the function that displays a tooltip icon through the image tag but instead of a dijit toolTip it simply uses html's title to display a popup.
function toolTip(){
src = "'/Subscriber/resources/images/icons/icon_error.gif'/>";
if(dojo.date.difference(today, expDate) <= 0 ){
message = "Credential expired.";
return "<img title='"+ message + "' src=" + src + "";
} else if(dojo.date.difference(today, expDate) <= 60) {
message = "This Subscriber will expire in " + dojo.date.difference(today, expDate) + " days."
+ "
To prevent an interruption in the Subscriber’s access, please sumbit a request to " +
"renew the Subscriber within 30 days of the expiration date.";
return "<img title='"+ message + "' src=" + src + "";
} else {
return " ";
}
}
I would do something like:
new Tooltip({
connectId: grid.domNode,
selector: "td",
getContent: function(matchedNode){
return matchedNode.innerText
}
});
With grid.domNode you can get the generated DOM of your widget. A grid generates a table-structure, so you can get the cells by using the selector and getContent properties.
I must say it's not really the correct way to do it because now you're playing with the internal structure of the Dojo widget. If they once decide not to use a table as DOM structure your code won't work.
But I don't think there is a better way to achieve this, in the end you will always have to translate the Dojo cell to a DOM node (because tooltips are DOM based). You can of course connect a tooltip to each cell, but I tried that before and it was a little buggy (sometimes the tooltip didn't pop up).
I also made a JSFiddle to show you a working example.

JQuery Mobile 1.3.0 Collapsible Nested Lists Not Rendering Properly

I have a JQM page that is inserted dynamically into the DOM and must be regenerated every time, as the data may change. The first time the page is displayed, everything works as it should, but if the user returns to this page later, I have the following rendering problem. Here is the list closed:
Here is the list open on the second viewing of the screen:
I've tried various combinations of $(id-selector).trigger('create'), .remove(), and .empty(), but nothing so far makes the page the second time work like it does on the first.
For what it's worth, since this seems to be a problem with JQM for which I am seeking a workaround, here's the code that builds this list:
var url_base_key = resource.url + '_base';
html += '<div data-role="collapsible-set" data-inset="false" id="per-back-issues">';
if (window.per_info.back_issues.length > 0){
html += '<br /><p><b>' + Label('label_back_issues') + '</b></p>';
for (var i = 0; i < window.per_info.back_issues.length; i++){
var group = window.per_info.back_issues[i];
if (group.issues.length > 0){
html += '<div data-role="collapsible" class="per_group" id="per-group-' + group.group + '"><h2 id="group-label-' + group.group + '">' + group.group + '</h2><ul data-role="listview">';
for(var j = 0; j < group.issues.length; j++){
var issue = group.issues[j];
var url_base = window.orgbase_api[url_base_key];
var url = url_base + issue.formats[0].file;
var id = resource.orgbaseapi_url + '-' + issue.year + '-' + issue.month + '-lit_menu_item';
var item = '<li class="per_item">' + GetPdfLink(resource.id, id, url, GetLongMonth('gregorian', issue.month)) + '</li>'
html += item;
}
html += '</ul></div>';
}
}
}
html += '</div>';
This content is wrapped in a JQM page container
<div id="newsletter" data-role="page" data-theme="b" data-content-theme="b">
<div data-role="header">
Back
<h1>Newsletter</h1>
Home
</div>
...
</div>
and added to the DOM every time with
var new_screen = $(html);
new_screen.appendTo($.mobile.pageContainer);
If I try to do a $('#newsletter').remove() before the appendTo(), the appendTo() doesn't work. I can't use an expand event to force the list to redraw itself because the event fires before the expansion happens.
Ok, the problem seems to be the use of new_screen.appendTo($.mobile.pageContainer) multiple times for the same id. I had tried to remove it using $('#' + id).empty().remove() if it already exists before the appendTo(), but I couldn't add it again (perhaps someone can help me understand why I can't do it this way). So I tried just updating the container if it already exists and that fixed the problem. I'd like to understand how adding the item multiple times led to the results I got.
if ($('#' + id).length > 0){
var html = script + body;
//update existing page container
$(id).html(html);
} else {
var html = '<div id="' + id + '" data-role="page" data-url="' + id
+ '" data-theme="' + theme + '" data-content-theme="' + theme + '" class="screen_section">'
+ script + body + '</div>';
var new_screen = $(html);
//add new page container to DOM
new_screen.appendTo($.mobile.pageContainer);
}

Resources