PDA

View Full Version : Forms, forms, forms, and complex widgets



mschwartz
19 Mar 2009, 8:51 AM
Ext is suited for the project I'm working on in every way but one. When it comes to things like trees, grids, layouts, tab panels, dialog windows, and toolbars, Ext is quite awesome. Since my project is also heavily reliant on literally hundreds of forms, I've had to work with Ext's form components and I have to say this is one of the two weakest parts of Ext (the other being rich text editing).

The choice of Ext is obvious - you want to focus on plugging things it provides into your application and it gets richer and richer. You don't want to focus on the guts of how a checkbox gets toggled or a select box populated; at least as little as possible.

The set of Ext form fields is a reasonable start, but as soon as you need any custom behavior or even try to render a form that looks like what the production people (designers) mock up, it becomes hell.

Why?

Answer in my next post.

mschwartz
19 Mar 2009, 8:57 AM
You need a widget that's a text box with a checkbox to the right, but in a column layout with some column of other widgets to the right of that.

So you end up coding up something that is a form with a nested column layout with a nested table layout (to get the checkbox where you want it), and then you struggle with why the checkbox isn't where you want it (5 pixels to the right of the text box).

In some of the older UI toolkits for Unix, like OpenLook, the textbox is a Widget, the checkbox is a Widget, and it's quite trivial to make a compound or complex Widget out of other Widgets. I'm not finding this to be true in Ext.

Consider my next post (it's got a big chunk of source code, hence another post).

mschwartz
19 Mar 2009, 9:04 AM
This is a bit of code that Saki wrote. It's only a slight modification to an existing widget. All he wants to do is to be able to make a DateField submit a date with a customizable format. Note I hacked it a bit to send Unix timestamp, too.



/**
* Ext.ux.form.DateField - Date field that supports submitFormat
*
* @author Ing. Jozef Sakalos
* @version $Id: Ext.ux.form.DateField.js 288 2008-06-11 08:53:17Z jozo $
*
* @license Ext.ux.grid.DateField is licensed under the terms of
* the Open Source LGPL 3.0 license. Commercial use is permitted to the extent
* that the code/component(s) do NOT become part of another Open Source or Commercially
* licensed development library or toolkit without explicit permission.
*
* License details: http://www.gnu.org/licenses/lgpl.html
*/

/*global Ext */

Ext.ns('Ext.ux.form');

/**
* @class Ext.ux.form.DateField
* @extends Ext.form.DateField
*/
Ext.ux.form.DateField = Ext.extend(Ext.form.DateField, {
submitFormat:'Y-m-d'
,onRender:function() {

// call parent
Ext.ux.form.DateField.superclass.onRender.apply(this, arguments);

var name = this.name || this.el.dom.name;
this.hiddenField = this.el.insertSibling({
tag:'input'
,type:'hidden'
,name:name
,value:this.formatHiddenDate(this.parseDate(this.value))
});
this.hiddenName = name; // otherwise field is not found by BasicForm::findField
this.el.dom.removeAttribute('name');
this.el.on({
keyup:{scope:this, fn:this.updateHidden}
,blur:{scope:this, fn:this.updateHidden}
});

this.setValue = this.setValue.createSequence(this.updateHidden);

} // eo function onRender

,onDisable: function(){
// call parent
Ext.ux.form.DateField.superclass.onDisable.apply(this, arguments);
if(this.hiddenField) {
this.hiddenField.dom.setAttribute('disabled','disabled');
}
} // of function onDisable

,onEnable: function(){
// call parent
Ext.ux.form.DateField.superclass.onEnable.apply(this, arguments);
if(this.hiddenField) {
this.hiddenField.dom.removeAttribute('disabled');
}
} // eo function onEnable

,formatHiddenDate : function(date){
if (Ext.isDate(date)) {
if (this.submitFormat === 'timestamp') {
return date.getTime()/1000;
}
else {
return Ext.util.Format.date(date, this.submitFormat);
}
}
return date;
}

,updateHidden:function() {
this.hiddenField.dom.value = this.formatHiddenDate(this.getValue());
} // eo function updateHidden

}); // end of extend

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


About 75 lines of code, minus comments, to change a simple behaviour of an existing Widget.

Again, I have hundreds of forms with all kinds of similar behaviour things that need to be dealt with.

100 forms x only 1 custom Widget per form x 75 lines of code = 7500 lines of code. At 4 widgets per form, you're close to writing the equivalent of 1/4 of Ext.

Another example in my next post.

mschwartz
19 Mar 2009, 9:09 AM
I apologize for not crediting the author of this bit of code. It's one of those Compound type Widgets I talked about before. All the guy wants to do is put a text box next to a slider and show the slider's value in the text box.



Ext.form.SliderField = Ext.extend(Ext.Slider, {
isFormField: true,
onRender: function(){
Ext.form.SliderField.superclass.onRender.apply(this, arguments);
this.nrField = this.el.createChild({
tag: 'input',
type: 'text',
name: this.name,
value: this.value,
disabled:true,
'class': "x-form-text",
style: 'position: relative; float:right; height:20px; left: 85px; margin-top:-19px; font-size:10px;width:80px;'
});
},
setValue: function(v) {
if(this.maxValue && v > this.maxValue) v = this.maxValue;
if(this.minValue && v < this.minValue) v = this.minValue;

Ext.form.SliderField.superclass.setValue.apply(this, arguments);
this.nrField.dom.value = v;
},
getValue: function() {
var v = this.nrField.dom.value;
Ext.form.SliderField.superclass.setValue.apply(this, [v]);
return v;
},
markInvalid: Ext.emptyFn,
clearInvalid: Ext.emptyFn,
validate: function(){this.nrField.dom.disabled=false; return true;}
});

Ext.reg('sliderfield', Ext.form.SliderField);
This one's only 32 lines of code with no comments. It's also not clear to me that it is 100% right, FWIW. For example, it looks like he is making two fields with the same arguments.name (the Ext.Slider and this.nrField). Nor does it resize, etc.

mschwartz
19 Mar 2009, 9:19 AM
SliderField is a good example of Compound Widgets. I'll offer some more:

1) Checkbox with a datefield to the right, checkbox enables datefield
2) Password field with invisible retype password field to the right, enter password and the invisible one becomes visible.
3) Grid as form field. I see this throughout Windows. Consider the Network Neighborhood / LAN Connection properties dialog (see attachment) - "This connection uses the following items" looks like a grid to me.
4) Tree as forum field.

