PDA

View Full Version : [CLOSED] A case made for ComboBox SelectedItem when ForceSelection=true



michaeld
Oct 30, 2013, 3:52 AM
On the server-side, it seems one can set Cmb.SelectedItem.Value= to a value that is not a value in a list of currently bound items even if ForceSelection=true. Naturally, on the client-side there is a reason for this limitation. However, on the server-side, there's a reasonable expectation that a failure should result if the programmer attempts to set an invalid value. Ideally, I'd like the ability to set a default value or some kind of nearest value on failure.

If the attempt to set Cmb.SelectedItem.Value="30" or Cmb.SelectedItem.Text="30 miles" did not match an item in the existing ListItem, the programmer should be able to test if Cmb.SelectedItem==null or string.IsNullOrEmpty(Cmb.SelectedItem.Value) after the original set, then set a default, but no such behavior is available. The programmer has to manually traverse the list of ListItems to test or handle the issue after it's resolved to nothing on the client-side.

Thoughts?

michaeld
Oct 30, 2013, 4:10 AM
FYI, I did write this extension but I still think some discussion should be considered in the library.


public static void SetSelection( this ComboBoxBase cmb, string value, string dflt=null ) {
// If ForceSelection, attempt to enforce
int len = cmb.Items==null ? 0 : cmb.Items.Count;
if( !string.IsNullOrEmpty(value) && cmb.ForceSelection && len > 0 ) {
for( int i = 0; i < len; i++ ) {
if( cmb.Items[i].Value == value ) {
// Found item so force selection enforced
cmb.SelectedItem.Value = value;
return;
}
}
// Item was not found/ForceSelection could not be enforced, so set default
if( dflt!=null ) {
cmb.SelectedItem.Value = dflt;
return;
}
// Otherwise use default combobox behavior
}
cmb.SelectedItem.Value = value;
}

Daniil
Oct 30, 2013, 6:31 AM
Hi @michaeld,

Well, it looks impossible to resolve it in a generic manner on server. I mean that a ComboBox's Items is not always a case. A ComboBox can be assigned to a Store. In this case, I don't see a possibility to iterate through a items.

You could try to override a ComboBox's setValue method to achieve the requirement. See the "you can add your custom logic here" comment.

ComboBox setValue

Ext.form.field.ComboBox.override({
setValue: function(value, doSelect) {
var me = this,
valueNotFoundText = me.valueNotFoundText,
inputEl = me.inputEl,
i, len, record,
dataObj,
matchedRecords = [],
displayTplData = [],
processedValue = [];

if (me.store.loading) {
// Called while the Store is loading. Ensure it is processed by the onLoad method.
me.value = value;
me.setHiddenValue(me.value);
return me;
}

// This method processes multi-values, so ensure value is an array.
value = Ext.Array.from(value);

// Loop through values, matching each from the Store, and collecting matched records
for (i = 0, len = value.length; i < len; i++) {
record = value[i];
if (!record || !record.isModel) {
record = me.findRecordByValue(record);
}
// record found, select it.
if (record) {
matchedRecords.push(record);
displayTplData.push(record.data);
processedValue.push(record.get(me.valueField));
}
// record was not found, this could happen because
// store is not loaded or they set a value not in the store
else {
// If we are allowing insertion of values not represented in the Store, then push the value and
// create a fake record data object to push as a display value for use by the displayTpl
if (!me.forceSelection) {
processedValue.push(value[i]);
dataObj = {};
dataObj[me.displayField] = value[i];
displayTplData.push(dataObj);
// TODO: Add config to create new records on selection of a value that has no match in the Store
}
// Else, if valueNotFoundText is defined, display it, otherwise display nothing for this value
else if (Ext.isDefined(valueNotFoundText)) { // you can add your custom logic here
displayTplData.push(valueNotFoundText);
}
}
}

// Set the value of this field. If we are multiselecting, then that is an array.
me.setHiddenValue(processedValue);
me.value = me.multiSelect ? processedValue : processedValue[0];
if (!Ext.isDefined(me.value)) {
me.value = null;
}
me.displayTplData = displayTplData; //store for getDisplayValue method
me.lastSelection = me.valueModels = matchedRecords;

if (inputEl && me.emptyText && !Ext.isEmpty(value)) {
inputEl.removeCls(me.emptyCls);
}

// Calculate raw value from the collection of Model data
me.setRawValue(me.getDisplayValue());
me.checkChange();

if (doSelect !== false) {
me.syncSelection();
}
me.applyEmptyText();

return me;
}
});

michaeld
Oct 30, 2013, 11:54 AM
Well, it looks impossible to resolve it in a generic manner on server. I mean that a ComboBox's Items is not always a case. A ComboBox can be assigned to a Store. In this case, I don't see a possibility to iterate through a items.


It would seem to me, if data is bound to a store on the server-side, values could be traversed in much the same way list items can. I realize sometimes the data is bound late by the client-side, but my proposal was for when it is not.

Daniil
Oct 30, 2013, 1:52 PM
Well, yes, I understand your suggestion. Thank you for that.

Though, in that case, I feel that it should be under a developer's responsibility. ForceSelection="true" does its job, it denies a non-existing item. Well, it looks quite a custom scenario when a developer tries to select a non-existing item and use ForceSelection="true" meanwhile.

