PDA

View Full Version : load page into panel and have javascript executed



luv2hike
8 Feb 2008, 7:53 AM
This was a problem that had me stumped for a while. I found 3 or 4 other threads in the forum from people with the same issue and several proposed solutions. However, none of the solutions worked for me, so I came up with this one. It seems to work consistently well and is very simple to understand and use.

I was kind of surprised this functionality wasn't already present in ExtJS (I only started using it last week) but think something like this could be easily added. Maybe this scheme could be used in the panel.load() method when loadScripts is set to true? I thought that was supposed to do it and is what I tried first, however, while it loaded the page, the script inside that page did not execute.

So without further delay, here is the solution I came up with, using information from another thread on this forum about the insertHtml() call, in case someone else needs it:



// this goes inside the parent/main page
var spanel;
var grid;

function loadPage(target, url, params)
{
Ext.Ajax.request(
{
url: url,
params: params,
success: function(response, options)
{
var html = response.responseText;
var el = target.getUpdater().getEl();
el.dom.innerHTML = '<div></div>';
el.insertHtml('afterbegin', html);
}
});
}
...
// it's used in here (also in the main page)
grid = new Ext.grid.GridPanel({
store: new Ext.data.Store({
data: myData,
reader: myReader
}),
columns: [
{header: 'ID', width: 30, sortable: true, dataIndex: 'exerID'},
{header: 'Exercise Name', width: 120, sortable: true, dataIndex: 'exerName'},
{header: 'Bench Position', width: 80, sortable: true, dataIndex: 'bench'}
],
viewConfig: {
forceFit: true
},
renderTo: 'exer',
stripeRows: false,
frame: true,
header: true,
tbar: [
{
id: 'plus',
text: 'Add Exercise',
handler: function()
{
spanel.expand();
loadPage(spanel, "exerciseDetails.jsp");
},
tooltip: 'Click to add a new exercise.'
}
]
});
...
// spanel is defined within the viewport definition which contains a border layout
spanel = new Ext.Panel({
region:"south",
bodyStyle:"background-color:#bacfff",
title:" ",
height:300,
autoScroll:true,
collapsible:true,
collapseMode: 'mini',
collapsed: true,
hideCollapseTool:true,
split:true,
}), // only an excerpt but other panels follow
...


Then the page that gets loaded looks like:
(again, only relevant portions shown to protect some business and DB logic)



<!-- NOTICE: no html, head, or body tags! -->
<!-- notice the call to include its own javascript -->
<script type="text/javascript" src="exercise.js"></script>

<%-- Page contents --%>
<div id="body">
<%-- header --%>
<!-- the form with the action going to our script -->
<form name="mainform"
action="javascript:saveExerciseForm(document.mainform.exerid.value,
document.mainform.exerciseName.value,
document.mainform.benchPosition.value)">
<%-- overall page table --%>
<table style="border: medium solid black">
<tr>
<th align="right">Exercise&nbsp;Name&nbsp;:&nbsp;</th>
<td><input tabindex="1" type="text" name="exerciseName" maxlength="64" size="64" value="${results.rows[0].exerciseName}"/></td>
</tr>
<tr>
<th align="right">Bench&nbsp;Position&nbsp;:&nbsp;</th>
<td>
<select tabindex="2" name="benchPosition">
<option value=""<c:if test="${empty results.rows[0].benchPosition}"> selected="selected"</c:if>>-- Select Bench Position --</option>
<option value="flat"<c:if test="${'flat' == results.rows[0].benchPosition}"> selected="selected"</c:if>>flat</option>
<option value="incline"<c:if test="${'incline' == results.rows[0].benchPosition}"> selected="selected"</c:if>>incline</option>
<option value="leg"<c:if test="${'leg' == results.rows[0].benchPosition}"> selected="selected"</c:if>>leg</option>
<option value="rowing"<c:if test="${'rowing' == results.rows[0].benchPosition}"> selected="selected"</c:if>>rowing</option>
<option value="squat"<c:if test="${'squat' == results.rows[0].benchPosition}"> selected="selected"</c:if>>squat</option>
</select>
</td>
</tr>

