I have been very successful building dynamic html portlets with jQuery.
My approach has been to define the html template first in the html portlet window, for example:
<div class='ppm_filter_section'>
<div style='height: 350px; margin-left: 80px '>
<div id='requestDivYes' style="display: none">
<br/><br/>
<table id="requestTable" class='ppm_grid'>
<thead>
<tr>
<td style="text-align: center">Request Type</td>
</tr>
</thead>
<tbody class='ppm_grid_content'>
</tbody>
</table>
</div>
<div id='requestDivNo' style='display: none; width: 680px; margin-left: 80px'>
<br/><br/>
Sorry, it appears that you do not have any authorization for Excel Requests.<br/><br/>
If you feel this is not correct, please contact Support.
</div>
<div id="requestDivSubmit" style='display: none; width: 680px; margin-left: 80px'>
<br/><br/>
The following Excel Request have been submitted:<br/>
<br/>
<ol id="requestItems"></ol>
<br/>
The requested item(s) will be sent to <a id='emailLink' href="mailto:name@example.com">example.com</a>
<br/><br/>
Please allow up to 10 mins for processing and delivery.
<br/>
</div>
</div>
</div>
After the template I define a number of helper functions.
<script type="text/javascript">
function pmatRequest(){
var requestTypesUrl = requestUrl + '/' + currentSession;
var queryRequest = getQueryRequest(currentSession);
var queryOptions = {url: ppmUrl, type: 'POST', data: queryRequest, contentType: 'text/xml;charset=UTF-8'};
var requestOptions = {url: requestTypesUrl, type: 'GET', contentType: ' application/json; charset=utf-8'};
jQuery.when(jQuery.ajax(queryOptions), jQuery.ajax(requestOptions)).then(processSuccess, processError);
}
function processError(data, textStatus, jqXhr) {
var error = data.responseText;
var i = 0;
}
function processSuccess(queryResults, requestResults) {
try {
var queryType = queryResults[0];
requestType = requestResults[0];
var authorizedRequests = {};
authorizedRequests.requests = [];
var records = jQuery(queryType).find('Record');
if (records == null || records.length == 0){
jQuery('#requestDivNo').show();
return;
}
for (var i = 0; i < records.length; i++) {
var token = jQuery(records[i]).find('token').text();
if (token != currentSession) continue;
requestType.UserId = jQuery(records[i]).find('user_name').text();
requestType.FirstName = jQuery(records[i]).find('first_name').text();
requestType.LastName = jQuery(records[i]).find('last_name').text();
requestType.Email = jQuery(records[i]).find('email_address').text();
authorizedRequests.distroString = jQuery(records[i]).find('mhs_summary_distro').text();
if (!isEmpty(authorizedRequests.distroString)) {
var distroList = authorizedRequests.distroString.split(";");
for (var k = 0; k < distroList.length; k++) {
var distro = distroList[k].trim();
for (var j = 0; j < requestType.Item.length; j++) {
if (distro != requestType.Item[j].Token) continue;
authorizedRequests.requests.push(jQuery.extend(true, {}, requestType.Item[j]));
break;
}
}
}
break;
}
if (authorizedRequests.requests.length == 0) {
jQuery('#requestDivNo').show();
return;
}
jQuery('#requestDivYes').show();
var tbody = jQuery('#requestTable').find('tbody');
for (var i = 0; i < authorizedRequests.requests.length; i++) {
var id = 'pmatRequestRadio_' + i;
var displayName = authorizedRequests.requests[i].DisplayName;
var token = authorizedRequests.requests[i].Token;
var tr = jQuery('<tr>');
var td = jQuery('<td>', {style: 'width="600px'});
td.append(jQuery('<input>', {type: "radio", alt: 'pmatRequestRadio', name: displayName, value: token, id: id}));
td.append(jQuery('<label>', {id: id}).innerHTML = displayName);
tr.append(td);
tbody.append(tr);
}
var tr = jQuery('<tr>');
var td = jQuery('<td>', {style: 'width:600px; text-align:center;vertical-align: middle;'});
td.append(jQuery('<br><input type="submit" value="Submit" onClick="processRequest();">'))
tr.append(td);
tbody.append(tr);
}
catch (ex) {
var msg = ex;
}
}
function processRequest() {
try {
var checked = jQuery('input[alt=pmatRequestRadio]:radio');
if (checked.length == 0) return;
var retVal = [];
retVal.push(currentSession);
for (var i = 0; i < checked.length; i++) {
if (!checked[i].checked) continue;
retVal.push(checked[i].value);
jQuery('#requestItems').append(jQuery('<li>').append(checked[i].name));
}
requestType.Requests = retVal.join('|');
var requesterMailTo = 'mailto:' + requestType.Email;
var requesterName = requestType.FirstName + ' ' + requestType.LastName;
jQuery('#emailLink').attr('href',requesterMailTo);
jQuery('#emailLink').text(requesterName);
jQuery('#requestDivYes').hide();
jQuery('#requestDivSubmit').show();
var jsonData = JSON.stringify(requestType);
var requestOptions = {url: requestUrl, type: 'POST', data: jsonData, contentType: ' application/json'};
jQuery.ajax(requestOptions);
}
catch (ex) {
var msg = ex;
}
}
function isEmpty(str) {
return (!str || 0 === str.length);
}
function getQueryRequest(sessionId) {
var queryXml = [];
queryXml.push('<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:quer="http://www.niku.com/xog/Query">');
queryXml.push('<soapenv:Header>');
queryXml.push('<quer:Auth>');
queryXml.push('<quer:SessionID>?</quer:SessionID>'.replace('?', sessionId));
queryXml.push('</quer:Auth>');
queryXml.push('</soapenv:Header>');
queryXml.push('<soapenv:Body>');
queryXml.push('<quer:Query>');
queryXml.push('<quer:Code>pmat_active_request</quer:Code>');
queryXml.push('</quer:Query>');
queryXml.push('</soapenv:Body>');
queryXml.push('</soapenv:Envelope>');
return queryXml.join("");
}
</script>
The a script to execute against the template:
<script type="text/javascript">
var currentSession = window.clarity.session.sessionId;
var host = window.location.host;
var requestUrl = 'https://simple.net.net/RequestServer/api/RequestTypes';
var ppmUrl = 'https://'+host+'/niku/xog';
var requestType = {};
jQuery(document).ready(pmatRequest());
</script>
Once the document is ready, I execute a function that calls the needed XOG/REST queries and if successful passes the results to a processSucess function otherwise a processError function.
I chose this example as it does multiple SOAP/REST calls and renders a user specific UI which might be solution for you need.
Let me know if it makes sense or I need to go into more detail -- I found this method to work very well and eliminates any external html files.
I still thinking about pushing this all into a custom object that holds everything and all html portlet contain is a very simple stub to latch the custom object element to.
Another option maybe,
Gene