PDA

View Full Version : Custom editor field in EditorGridPanel



josephhamilton1
31 May 2011, 4:21 AM
Basically, I'm trying to create a custom editor field in a GridEditorPanel.

The input will be like this: "HH:MM-HH:MM;HH:MM-HH:MM;SUNRISE-SUNSET". (time schedules)

And I've written code already to validated and parse the string out into individual tokens, I just need to know how to generate a pair of textboxes per "-" delimited pair of times.

Like: starttime (textbox) endtime (textbox)

Any help will be greatly appreciated!

skirtle
31 May 2011, 8:15 AM
That could prove to be a little tricky. I think you may need to write your own extension of Ext.form.Field to do what you want. Ext.form.CompositeField may help for some inspiration on how to do it.

Any chance you could treat the 2 times as separate cells and edit them separately?

josephhamilton1
31 May 2011, 9:23 AM
Yeah, that's exactly what I want to do. To have 2 pairs of textboxes per time pair

Like:

StartTime (textbox) EndTime (textbox)

<button>Add New Time</button>

josephhamilton1
2 Jun 2011, 11:54 AM
I managed to write a custom onRender event which creates my custom editing interface with 2 textboxes per time string delimited by ";".

The issue I have now is that once rendered the custom control retains all of it state EVEN if I click on another cell in the grid. Its like the HTML/Ext objects are being cached. This may be by design in Ext, I just need to know how to override it to get a fresh object when clicking on a different cell in the grid.

Any help would be greatly appreciated! Thanks!

Here's my custom ScheduleEditor:



Ext.ux.ScheduleEditor = Ext.extend(Ext.form.TextField, {
relativeTimes: { SUNSET: 'SUNSET', SUNRISE: 'SUNRISE' },
initComponent: function () {
// call parent initComponent
Ext.ux.ScheduleEditor.superclass.initComponent.call(this);
}, // end of function initComponent
onBlur: function () { }, //used to prevent the closing of the popup
onRender: function (ct, position) {
Ext.ux.ScheduleEditor.superclass.onRender.call(this, ct, position);
var scheduleString = this.gridEditor.record.data.scheduleString;
var timePairs = scheduleString.split(";");

var scheduleEditorWrapper = document.createElement("div");
scheduleEditorWrapper.id = "ScheduleEditorWrapperDiv";
scheduleEditorWrapper.setAttribute("class", "ScheduleEditorWrapper");

for (i = 0; i < timePairs.length; i++) {
var times = timePairs[i].split("-");
var rowDiv = document.createElement('div');
scheduleEditorWrapper.appendChild(rowDiv);
CreateTimePair(rowDiv, times[0], times[1], i);
}
var applyButton = document.createElement('button');
applyButton.innerHTML = "Apply";
scheduleEditorWrapper.appendChild(applyButton);

var ab = new Ext.Element(applyButton);
var currentContext = this;
ab.on('click', function () {
var newScheduleString = '';
var timeSeparator = '-';
var timePairSeparator = ';';

//loop through all textbox pairs
var numTextBoxes = GetNumTextBoxes();
for (i = 0; i < (numTextBoxes / 2); i++) {
//concatenate each value using a '-'
var startTime = window['StartTime' + i.toString()].value;
var endTime = window['EndTime' + i.toString()].value;
//concatenate each pair using a ';'
newScheduleString += startTime + timeSeparator + endTime + timePairSeparator;
}
var currentRecord = currentContext.gridEditor.record;
currentRecord.set('scheduleString', newScheduleString.slice(0, newScheduleString.length - 1));
currentContext.container.dom.style["visibility"] = "hidden";
Ext.ux.ScheduleEditor.superclass.onBlur.call(currentContext, ct, position);
});

var addTimeButton = document.createElement('button');
addTimeButton.innerHTML = "Add Schedule";
scheduleEditorWrapper.appendChild(addTimeButton);

var aTB = new Ext.Element(addTimeButton);
aTB.on('click', function () {
var rowDiv = document.createElement('div');
scheduleEditorWrapper.insertBefore(rowDiv, ab);
var numTextBoxes = GetNumTextBoxes();
CreateTimePair(rowDiv, '', '', numTextBoxes + 1);
});

var cancelButton = document.createElement('button');
cancelButton.innerHTML = "Cancel";
scheduleEditorWrapper.appendChild(cancelButton);

var cb = new Ext.Element(cancelButton);
cb.on('click', function () {
currentContext.container.dom.style["visibility"] = "hidden";
Ext.ux.ScheduleEditor.superclass.onBlur.call(currentContext, ct, position);
});

this.container.dom.style["overflow-y"] = "visible";
this.container.dom.style["overflow-x"] = "visible";
this.container.dom.style["width"] = "600px";
this.container.appendChild(scheduleEditorWrapper);
this.rendered = false;
}
});

// register xtype
Ext.reg('ScheduleEditor', Ext.ux.ScheduleEditor);

josephhamilton1
2 Jun 2011, 12:49 PM
I've read and noticed in other posts that the render event only fires once.

Which event will fire each time a cell in a grid is clicked assuming you have a custom editor field on that cell?

Is there a way to make the render event fire each time the editor cell is clicked?

skirtle
2 Jun 2011, 9:52 PM
Indeed, the editor field will be reused and the render event will only fire the first time.

If you take a look at the source for EditorGridPanel you'll see quite a few places you can hook in. For example, the panel itself has a beforeedit event that may be of use to you.

More likely though you'll need to implement an override of the setValue() method. This gets called on the field when a cell enters edit mode. Similarly you'll need a getValue() method for when editing is complete.

josephhamilton1
3 Jun 2011, 5:18 AM
Thanks! It looks like that's exactly what I need. I'll post the completed code when I have it complete for others to use.

josephhamilton1
3 Jun 2011, 6:17 AM
I have a couple more questions, if you don't mind entertaining a n00b's pitiful notions :)

1. The setValue and getValue functions both are called twice in this order:

getValue->setValue->setValue->getValue

2. The setValue function does get called each time, so this will work for me, but when I click on the 2nd cell to edit, it clears out the value of the 1st cell that I previously edited.

Questions:

Why are the setValue and getValue functions being called twice?

What do each of the functions do exactly? I mean I understand the idea of get and set, but what are they getting and setting (editor textbox value vs record value from the store)

Last question:

Is there a "best" way to do what I'm trying to do? Or do I just need to try and hack something together and get it to work?

josephhamilton1
3 Jun 2011, 7:12 AM
I've scratched the idea of extending the TextField in favor of just overriding the beforeedit event (as you suggested).

This works perfectly for the way I wrote the code.

Basically, this is what I'm doing:

beforeedit:

1. Create my own abs positioned editing interface with buttons on it.
2. Give the buttons references to the record in the store
3. Set up events on those buttons to update the store based on the values in the new textboxes.
4. Repopulate the textboxes each time beforeedit is called.
5. Hide the editing interface on "apply" or "cancel" button presses.
6. On "Add" button click, add another pair of textboxes.

I'll post the finished product once i've got all the bugs worked out.