<%-- buttons --%>
<tr><td colspan="2">&nbsp;</td></tr>
<tr>
<th colspan="2">
<input type="button" value="Cancel"
onClick="spanel.getUpdater().getEl().update(' '); spanel.collapse()"/>&nbsp;
<input type="submit" value="Save"/>
</th>
</tr>
</table>
<c:if test="${empty param.id}">
<input type="hidden" name="exerid" value="0"/>
</c:if>
<c:if test="${not empty param.id}">
<input type="hidden" name="exerid" value="${param.id}"/>
</c:if>
</form>
</div>


And finally the external javascript in its entirety. Note that this could also be directly in the above file instead of external. This has been reformatted a tad to be easier to read in the forum space.



// exercise.js
function saveExerciseForm(id, name, bench)
{
loadPage(spanel, "saveExercise.jsp",
{
exerId:id,
exerciseName:name,
benchPosition:bench
});
}


Hopefully this makes sense. It is working well for me and hopefully might help someone else.

franckxx
8 Feb 2008, 8:49 AM
Really interesting way for javascript executed...
It can help many users...
Is it possible for you to purpose fully working exemple with files ?

Animal
11 Feb 2008, 3:24 AM
http://extjs.com/deploy/dev/docs/?class=Ext.Element&member=load

or

http://extjs.com/deploy/dev/docs/?class=Ext.Updater&member=update

luv2hike
11 Feb 2008, 7:46 AM
franckxx, i posted all i could without making the boss nervous for showing too much business logic. I tried my best to show all the contexts that make this work. The rest is pretty standard layout stuff.

Animal, I had previously tried both of those. The Element load did not execute any JavaScript in the external file. The update method did not either even with loadScripts:true specified. That is why I took the trouble to do this.

Animal
12 Feb 2008, 8:03 AM
I can assure you that this part of Ext works as documented and there is no need to add hacks.

luv2hike
12 Feb 2008, 8:26 AM
Well, it didn't work for me, whether that be due to a bug or misunderstanding on my behalf. Either way I needed it to work on the solution I coded (which I do not consider a hack) does work. I'd love to see a simpler way using the built-in functionality but haven't yet seen one that works. Plenty do load an external page, but do not execute javascript code in those external files. This is crucial to my application.

wantez
12 Feb 2008, 8:51 AM
Hi luv2hike,
I tried to load an external file (html) with js embedded, with the tab widget:


var tab = mainPanel.add( {
title : 'DICT08-000008-2007',
iconCls : 'tabs',
autoLoad : {
url : 'form.html',
text : "Carregant ...",
scripts : true
},
closable : true
});

form.html stripped:


<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ca" lang="ca">
<head>
<meta http-equiv="Pragma" content="no-cache" />
<meta http-equiv="Expires" content="-1" />
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Insert title here</title>
<style type="text/css">
.x-window-dlg .ext-mb-download {
background:transparent url(../sigc/assets/img/download.gif) no-repeat top left;
height:46px;
}
</style>
</head>
<body>
<div style="padding: 10px">
<div id="dummyForm"></div>
</div>
<script>
Ext.onReady(function(){
BlaBlaBlaBlaBlaBlaBlaBla[...]
});
</script>
</body>
</html>



This works out of the box :), js from form.html is executed when tab is show.

luv2hike
12 Feb 2008, 9:40 AM
People keep telling me this and I've learned a lot in the last 2 weeks of starting to use ExtJS, so I thought I'd give it another go to see maybe where my mistakes were. Firstly wantez, thanks for your input but your case is quite a bit different than mine. Still it got me thinking. Animal, I apologize if my previous reply sounded defensive. It was not meant to and I do appreciate all the time and effort folks give to help others on this forum.

Now time for me to eat crow. I have NO idea what I've done differently, as so many attempts and changes have been done during my learning curve with this terrific tool last week, but all I can say is IT WORKS NOW! No more using my own loadPage() routine but instead I use the standard panel.load() method with scripts:true. I know I tried this before but must've had vars out of scope or something like that which I've come to realize over time and tests.

Thank you to everyone for your patience and assistance. I'm posting relevant code here to show the solution that worked and maybe help some other newbie out.



<script type="text/javascript">
// these must have "global" scope to be used by loaded files
var spanel;
var workgrid;
var Exercise;
var grid;
var store;

function executeQuery()
{
spanel.expand();
spanel.load(
{
url:"respond.jsp",
params: { sqlQuery:document.queryForm.queryStr.value },
timeout:10
});
}