michaeld
Oct 30, 2013, 11:57 PM
Though, in that case, I feel that it should be under a developer's responsibility. ForceSelection="true" does its job, it denies a non-existing item. Well, it looks quite a custom scenario when a developer tries to select a non-existing item and use ForceSelection="true" meanwhile.

The question I ask is, are ext.net control properties just setters for the delivery channel to pass values down to the extjs controls on the client side? Your answer suggests yes.


Because I cannot expect ForceSelection to enforce it's rule on the server-side during load (when sometimes it can) until it gets down to the extjs client-side for resolution, I can't rely on the getter on the server-side to contain the ForcedSelection resolved result until it comes back in a DirectEvent or Method.


You're telling me that ForceSelection and SelectedItem.Value on the server-side are nothing more than properties used to pass the message on to the renderer and don't have the same functional meaning on the server as they do for the client.


Okay, fine. But there are many topics like this. I'm hoping maybe if there is a 3.0 someday, we can open up discussion regarding the direction of the library and have more open discussion about cohesiveness between what we expect between both server and client especially distinctions between initial load and postback.

Daniil
Oct 31, 2013, 4:46 AM
The question I ask is, are ext.net control properties just setters for the delivery channel to pass values down to the extjs controls on the client side? Your answer suggests yes.


Yes, mostly that is true.



Because I cannot expect ForceSelection to enforce it's rule on the server-side during load (when sometimes it can) until it gets down to the extjs client-side for resolution, I can't rely on the getter on the server-side to contain the ForcedSelection resolved result until it comes back in a DirectEvent or Method.


A key word is "sometimes". Sometimes it is going to work (with a ComboBox's Items), sometimes not (with a Store with a Proxy). It would be quite inconsistency. Someone can ask "why does it work sometimes, but not always?". The answer is "well, it is not always possible". "It is inconsistency. Maybe, it is better not to do that at all leaving on a developer's responsibility".



But there are many topics like this. I'm hoping maybe if there is a 3.0 someday, we can open up discussion regarding the direction of the library and have more open discussion about cohesiveness between what we expect between both server and client especially distinctions between initial load and postback.

Yes, it is a good idea. And it would be so good to have in the toolkit. Though, it is not always possible to resolve (at least, in a generic manner) for all the cases. Also, the server side properties very often make sense only on client, being only an intermediary to pass configs to client.

michaeld
Oct 31, 2013, 7:15 AM
A key word is "sometimes". Sometimes it is going to work (with a ComboBox's Items), sometimes not (with a Store with a Proxy). It would be quite inconsistency. Someone can ask "why does it work sometimes, but not always?". The answer is "well, it is not always possible". "It is inconsistency. Maybe, it is better not to do that at all leaving on a developer's responsibility".


I'm not so sure it's inconsistent from the programmer's standpoint. If he makes the binding on the server-side, it's reasonable he should expect he/she should be able to access it's members after the binding. Nothing suspicious. If he binds to something on the client, I think it's reasonable he/she should expect he/she will have to wait to come back from the client. Chronology is critical to understanding anything in programming. It's not so much sometimes as "well, duh." ;) Maybe you have an idea of something I'm not thinking of.

Daniil
Nov 05, 2013, 7:28 AM
Do you mean such a check during initial rendering of a ComboBox or during a DirectEvent/DirectMethod?

michaeld
Nov 14, 2013, 9:10 AM
Do you mean such a check during initial rendering of a ComboBox or during a DirectEvent/DirectMethod?

Yeah, initial. On the directevent/directmethod, it's already resolved.

This item has gotten stale but I noticed you had replied so I thought you deserved a response. For the record, I'm not too seriously worried what you decide even though I've present a strong impression that I think is the correct answer (as it may affect other programmers). My extension method does what I needed, though it might need to be enhanced to support databound comboboxes. I'll add that support when I have that need and try to remember to post here.

Daniil
Nov 14, 2013, 1:30 PM
On the directevent/directmethod, it's already resolved.

Hmm, probably, I misunderstand something, but how?

michaeld
Nov 15, 2013, 2:37 AM
Hmm, probably, I misunderstand something, but how?

Because ForceSelection of the value that was set during initialization is cleared as an invalid value by extjs in the client (because it did not match). By the time it posts back to the DirectEvent, the value is no longer what the initial setting was because the client determined it did not match one from the list and clears it to empty.

This whole topic is about providing the same mechanism of ForceSelection on the server-side, as is already provided by the client-side, so that a value that is set, if and only if already bound to a list before the test, can be verified if it is matched before sending it down to the client. Ideally, in my case, I want to test if the set was valid from the list so I can set a default value instead of having it clear to empty later before I send it down to the client. See my extension method.

The reason I made this argument in the first place was to say that clientside and serverside mechanisms should match just like sometimes we have to write validators for both sides that do the same thing for consistency and code assurance. I just tried to make a case that ForceSelection should mean something on the server-side too, even if you decide another attribute might be necessary to turn this scenario on. If you again say no, okay, moving on.

Daniil
Nov 15, 2013, 3:34 AM
Because ForceSelection of the value that was set during initialization is cleared as an invalid value by extjs in the client (because it did not match). By the time it posts back to the DirectEvent, the value is no longer what the initial setting was because the client determined it did not match one from the list and clears it to empty.

Well, yes. But I meant setting up a ComboBox's SelectedItem.Value during a DirectEvent/DirectMethod. It is not going to work.

Your suggestion definitely makes good sense, but it is possible to implement for a very little set of possible scenarios. Yes, we are still reluctant to do that.