Etc.

mschwartz
19 Mar 2009, 9:31 AM
By far the easiest way to get a form to look like the mock ups is to use raw HTML and apply the various Ext widgets to it. Where do you store the HTML? What are the options?

1) In a bunch of javascript statements, perhaps. Not very easy to customize by production. You have to tell them, "use this ID for that date field," then renderTo that ID. Not at all a clean separation of the code from the display.
2) Use Web 1.0 style forms and just show them in a dialog with an iframe. The benefit is production can make the HTML and CSS and add images and what not to their heart's content. The drawback is... ewww, this is the way we did things in 1995 and you need MVC complexity for multipaged wizards or tabbed forms.
3) Use ExtJS as directly as possible generating forms dynamically (in the DOM). This means the app is going to grow 2x or more just for all the custom widgets.

Last but not least, there's the issue of validation. My client has seen all 4 of the ways Ext shows error messages when field validation fails and hates them all. Am I going to have to extend every field in Ext to fix this? Sure seems so because the valid/invalid events have to be handled the way the client wants.

Anyone want to chime in with the easy solution? :)

Joe
19 Mar 2009, 9:56 AM
...
About 75 lines of code, minus comments, to change a simple behaviour of an existing Widget.

Again, I have hundreds of forms with all kinds of similar behaviour things that need to be dealt with.

100 forms x only 1 custom Widget per form x 75 lines of code = 7500 lines of code. At 4 widgets per form, you're close to writing the equivalent of 1/4 of Ext.

Another example in my next post.

Notice the code you point to does an Ext.extend? This extends the component which should be referenced and cached in a .js file. Making it ... 75 lines of code ... saving you tons of code all over your application.

... that said ...

I agree with you about forms and in fact I only use the field widgets - not the FormPanel and related functionality. I also do not use the vtype or validation for the same reason you mention in your last post (among others). I do however use the vtype design pattern for a ton of implementations, including validations. I do this partly due to the fact that I work with a system that creates the HTML and I have to (get to) integrate.

Even in that environment I am able to implement solutions one time that solve my needs and are available (cached) for all my forms.

Try to get what works well and use it. Where the out of box system does not fit your exact needs - override, extend and/or create what you need a .js files.

They may have some of this worked out in 3.0 and for sure need some examples on how to integrate with existing HTML and environments better.

Good discussion point.

Cheers

mschwartz
19 Mar 2009, 10:15 AM
The only place I've used an actual Ext form is where I have a file upload. It's handier to let Ext do its thing with the invisible IFrame than do it myself.

I see the Ext.extend, obviously. It's still 75 lines for just the one component, and I no doubt have dozens if not hundreds of components that need to be implemented.

A word of caution. I rendered a form as HTML and applied (renderTo) some Ext fields to it and those fields do not get their destroy() member called. This means you have to do your own bookkeeping per form.

Joe
19 Mar 2009, 11:20 AM
You do not have to extend every object - just run your own validation code.

If you do not like the way ExtJS validation works, as I don't, then just don't use it. Writing validation routines is easy enough and can be just as module as vtype is.

Are you working with existing HTML or a tool that generates HTML?

mschwartz
19 Mar 2009, 11:59 AM
You do not have to extend every object - just run your own validation code.

If you do not like the way ExtJS validation works, as I don't, then just don't use it. Writing validation routines is easy enough and can be just as module as vtype is.

Are you working with existing HTML or a tool that generates HTML?

It's a revamp of an existing product built on a custom JS framework written 6 years ago, hacked on by numerous programmers, with frames inside of frames inside of frames, and an MVC form engine on the server side that generates JS in a bazillion places assuming this custom JS framework is running in every frame.

I don't have an issue with the validation, but where the validation errors are reported. I don't see any way but Extending all the form fields and adding on('invalid'... and on('valid'... handlers to them all.

Joe
19 Mar 2009, 12:20 PM
If you are using ExtJS field validation then if you want to change how that works - you very well may have to extend. I did extend all the classes in my first shot at this - but found I didn't really use any of the existing validation .. my override simply short circuited it and I did my own. So in the second run I just communicate with the widgets and do my own validation (at the field level - just not the same way - but similar :) ).

Maybe 3.0 will solve some of your woes :)

Cheers