Ext.onReady(function()
{
<%-- load workouts --%>
<sql:query var="results">
select workoutID, workoutDate as `Date` from workouts order by workoutDate desc
</sql:query>

var workData = [
<%-- Get the value of each column while iterating over rows --%>
<c:forEach items="${results.rowsByIndex}" var="row" varStatus="status">
<c:if test="${!status.first}">
[${row[0]},'${row[1]}']<c:if test="${!status.last}">,</c:if>
</c:if>
</c:forEach>
];

var workReader = new Ext.data.ArrayReader({}, [
{name: 'workoutID', type: 'int'},
{name: 'workoutDate', type: 'date', dateFormat: 'Y-m-d'}
]);

workgrid = new Ext.grid.GridPanel({
store: new Ext.data.Store({
data: workData,
reader: workReader
}),
columns: [
{header: 'ID', width: 30, sortable: true, dataIndex: 'workoutID'},
{header: 'Workout Date', width: 80, sortable: true,
renderer: Ext.util.Format.dateRenderer('m/d/Y'),
dataIndex: 'workoutDate'}
],
viewConfig: {
forceFit: true
},
renderTo: 'work',
stripeRows: false,
frame: true,
tbar: [
{
id: 'plus',
text: 'Add Workout',
handler: function()
{
workgrid.getSelectionModel().clearSelections();
spanel.expand();
loadPage(spanel, "workoutDetails.jsp");
},
tooltip: 'Click to begin a new workout.'
}
]
});

workgrid.on('rowClick', function(thegrid, row, e)
{
var model = thegrid.getSelectionModel();
var rec = model.getSelected();
spanel.expand();
loadPage(spanel, "workoutDetails.jsp", { id:rec.get("workoutID") });
});

<%-- load exercises --%>
<sql:query var="results">
select * from exercises order by exerciseName
</sql:query>

var myData = [
<%-- Get the value of each column while iterating over rows --%>
<c:forEach items="${results.rowsByIndex}" var="row" varStatus="status">
<c:if test="${!status.first}">
[${row[0]},'${row[1]}','${row[2]}']<c:if test="${!status.last}">,</c:if>
</c:if>
</c:forEach>
];

var myReader = new Ext.data.ArrayReader({}, [
{name: 'exerID', type: 'int'},
{name: 'exerName'},
{name: 'bench'}
]);

Exercise = recdef = Ext.data.Record.create([
{name: 'exerID', type: 'int'},
{name: 'exerName'},
{name: 'bench'}
]);

store = new Ext.data.Store({
data: myData,
reader: myReader
});

grid = new Ext.grid.GridPanel({
store: store,
columns: [
{header: 'ID', width: 30, sortable: true, dataIndex: 'exerID'},
{header: 'Exercise Name', width: 120, sortable: true, dataIndex: 'exerName', editor: new Ext.form.TextField({ allowBlank: false })},
{header: 'Bench Position', width: 80, sortable: true, dataIndex: 'bench',
editor: new Ext.form.ComboBox({
editable: false,
triggerAction: 'all',
lazyInit: false,
lazyRender: true,
transform: 'benchposID'
})
}
],
viewConfig: {
forceFit: true
},
renderTo: 'exer',
stripeRows: false,
frame: true,
tbar: [
{
text: 'Add Exercise',
handler: function()
{
grid.getSelectionModel().clearSelections();
spanel.expand();
// THIS WORKS NOW
spanel.load({
url: "exerciseDetails.jsp",
scripts: true
});
},
tooltip: 'Click to add a new exercise.'
}
]
});

grid.on('rowClick', function(thegrid, row, e)
{
var model = thegrid.getSelectionModel();
var rec = model.getSelected();
spanel.expand();
// THIS WORKS TOO - DON'T KNOW WHY IT DIDN'T BEFORE
spanel.load({
url: "exerciseDetails.jsp",
params: { id:rec.get("exerID") },
scripts: true
});
});

var viewport = new Ext.Viewport(
{
layout:"border",
items:[
{
region:"north",
title:"Bowflex Workout Database",
bodyStyle:"background-color:#bacfff",
height:20
},
{
region:"east",
contentEl:'east',
title:" ",
width:200,
collapsible:true,
collapseMode: 'mini',
collapsed: true,
hideCollapseTool:true,
bodyStyle:"background-color:#bacfff",
split:true
},
new Ext.Panel({
region: 'center',
layout: 'border',
minWidth: 430,
items:[
spanel = new Ext.Panel({
region:"south",
bodyStyle:"background-color:#bacfff",
title:" ",
height:300,
autoScroll:true,
collapsible:true,
collapseMode: 'mini',
collapsed: true,
hideCollapseTool:true,
split:true,
}),
new Ext.TabPanel({
region:'center',
deferredRender:false,
minHeight: 150,
activeTab: 0,
items:[
// workouts tab grid
new Ext.Panel({
layout:'border',
title: 'Workouts',
items:[
new Ext.Panel({
layout: 'fit',
region: 'west',
width: 180,
items: [ workgrid ]
}),
{
region:'center',
bodyStyle:"background-color:#bacfff"
}
]
}),
// exercises tab grid
new Ext.Panel({
layout:'border',
title: 'Exercises',
items:[
new Ext.Panel({
layout: 'fit',
region: 'west',
width: 430,
items: [ grid ]
}),
{
region:'center',
bodyStyle:"background-color:#bacfff"
}
]
}),
{
contentEl:'query',
title: 'Query Viewer',
bodyStyle: "background-color:#bacfff",
autoScroll:true
}
]
})
]
}),
]
});

// Init the singleton. Any tag-based quick tips will start working.
Ext.QuickTips.init();

// Apply a set of config properties to the singleton
Ext.apply(Ext.QuickTips.getQuickTip(), {
showDelay: 50,
dismissDelay: 0,
animCollapse: false,
trackMouse: true,
mouseOffset: [10, 10]
});
});//end onReady function
</script>


