PDA

View Full Version : New: Ext.ux.FormPanelEditor (w/ EditableGridPanel update)



SMMJ_Dev
23 Apr 2010, 5:44 AM
DEMO: http://www.djengineer.com/ExtJSExamples/formpaneleditorexample.html

Now upgraded for ExtJS 4.0 as well: Form Panel Editor Example (ExtJS 4.0) (http://www.djengineer.com/ExtJSExamples/PluginExamples/FormPanelEditor/FormPanelEditor.html)

Good Morning/Afternoon everyone,

I have deleted my previous post with a cell editor and form panel editor combo, for a new Ext.ux.FormPanelEditor that allows you to have a form panel as your editor for the EditableGridPanel. Also, I was having trouble with textareas in the EditableGridPanel not taking up the entire cell row and height all the time. So I put a little extra stuff in the EditableGridPanel as well.

First of all, let's talk about the overrides!

Ext.grid.EditorGridPanel Overrides


Ext.override(Ext.grid.EditorGridPanel,{
onEditComplete:function(ed, value, startValue){
this.editing=false;
this.lastActiveEditor=this.activeEditor;
this.activeEditor=null;
var r=ed.record,field=this.colModel.getDataIndex(ed.col);
value=this.postEditValue(value,startValue,r,field);
if(this.forceValidation===true||String(value)!==String(startValue)){
var e={
grid:this,
record:r,
field:field,
originalValue:startValue,
value:value,
row:ed.row,
column:ed.col,
cancel:false
};
if(this.fireEvent("validateedit",e)!==false&&!e.cancel&&String(value)!==String(startValue)){
if(ed.field.getXType()!="formeditor"){
r.set(field,e.value);
}
delete e.cancel;
this.fireEvent("afteredit",e);
}
}
this.view.focusCell(ed.row,ed.col);
},
startEditing : function(row, col){
this.stopEditing();
if(this.colModel.isCellEditable(col, row)){
this.view.ensureVisible(row, col, true);
var r = this.store.getAt(row),
field = this.colModel.getDataIndex(col),
e = {
grid: this,
record: r,
field: field,
value: r.data[field],
row: row,
column: col,
cancel:false
};
if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
this.editing = true;
var ed=this.colModel.getCellEditor(col,row);
if(!ed){return;}
/**
* Update height and width of the editor
*/
var colWidth=e.grid.getColumnModel().getColumnAt(col).width;
var rowHeight=(ed.field.fillRowHeight)?this.view.getCell(row,col).offsetHeight:"";
if(ed.field.minWidth){colWidth=(colWidth>=ed.field.minWidth)?colWidth:ed.field.minWidth;}
if(ed.field.maxWidth){colWidth=(colWidth<=ed.field.maxWidth)?colWidth:ed.field.maxWidth;}
if(ed.field.minHeight){rowHeight=(rowHeight>=ed.field.minHeight)?rowHeight:ed.field.minHeight;}
if(ed.field.maxHeight){rowHeight=(rowHeight<=ed.field.maxHeight)?rowHeight:ed.field.maxHeight;}
ed.setSize(colWidth,rowHeight);
if(!ed.rendered){
ed.parentEl = this.view.getEditorParent(ed);
ed.on({
scope: this,
render: {
fn: function(c){
c.field.focus(false, true);
},
single: true,
scope: this
},
specialkey: function(field, e){
this.getSelectionModel().onEditorKey(field, e);
},
complete: this.onEditComplete,
canceledit: this.stopEditing.createDelegate(this, [true])
});
}
Ext.apply(ed, {
row : row,
col : col,
record : r
});
this.lastEdit = {
row: row,
col: col
};
this.activeEditor = ed;
// Set the selectSameEditor flag if we are reusing the same editor again and
// need to prevent the editor from firing onBlur on itself.
ed.selectSameEditor = (this.activeEditor == this.lastActiveEditor);
var v = this.preEditValue(r, field);
ed.startEdit(this.view.getCell(row, col).firstChild, Ext.isDefined(v) ? v : '');
if(ed.field.getXType()=="formeditor"){
var scrollerBox=this.view.scroller.getBox();
var editorBox=ed.getEl().getBox();
var scrollTop=this.view.scroller.dom.scrollTop;
if(editorBox.bottom>=(scrollerBox.y+scrollerBox.height)){
this.view.scroller.dom.scrollTop=editorBox.bottom-(scrollerBox.y+scrollerBox.height)+scrollTop;
}
}
// Clear the selectSameEditor flag
(function(){
delete ed.selectSameEditor;
}).defer(50);
}
}
}
});
(On the onEditComplete function, the value in the record was being set incorrectly to how the FormPanelEditor works. On the startEditing function, I have added some code to allow me to have a textarea editor fill up the entire contents of cell. I also added the ability to set minimum and maximum width's and height's on the editors.)

