I have an Ext app that is working for Firefox (OS/X, Windows, Linux) and in Opera on OS/X and Konqueror in Linux and IE in Windows, but that fails in Opera for Linux. I'm hoping someone here might know why or have a workaround I can add to my code to fix it. A summary of the issue:
1. Have a nested grid that is related to the expanded parent row
2. User clicks the Add button in the nested grid's toolbar
3. The form in the east region (spanel var) opens up and is empty (ok so far)
4. The user fills out the form and clicks Save
5. ERROR: At this point, it should save the form and update the nested grid via a reload to show the new row added that relates to the parent row. In all browsers but Opera Linux it does, Opera Linux gives no error, even in the console, but closes the form panel and does not update the grid. Upon looking at the database, it is adding the new info with a parent ID of 0 instead of the ID from the parent grid's expanded row. When I put an alert in at the point where the form is populated, the value is correct. So it appears the hidden field is not sent with the form request to the server for saving.
Again, this only happens in the one browser, but one we still wish to support. Any help is appreciated.
The nested and parent grid, stores, and readers:
store = new Ext.data.Store({
proxy: new Ext.data.MemoryProxy(myData),
reader: myReader,
autoLoad: true,
sortInfo: { field: "exerName", direction: "ASC" }
});
Ext.onReady(function()
{
<%-- load workouts --%>
select workoutID, workoutDate from workouts order by workoutDate desc
var workData = [
<%-- Get the value of each column while iterating over rows --%>
[${row[0]},'${row[1]}'],
];
// row expander
var expander = new Ext.grid.RowExpander(
{
tpl : new Ext.Template('')
});
// an item in the grid has been selected
function expandedRow(obj, record, body, rowIndex)
{
id = "myrow-" + record.get("workoutID");
id2 = "mygrid-" + record.get("workoutID"); Download Opera 10.0 Alpha Build 6186 / 9.63 Build 5261 - Experience the :: do not file bugs on English text in the UI when using another language for your interface. Track any satellite on your PC, Mac or Linux box. No Brake! 0.1.5 http://mac.softpedia.com/get/Internet-Utilities/Opera.shtmlHOME | dev.rubyonrails.org/report/21?format=csv&USER=anonymous:: Thanks for your work,Immoral 2,8193,Draggable Scrolling bug in Opera 9_ probably without issue using the latest versions.,phlipper 3,10756,bug Ajax.InPlaceEditor http://dev.rubyonrails.org/report/21?format=csv&USER=anonymousHOME |
var reader2 = new Ext.data.ArrayReader({},
[
{ name: 'workoutID' },
{ name: 'repID' },
{ name: 'exerciseID' },
{ name: 'exercise' },
{ name: 'bench' },
{ name: 'lifts' },
{ name: 'weight' }
]);
repsStore = new Ext.data.Store(
{
reader: reader2,
sortInfo: { field: "repID", direction: "ASC" },
autoLoad: true,
proxy: new Ext.data.HttpProxy(new Ext.data.Connection(
{
url: "getReps.jsp",
method: "POST",
extraParams: { id:record.get("workoutID") }
}))
});
var gridX = new Ext.grid.GridPanel(
{
store: repsStore,
columns: [
{ id:'idXX', header: 'ID', width: 50, sortable: true, dataIndex: 'repID' },
{ id: 'exer', header: 'Exercise', width: 120, sortable: true, dataIndex: 'exercise' },
{ header: 'Bench', width: 80, sortable: true, dataIndex: 'bench' },
{ header: 'Lifts', width: 60, sortable: true,
editor: new Ext.form.TextField({ allowBlank: false }),
dataIndex: 'lifts' },
{ header: 'Weight', width: 75, sortable: true,
editor: new Ext.form.TextField({ allowBlank: false }),
dataIndex: 'weight' }
],
viewConfig: {
forceFit:true
},
autoExpandColumn: 'exer',
width: 370,
height: 250,
id: id2,
title: 'Details for '+record.get("workoutDate").format('m/d/Y'),
renderTo: body,
frame: true,
tbar: [
{
text: 'Add Set',
tooltip: 'Click to add a new set.',
iconCls: 'add',
handler: function()
{
gridX.getSelectionModel().clearSelections();
spanel.remove(mp, false);
spanel.getUpdater().getEl().dom.innerHTML = ' ';
spanel.setTitle("Add a Set");
spanel.add(repDetailsPanel);
spanel.expand();
spanel.doLayout();
repDetailsForm.getForm().setValues({ workoutID: record.get("workoutID"), repID: 0, exerciseID: '', weight: '', lifts: '' });
}
},
{
text: ' ', // These are needed to force IE to left-justify the Add button.
hideMode: 'visibility',
hidden: true
},
{
text: ' ',
hideMode: 'visibility',
hidden: true
}]
});
gridX.on('rowClick', function(thegrid, row, e)
{
spanel.remove(mp, false);
spanel.getUpdater().getEl().dom.innerHTML = ' ';
spanel.setTitle("Edit Set Details");
spanel.add(repDetailsPanel);
spanel.expand();
spanel.doLayout();
repDetailsForm.getForm().loadRecord(thegrid.getSto re().getAt(row));
});
gridX.render(id);
gridX.getEl().swallowEvent(['mousedown', 'click', 'dblclick']);
}
expander.on("expand", expandedRow);
var Workout = Ext.data.Record.create([
{ name: 'workoutID', type: 'int' },
{ name: 'workoutDate', type: 'date', dateFormat: 'Y-m-d' },
{ name: 'filler' }
]);
var workReader = new Ext.data.ArrayReader({}, Workout);
var workStore = new Ext.data.Store({
data: workData,
reader: workReader,
sortInfo: { field: "workoutDate", direction: "DESC" }
});
workgrid = new Ext.grid.EditorGridPanel({ 42693: Opera Rich Text Editing Functionality designMode Cross-domain :: http://www.opera.com/docs/changelogs/linux/925/ http://www.opera.com/docs Use of the information constitutes acceptance for use in an AS IS condition, and http://osvdb.org/42693HOME |
store: workStore,
columns: [
expander,
{ id: 'Id', header: 'ID', width: 50, sortable: true, dataIndex: 'workoutID' },
{ header: 'Workout Date', width: 120, sortable: true,
editor: new Ext.form.DateField({ allowBlank: false }),
renderer: Ext.util.Format.dateRenderer('m/d/Y'),
dataIndex: 'workoutDate' },
{ id: 'filler', header: ' ', width: 180, hideable: false, resizable: false, sortable: false, dataIndex: 'filler' }
],
viewConfig: {
forceFit: true
},
autoExpandColumn: 'filler',
renderTo: 'work',
stripeRows: false,
trackMouseOver: true,
frame: true,
clicksToEdit: 1,
plugins: expander,
tbar: [
{
text: 'Add Workout',
tooltip: 'Click to begin a new workout.',
iconCls: 'add',
handler: function()
{
var w = new Workout(
{
workoutID: 0,
workoutDate: new Date()
});
workgrid.stopEditing();
workStore.insert(0, w);
workgrid.startEditing(0, 2);
}
}, '-',
{
text: 'Save Changes',
tooltip: 'Click to save all changes.',
iconCls: 'save',
handler: function()
{
workStore.commitChanges();
}
}
]
});
workStore.on('update', function(thestore, record, operation)
{
if(operation == Ext.data.Record.COMMIT)
{
var sortstate = thestore.getSortState();
Ext.Ajax.request(
{
url: "saveWorkout.jsp",
params: { workoutId: record.get("workoutID"), workoutDate: Ext.util.Format.date(record.get("workoutDate"), "Y-m-d") },
success: function(response, options)
{
record.data.workoutID = response.responseText;
thestore.sort(sortstate.field, sortstate.direction);
}
});
}
});
The form panel:
repDetailsForm = new Ext.form.FormPanel(
{
region: 'north',
height: 200,
url: 'saveRep.jsp',
method: 'POST',
onSubmit: Ext.emptyFn,
monitorValid: true,
defaultType: 'numberfield',
labelAlign: "top",
buttonAlign: "left",
bodyStyle: blankBgStyle,
items: [
{
name: 'workoutID',
xtype: 'hidden'
},
{
name: 'repID',
xtype: 'hidden'
},
{
fieldLabel: 'Exercise',
hiddenName: 'exerciseID',
tabIndex: 1,
id: 'exCombo',
xtype: 'combo',
forceSelection: true,
typeAhead: true,
triggerAction: 'all',
selectOnFocus: true,
mode: 'local',
store: store,
valueField: 'exerID',
displayField: 'exerName'
},
{
fieldLabel: 'Weight',
name: 'weight',
allowBlank: false,
allowDecimals: false,
allowNegative: false,
maxLength: 3,
selectOnFocus: true,
tabIndex: 2,
validationDelay: 100,
width: 32
},
{
fieldLabel: 'Reps',
name: 'lifts',
allowBlank: false,
allowDecimals: false,
allowNegative: false,
maxLength: 2,
selectOnFocus: true,
validationDelay: 100,
tabIndex: 3,
width: 25
}
],
buttons: [
{
text: 'Save',
tooltip: 'Saves your changes',
formBind: true,
handler: function()
{
repDetailsForm.getForm().submit(
{
waitMsg: 'Saving...',
success: function(f, action)
{
clearSPanel();
repsStore.reload();
}
});
}
},
{
text: 'Cancel',
tooltip: 'Cancels your changes and closes the form',
handler: function()
{
clearSPanel();
}
}]
});
repDetailsPanel = new Ext.Panel(
{
layout: 'border',
width: tabPanelWidth,
bodyStyle: blankBgStyle,
items: [
repDetailsForm,
new Ext.Panel({
region: 'center',
bodyStyle: blankBgStyle
})
]
});
Ext.getCmp('exCombo').on('blur', function() { store.clearFilter(); });
The save JSP (once prototyping is done, this will be a Java servlet):
<%@ page session="false" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/sql" prefix="sql" %>
<%-- save info if passed in --%>
INSERT INTOUPDATE reps SET workoutID=?, exerciseID=?, lifts=?, weight=? WHERE repID=${param.repID}
{success:true}
Does anyone know how I can debug this in Opera? I installed their Developer tools but after using Firebug, they really blow.
Another Opera issue that I may have resolved but would like confirmation or at least an explanation of what might be happening and why the fix worked. I have a parent grid with expandable rows. When you expand, an ajax call is made to get the data for the store used by the inner (or nested) grid with details for that row. This worked the first time in opera after reloading the page, but not again after that until the page was reloaded. Again, for all other browsers - no problem.
Now my fix was to remove the autoLoad:true config option from the inner grid's store and instead explicitly call its load({}) method in the row click handler of the parent just before rendering the nested grid.
Any ideas why autoLoad would only work the first time a row is clicked using opera? Does the "fix" of using a direct call to load instead make sense and is the correct answer? I guess I'm a little gun shy to plow ahead with a solution without knowing why it is or what the problem really was.
Here are the relevant sections of code with the "opera" changes highlighted in RED. Any insights on why this worked or a problem still present would be most appreciated!
// row expander
var expander = new Ext.grid.RowExpander(
{
tpl : new Ext.Template('')
});
// an item in the grid has been selected
function expandedRow(obj, record, body, rowIndex)
{
id = "myrow-" + record.get("workoutID");
id2 = "mygrid-" + record.get("workoutID");
var reader2 = new Ext.data.ArrayReader({},
[
{ name: 'workoutID' },
{ name: 'repID' },
{ name: 'exerciseID' },
{ name: 'exercise' },
{ name: 'bench' },
{ name: 'lifts' },
{ name: 'weight' }
]);
// the inner grid's store
repsStore = new Ext.data.Store(
{
reader: reader2,
sortInfo: { field: "repID", direction: "ASC" },
//autoLoad: true, //had to comment this option out for opera to work consistently
proxy: new Ext.data.HttpProxy(new Ext.data.Connection(
{
url: "getReps.jsp",
method: "POST",
extraParams: { id:record.get("workoutID") }
}))
});
var gridX = new Ext.grid.GridPanel(
{
store: repsStore,
columns: [
{ id:'idXX', header: 'ID', width: 50, sortable: true, dataIndex: 'repID' },
{ id: 'exer', header: 'Exercise', width: 120, sortable: true, dataIndex: 'exercise' },
{ header: 'Bench', width: 80, sortable: true, dataIndex: 'bench' },
{ header: 'Lifts', width: 60, sortable: true,
editor: new Ext.form.TextField({ allowBlank: false }),
dataIndex: 'lifts' },
{ header: 'Weight', width: 75, sortable: true,
editor: new Ext.form.TextField({ allowBlank: false }),
dataIndex: 'weight' }
],
viewConfig: {
forceFit:true
},
autoExpandColumn: 'exer',
width: 370,
height: 250,
id: id2,
title: 'Details for '+record.get("workoutDate").format('m/d/Y'),
renderTo: body,
frame: true,
tbar: [
{
text: 'Add Set',
tooltip: 'Click to add a new set.',
iconCls: 'add',
handler: function()
{
gridX.getSelectionModel().clearSelections();
spanel.remove(mp, false);
spanel.getUpdater().getEl().dom.innerHTML = ' ';
spanel.setTitle("Add a Set");
spanel.add(repDetailsPanel);
spanel.expand();
spanel.doLayout();
repDetailsForm.getForm().setValues({ workoutID: record.get("workoutID"), repID: 0, exerciseID: '', weight: '', lifts: '' });
}
},
{
text: ' ', // These are needed to force IE to left-justify the Add button.
hideMode: 'visibility',
hidden: true
},
{
text: ' ',
hideMode: 'visibility',
hidden: true
}]
});
gridX.on('rowClick', function(thegrid, row, e)
{
spanel.remove(mp, false);
spanel.getUpdater().getEl().dom.innerHTML = ' ';
spanel.setTitle("Edit Set Details");
spanel.add(repDetailsPanel);
spanel.expand();
spanel.doLayout();
repDetailsForm.getForm().loadRecord(thegrid.getSto re().getAt(row));
});
// the next line was added to make opera work consistently
repsStore.load({});
// show the inner grid
gridX.render(id);
gridX.getEl().swallowEvent(['mousedown', 'click', 'dblclick']);
}
expander.on("expand", expandedRow);
Well for some reason the Developer Tools starting working in Opera (maybe restarting helped?) so I was able to debug this one and solve it. I still find it strange that only 9.26 in Linux had the issue but evidently other browsers are more forgiving with Ajax/JSON responses. Turns out, it was my parent grid row saving JSP that was the culprit. It returned the new row ID as plain text without the {success:true} and being properly wrapped. This worked in FF as I was just setting the grid cell with response.responseText. So now it is JSON-ified and the response is placed in an eval() statement. This still works in FF and makes Opera happy. Long story short, hopefully this finding will help someone else from struggling.
The new commit routine for the parent grid:
workStore.on('update', function(thestore, record, operation)
{
if(operation == Ext.data.Record.COMMIT)
{
var sortstate = thestore.getSortState();
Ext.Ajax.request(
{
url: "saveWorkout.jsp",
params: { workoutId: record.get("workoutID"), workoutDate: Ext.util.Format.date(record.get("workoutDate"), "Y-m-d") },
success: function(response, options)
{
// the following 2 lines were part of the fix
var r = eval("("+response.responseText+")");
record.data.workoutID = r.data;
thestore.sort(sortstate.field, sortstate.direction);
}
});
}
});
And the new return code in the JSP that saves the new parent row:
(whereas before it just returned the text ${inserted.rows[0].newkey})
{success:true, data:"${inserted.rows[0].newkey}"}
I Am a Sinner – What About You?
Global Sourcing and Supplier Online by Dylan
|