Here is the body section of the main html file that includes the above script in its head:



<body>
<div id="east"></div>
<div id="work"></div>
<div id="exer"></div>

<div id="query">
<div>
<form name="queryForm">
<label style="vertical-align:top" for="queryStr">Enter Query: </label>
<textarea name="queryStr" rows="5" cols="50"></textarea>
<br/>
<input style="margin-top:1em; margin-left:18em" type="button" onClick="executeQuery()" value="Submit"/>
<input style="margin-top:1em; margin-left:2em" type="button" onClick="this.form.queryStr.value=''" value="Clear"/>
</form>
</div>
</div>
<!-- you must define the select box here, as the custom editor for the 'Light' column will require it -->
<select name="benchPosition" id="benchposID" style="display: none;">
<option value="">Select Bench Position</option>
<option value="flat">flat</option>
<option value="incline">incline</option>
<option value="leg">leg</option>
<option value="rowing">rowing</option>
<option value="squat">squat</option>
</select>
</body>
</html>


Now the first loaded file called when the user clicks a row to edit or the Add button:



<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/sql" prefix="sql" %>

<%-- get existing info if modifying --%>
<c:if test="${not empty param.id}">
<sql:query var="results" dataSource="bowflex">
select * from exercises where exerciseID=?
<sql:param value="${param.id}"/>
</sql:query>
</c:if>

<script type="text/javascript">
<!--
function checkExerciseForm()
{
// do input checks here
return true;
}

function saveExerciseForm(id, name, bench)
{
// spanel var defined in the main file
// This is what used to not work without using my own routine.
// It does now. Maybe I didn't have spanel in scope or
// maybe left off scripts:true for this call?
spanel.load({
url: "saveExercise.jsp",
params: { exerId:id, exerciseName:name, benchPosition:bench },
scripts: true
});
}
// -->
</script>

<%-- Page contents --%>
<div id="body">
<form name="mainform" action="javascript:saveExerciseForm(document.mainform.exerid.value, document.mainform.exerciseName.value, document.mainform.benchPosition.value)" onSubmit="return checkExerciseForm()">
<%-- overall page table --%>
<table style="border: medium solid black">
<tr>
<th align="right">Exercise&nbsp;Name&nbsp;:&nbsp;</th>
<td><input tabindex="1" type="text" name="exerciseName" maxlength="64" size="64" value="${results.rows[0].exerciseName}"/></td>
</tr>
<tr>
<th align="right">Bench&nbsp;Position&nbsp;:&nbsp;</th>
<td>
<select tabindex="2" name="benchPosition">
<option value=""<c:if test="${empty results.rows[0].benchPosition}"> selected="selected"</c:if>>-- Select Bench Position --</option>
<option value="flat"<c:if test="${'flat' == results.rows[0].benchPosition}"> selected="selected"</c:if>>flat</option>
<option value="incline"<c:if test="${'incline' == results.rows[0].benchPosition}"> selected="selected"</c:if>>incline</option>
<option value="leg"<c:if test="${'leg' == results.rows[0].benchPosition}"> selected="selected"</c:if>>leg</option>
<option value="rowing"<c:if test="${'rowing' == results.rows[0].benchPosition}"> selected="selected"</c:if>>rowing</option>
<option value="squat"<c:if test="${'squat' == results.rows[0].benchPosition}"> selected="selected"</c:if>>squat</option>
</select>
</td>
</tr>