Ext.Editor Overrides


Ext.override(Ext.Editor,{
startEdit:function(el,value){
if(this.editing){this.completeEdit();}
this.boundEl=Ext.get(el);
if(this.field.getXType()=="formeditor"){
var buildValue="";
this.field.items.each(function(item,idx){
buildValue+=(idx==0)?this.record.get(item.dataIndex):this.field.delimiter+this.record.get(item.dataIndex);
},this);
var v=buildValue;
}
else{var v=value!==undefined?value:this.boundEl.dom.innerHTML;}
if(!this.rendered){this.render(this.parentEl||document.body);}
if(this.fireEvent("beforestartedit", this, this.boundEl, v) !== false){
this.startValue = v;
this.field.reset();
this.field.setValue(v);
this.realign(true);
this.editing = true;
this.show();
}
},
onHide:function(){
if(this.editing){
this.completeEdit();
return;
}
this.field.blur();
if(this.field.getXType()!="formeditor"){
if(this.field.collapse){this.field.collapse();}
}
this.el.hide();
if(this.hideEl !== false){
this.boundEl.show();
}
},
clkTarget:function(e){
var clkTarget=false;
var target=Ext.get(e.getTarget());
if(e.getXY()[0]>=this.el.getXY()[0]&&e.getXY()[1]>=this.el.getXY()[1]){
if(e.getXY()[0]<=this.el.getXY()[0]+this.el.getSize().width&&e.getXY()[1]<=this.el.getXY()[1]+this.el.getSize().height){
clkTarget=true;
}
else if(target.hasClass("x-combo-list-item")||target.hasClass("x-combo-list-inner")||target.findParent("a.x-date-date")){clkTarget=true;}
else{clkTarget=false;}
}
else if(target.hasClass("x-combo-list-item")||target.hasClass("x-combo-list-inner")||target.findParent("a.x-date-date")){clkTarget=true;}
else{clkTarget=false;}
if(!clkTarget){
Ext.getBody().un("mousedown",this.clkTarget,this);
this.completeEdit();
return;
}
},
onShow:function(){
if(this.field.getXType()=="formeditor"){this.hasClickTarget=true;Ext.getBody().on("mousedown",this.clkTarget,this);}
this.el.show();
if(this.hideEl !== false){
this.boundEl.hide();
}
if(this.field.getXType()!="formeditor"){this.field.show().focus(false, true);}
else{this.field.items.items[0].focus(false,true);}
this.fireEvent("startedit", this.boundEl, this.startValue);
},
completeEdit:function(remainVisible){
if(!this.editing){return;}
if (this.field.assertValue){this.field.assertValue();}
var v=this.getValue();
if(!this.field.isValid()){
if(this.revertInvalid!==false){this.cancelEdit(remainVisible);}
return;
}
if(String(v)===String(this.startValue)&&this.ignoreNoChange){
this.hideEdit(remainVisible);
return;
}
if(this.fireEvent("beforecomplete",this,v,this.startValue)!==false){
v=this.getValue();
if(this.field.getXType()=="formeditor"){
this.field.items.each(function(item,idx){
this.record.set(item.dataIndex,v.split(this.field.delimiter)[idx]);
},this);
}
else{
if(this.updateEl&&this.boundEl){this.boundEl.update(v);}
}
this.hideEdit(remainVisible);
this.fireEvent("complete",this,v,this.startValue);
}
}
});
(The startEdit function was modified to pre-populate the values in the FormPanelEditor based upon their dataIndex of each field instead of what is in the cell. The onHide function was modified because the FormPanel of the FormPanelEditor was being collapsed and never reshown. I didn't know what the need to even collapse the FormPanel was for the FormPanelEditor, so I left it out for this editor. The onShow function was changed to focus the first field of the FormPanel. There is also an onmousedown event added to the body to check when the FormPanelEditor looses focus. The completeEdit function was changed to correctly update the store with values inside of the FormPanel with their associated dataIndex values.)

Ext.ux.form.FormPanelEditor


/**
* @class Ext.ux.FormPanelEditor
* @extends Ext.form.FormPanel
* Form panel editor
* @author Phillip W. Moore
* @date 04/20/2010
* @version 1.0
*/
Ext.ux.FormPanelEditor=Ext.extend(Ext.form.FormPanel,{
initComponent:function(){
Ext.ux.FormPanelEditor.superclass.initComponent.call(this);
},
initValue:function(){
if(this.value !== undefinded){
this.setValue(this.value);
}else if(!Ext.isEmpty(this.el.dom.value)&&this.el.dom.value!=this.emptyText){
this.setValue(this.el.dom.value);
}
this.originalValue=this.getValue();
},
getValue:function(){
var v=[];
this.items.each(function(item,idx){
if(item.showRawValue){v.push(item.getRawValue());}
else{v.push(item.getValue());}
},this);
return v.join(this.delimiter);
},
getRawValue:function(){
var v=[];
this.items.each(function(item,idx){
v.push(item.getRawValue());
},this);
return v.join(this.delimiter);
},
setValue:function(v){
v=v.split(this.delimiter);
this.items.each(function(item,idx){
item.setValue(v[idx]);
},this);
},
setRawValue:function(v){
v=v.split(this.delimiter);
this.items.each(function(item,idx){
this.setRawValue(v[idx]);
},this);
},
isValid:function(){
var isValid=true;
this.items.each(function(item,idx){
if(!item.isValid()){
isValid=false;
}
},this);
return isValid;
},
isDirty:function(){
if(this.disabled||!this.rendered){return false;}
return String(this.getValue()!==String(this.originalValue));
},
setReadOnly:function(readOnly){
if(this.rendered){
this.items.each(function(item,idx){
item.setReadOnly(readOnly);
},this);
}
},
processValue:function(value){
return value;
},
reset:function(){
this.items.each(function(item,idx){
item.setValue(item.originalValue);
item.clearInvalid();
},this);
}
});
Ext.reg('formeditor',Ext.ux.FormPanelEditor);
The FormPanel was extended to become a FormPanelEditor. I have added functions that are like form fields to change the FormPanel into a type of field to be used as an editor.

If you have any questions or modifications, just let me know.

Here's an example of how to set the editor:


editor:{
xtype:'formeditor',
id:'formpaneleditor',
padding:'5',
defaults:{width:170},
editable:false,
minWidth:170,
defaultType:'textfield',
delimiter:'~#~',
items:[{
name:'category',
id:'category',
hideLabel:true,
dataIndex:'category',
style:'text-align:left;'
},{
xtype:'textfield',
name:'entry',
id:'entry',
hideLabel:true,
dataIndex:'entry',
style:'text-align:left;'
}]
}
Updates
4/26/2010: Fix to the Ext.grid.EditorGridPanel override on the startEditing function. Instead of

var rowHeight=(ed.field.fillRowHeight)?this.view.getCell(row,col).offsetHeight:null;this has been replaced with

var rowHeight=(ed.field.fillRowHeight)?this.view.getCell(row,col).offsetHeight:"";to fix rendering errors in IE.
4/26/2010: Update added to the Ext.editor override on the onShow function. Added new clkTarget function.
4/30/2010: Update to the Ext.grid.EditorGridPanel to the startEditing function. The update will automatically scroll the grid to make sure the FormPanelEditor is in the viewing area when displaying the editor.
5/17/2010: Update to the Ext.editor override on the startEdit function. Changed the delimiter from a "," to a formpaneleditor specified delimiter.
5/17/2010: Update to the Ext.editor override on the clkTarget function. Changed the clkTarget function. The clkTarget function is used to see if we have clicked somewhere on the Ext.ux.FormPanelEditor or not. If we have not clicked on the editor, then changes to the editor are saved and the editor will hide. If we have clicked on the editor, then the editor will remain open. I have added special code to see if the selected target was a combo list item or part of the scrollbar. These items can appear outside the area of the form panel editor when expanded. This issue was found by the testers at our company and by flipthefrog.
5/17/2010: Update to the Ext.editor override on the completeEdit function. Changed the delimiter from a "," to a formpaneleditor specified delimiter.

5/17/2010: Update to the Ext.ux.FormPanelEditor extend on almost all functions. Changed the delimiter from a "," to a formpaneleditor specified delimiter. Also removed escape and unescape calls to prevent odd store value saving.

5/31/2010: Update to Ext.ux.FormPanelEditor on the clkTarget functions. Bug & Solution found by flipthefrog (http://www.extjs.com/forum/member.php?16987-flipthefrog). Added target.findParent('a.x-date-date') to check to see if you are still clicking w/in the FormPanelEditor to avoid the panel editor from closing unexpectedly.

SMMJ_Dev
23 Apr 2010, 6:25 AM
Just wanted to add a screenshot of an example of the form panel editor in action. This particular form panel editor has a custom combobox and a text area. I have hidden the labels, and there is no title to the FormPanel.

SMMJ_Dev
26 Apr 2010, 10:43 AM
I have made a couple of updates today. The first update was to the override of the Ext.grid.EditorGridPanel to the startEditing function. IE was having problems with setting a rowHeight of null when no height is specified. I changed it to an empty string, and IE and FF play well now.

The second change I made was to the override of the Ext.editor to the onShow function. I have added an onmousedown event to the body that checks to see if the FormPanelEditor has lost it's focus. The new clkTarget function determines if the focus is lost, removes the onmousedown event, and then complete's the edit of the FormPanelEditor.

SMMJ_Dev
28 Apr 2010, 4:34 AM
Is there a sample for us ?

I can get one together for you. I'll reply after I have a public version ready.

SMMJ_Dev
28 Apr 2010, 6:31 AM
I now have a demo up. There's a couple different grids I am showing just to show you that the styles for the FormPanelEditor should follow the same styles applied to a Form Panel.

SMMJ_Dev
30 Apr 2010, 4:26 PM
I have made another update to the Ext.grid.EditorGridPanel to the startEditing function. The update will automatically scroll the grid to make sure the FormPanelEditor is in the viewing area when displaying the editor. This will fix the issue with not being able to edit the last item in the grid. When you would scroll to see the FormPanelEditor, the EditorGridPanel would hide the FormPanelEditor making it impossible to edit the last item. I have the code updated and the demo has been updated.

flipthefrog
14 May 2010, 5:40 AM
Thank you so much. Ive been wishing for this feature in Ext for a long time.

One problem though. If you have a combo that extends below the editor area and select one of the items outside that area, Editor.clkTarget thinks that you've clicked outside the edit area, because the mouse is indeed outside the editor, and calls Editor.completeEdit. I guess you would have to check if the current editor is a combo and then see if the mouse is still inside its xy coordinates. But at that point I just break down crying because I cant figure out how to

Thanks again for your extension. I do hope this is fixable

SMMJ_Dev
14 May 2010, 5:43 AM
I have an updating coming out soon for this. There is something screwy going on with the escaping of characters. So instead of escaping and splitting on commas, I am adding a delimiter option to the FormPanelEditor so you can specify what to split on to avoid the escape issue when saving data to the store and reading data from the store. I'll have it out by Monday 5/17/2010

SMMJ_Dev
14 May 2010, 5:46 AM
Thank you so much. Ive been wishing for this feature in Ext for a long time.

One problem though. If you have a combo that extends below the editor area and select one of the items outside that area, Editor.clkTarget thinks that you've clicked outside the edit area, because the mouse is indeed outside the editor, and calls Editor.completeEdit. I guess you would have to check if the current editor is a combo and then see if the mouse is still inside its xy coordinates. But at that point I just break down crying because I cant figure out how to

Thanks again for your extension. I do hope this is fixable

I do have a fix for this. This will be available Monday. I can't believe nobody created this before. I needed this functionality for work, so I ended up creating but wishing it was already created, lol.

SMMJ_Dev
17 May 2010, 7:23 AM
Good Morning flipthefrog and everyone else,

I have made the corrections to the Ext.ux.FormPanelEditor site. You can either get the code from above or get the code from the demo site: http://www.djengineer.com/ExtJSExamples/formpaneleditorexample.html

flipthefrog
30 May 2010, 1:44 PM
Sorry about the late reply. Just wanted to say thanks.

flipthefrog
30 May 2010, 5:08 PM
Found a bug: When the editor is a datefield and you select a date in the calendar widget, the panel closes because, again, the target is outside the panel's boundary.

Adding this to the clickTarget method fixes it (as far as I can tell, anyway):


...
else if
((target.hasClass("x-combo-list-item") || target.hasClass("x-combo-list-inner") || target.findParent('a.x-date-date')){
clkTarget=true;
}
// and the same a few lines further down

SMMJ_Dev
31 May 2010, 2:35 PM
Thanks flipthefrog! I'll add that to the code. I never tested with a calendar!