<%-- buttons --%>
<tr><td colspan="2">&nbsp;</td></tr>
<tr>
<th colspan="2">
<input tabindex="15" type="button" value="Cancel" onClick="spanel.getUpdater().getEl().update(' '); spanel.collapse()"/>&nbsp;
<input tabindex="14" type="submit" value="Save"/>
</th>
</tr>
</table>
<c:if test="${empty param.id}">
<input type="hidden" name="exerid" value="0"/>
</c:if>
<c:if test="${not empty param.id}">
<input type="hidden" name="exerid" value="${param.id}"/>
</c:if>
</form>
</div>

<script type="text/javascript">
<!--
document.mainform.exerciseName.select();
// -->
</script>


And finally, the final JSP file that saves the data and updates the grid:



<sql:setDataSource dataSource="bowflex"/>

<%-- save info if passed in --%>
<c:if test="${not empty param.exerId}">
<sql:transaction>
<sql:update var="updateCount">
<c:if test="${param.exerId == '0'}">INSERT INTO</c:if><c:if test="${param.exerId != '0'}">UPDATE</c:if> exercises SET exerciseName=?, benchPosition=?<c:if test="${param.exerId != '0'}"> WHERE exerciseID=${param.exerId}</c:if>
<sql:param value="${param.exerciseName}"/>
<sql:param value="${param.benchPosition}"/>
</sql:update>
<c:if test="${param.exerId == '0'}">
<sql:query var="inserted">
select last_insert_id() as `newkey`
</sql:query>
</c:if>
</sql:transaction>
</c:if>

<%-- update the grid --%>
<script type="text/javascript">
// grid is defined in the main file
rsm = grid.getSelectionModel();

if(rsm.hasSelection())
{
// updating
row = rsm.getSelected();
row.beginEdit();
row.set("exerName", "${param.exerciseName}");
row.set("bench", "${param.benchPosition}");
row.endEdit();
}
else
{
// adding
// Exercise is defined in the main file
rec = new Exercise(
{
exerID: '${inserted.rows[0].newkey}',
exerName: '${param.exerciseName}',
bench: '${param.benchPosition}'
});
store.addSorted(rec);
}
store.commitChanges(); // removes the red triangles
</script>

hendricd
12 Feb 2008, 9:49 AM
@luv2hike -- was this scenario factored into your last post-example:



<script type="text/javascript" src="exercise.js"></script>
I didn't see it anywhere. That scenario is the one you originally had trouble with, no?

luv2hike
12 Feb 2008, 10:03 AM
You are correct. I moved the external script into the jsp file itself. However, I just moved it back and changed the JSP to use the src option again and it still works.

The script section of the first loaded file now looks like:


...
<script type="text/javascript" src="exercise.js"></script>
...


And for completeness sake, here is the now separate js file:



function checkExerciseForm()
{
// do input checks here
return true;
}

function saveExerciseForm(id, name, bench)
{
spanel.load({
url: "saveExercise.jsp",
params: { exerId:id, exerciseName:name, benchPosition:bench },
scripts: true
});
}


Again, both versions now work with the "out-of-the-box" methods, whether I have inline script or src an external script file.

hendricd
12 Feb 2008, 10:05 AM
Good to hear! =D>

vayumahesh
1 Aug 2008, 10:48 AM
Hi Luv2hike,

Would the code you posted work as-is if I create the necessary tables ? Where is the code for respond.jsp ? I was looking for extJs example with JSP/JSTL and this is the only one thread I found. If I can get this example to work, that would be a good starting point for me.

Do you have any other examples for Panel Layout etc., ?

Thanks
vmrao