PDA

View Full Version : Ext.ux.form.CheckboxTextfield & Ext.ux.form.RadioTextfield



NeonMonk
3 Dec 2008, 11:57 PM
Hey guys,

I have quickly thrown together a radiotextfield & checkboxtextfield extensions (by shamelessly piggy backing off jsakalos's fantastic work with the DateTimeField extension (https://extjs.com/forum/showthread.php?t=22661))

It works and may help people searching for something like this. I make no guarantees however, consider it not properly tested & fundamentally unfinished!

Any bugs/fixes/improvements please post back to this thread. :)


Ext.ns('Ext.ux.form');
Ext.ux.form.CheckTextfield = Ext.extend(Ext.form.Field, {
value: false,
checkType: 'checkbox',
checked: false,
defaultAutoCreate: {
tag: 'input',
type: 'hidden'
},
initComponent: function() {
Ext.ux.form.CheckTextfield.superclass.initComponent.call(this);
var checkConfig = Ext.apply({},{
id: this.id + '-' + this.checkType,
checked: this.checked,
ownerCt: this,
listeners: {
blur: {
scope: this,
fn: this.onBlur
},
focus: {
scope: this,
fn: this.onFocus
}
}
},this.checkConfig);
this.cf = (this.checkType == 'checkbox' ? new Ext.form.Checkbox(checkConfig) : new Ext.form.Radio(checkConfig));
delete(this.checkConfig);
var textfieldConfig = Ext.apply({},{
id: this.id + '-textfield',
disabled: true,
style: 'width: 100%;',
ownerCt: this,
enable: function() {
this.disabled = false;
this.el.removeClass('x-item-disabled');
this.focus();
},
disable: function(check) {
this.ownerCt.cf.setValue(false);
this.disabled = true;
this.el.addClass('x-item-disabled');
if (this.hasFocus) {
this.ownerCt.cf.focus();
}
// else {
// var field = (this.checkType == "checkbox" ? Ext.form.Checkbox : Ext.form.Radio);
// field.prototype.disable.apply(this, arguments);
// }
},
listeners: {
blur: {
scope: this,
fn: this.onBlur
},
focus: {
scope: this,
fn: this.onFocus
}
}
},this.textfieldConfig);
this.tf = new Ext.form.TextField(textfieldConfig);
delete(this.textfieldConfig);
},
onRender: function(ct, position) {
if (this.isRendered) {
return null;
}
Ext.ux.form.CheckTextfield.superclass.onRender.call(this, ct, position);
var t;
t = Ext.DomHelper.append(ct, {
tag: 'table',
style: 'border-collapse:collapse;',
children: [{
tag: 'tr',
children: [{
tag: 'td',
style: 'padding-right:4px; width: 10px;',
cls: 'ux-checktextfield-cf'
},
{
tag: 'td',
cls: 'ux-checktextfield-tf'
}]
}]
},true);
this.tableEl = t;
this.wrap = t.wrap();
this.cf.render(t.child('td.ux-checktextfield-cf'));
this.cf.on('check',this.onCheck, this);
this.tf.render(t.child('td.ux-checktextfield-tf'));
this.tf.el.on('click',this.onClick, this);
if (Ext.isIE && Ext.isStrict) {
t.select('input').applyStyles({
top: 0
});
}
this.on('specialkey', this.onSpecialKey, this);
this.cf.el.swallowEvent(['keydown', 'keypress']);
this.tf.el.swallowEvent(['keydown', 'keypress']);
if ('side' === this.msgTarget) {
var elp = this.el.findParent('.x-form-element', 10, true);
this.errorIcon = elp.createChild({
cls: 'x-form-invalid-icon'
});
this.cf.errorIcon = this.errorIcon;
this.tf.errorIcon = this.errorIcon;
}
this.hiddenName = this.name || this.id;
this.hiddenName = this.hiddenName + '-hidden';
this.el.dom.name = this.name || this.id;
if (this.checkType == 'radio') {
this.cf.el.dom.name = this.name || this.id;
this.cf.name = this.name || this.id;
} else {
this.cf.el.dom.removeAttribute("name");
}
this.tf.el.dom.removeAttribute("name");
this.isRendered = true;
this.cf.setValue(this.checked || false);
},
onCheck: function(cb,checked) {
if (checked) {
this.enable();
} else {
this.disable('textfield');
}
},
onClick: function(e,el) {
if (this.tf.disabled) {
this.tf.enable();
this.tf.focus(true);
}
this.setValue(true);
},
adjustSize: Ext.BoxComponent.prototype.adjustSize,
alignErrorIcon: function() {
this.errorIcon.alignTo(this.tableEl, 'tl-tr', [2, 0]);
},
clearInvalid: function() {
this.tf.clearInvalid();
},
disable: function(e) {
if (e) {
this.cf.setValue(false);
this.tf.disable(true);
return this;
}
if (this.isRendered) {
this.cf.disabled = this.disabled;
this.cf.onDisable();
this.tf.onDisable();
}
this.disabled = true;
this.cf.disabled = true;
this.tf.disabled = true;
this.fireEvent("disable", this);
return this;
},
enable: function() {
if (this.rendered) {
this.cf.onEnable();
this.tf.onEnable();
}
this.disabled = false;
this.cf.disabled = false;
this.tf.disabled = false;
this.fireEvent("enable", this);
return this;
},
focus: function() {
this.cf.focus();
},
getPositionEl: function() {
return this.wrap;
},
getResizeEl: function() {
return this.wrap;
},
setSize: function(w, h) {
if (!w) {
return null;
}
this.tf.el.up('td').setWidth(w - 24);
},
getValue: function() {
return (this.cf.getValue() ? this.tf.getValue() : false);
},
isValid: function() {
if (this.cf.getValue()) {
return this.tf.isValid();
}
return this.cf.getValue();
},
isVisible: function() {
return this.cf.rendered && this.cf.getActionEl().isVisible();
},
onBlur: function(f) {
if (this.wrapClick) {
f.focus();
this.wrapClick = false;
}
(function() {
if (!this.cf.hasFocus && !this.tf.hasFocus) {
var v = this.getValue();
if (String(v) !== String(this.startValue)) {
this.fireEvent("change", this, v, this.startValue);
}
this.hasFocus = false;
this.fireEvent('blur', this);
}
}).defer(100, this);
},
onFocus: function() {
if (!this.hasFocus) {
this.hasFocus = true;
this.startValue = this.getValue();
this.fireEvent("focus", this);
}
},
onMouseDown: function(e) {
if (!this.disabled) {
this.wrapClick = 'td' === e.target.nodeName.toLowerCase();
}
},
onSpecialKey: function(t, e) {
var key = e.getKey();
if (key === e.TAB) {
if (t === this.df && !e.shiftKey) {
e.stopEvent();
this.tf.focus();
}
if (t === this.tf && e.shiftKey) {
e.stopEvent();
this.cf.focus();
}
}
if (key === e.ENTER) {
this.updateValue();
}
},
toggleValue : function() {
if(this.cf.checked){
var els = this.cf.getParent().select('input[name='+this.el.dom.name+']');
els.each(function(el){
if(el.dom.id != this.id && el.dom.id != this.id + '-radio'){
Ext.getCmp(el.dom.id).setValue(false);
}
}, this);
}
},
initValue : function(){
if(this.value !== undefined){
this.setValue(this.value,true);
}else if(this.el.dom.value.length > 0 && this.el.dom.value != this.emptyText){
this.setValue(this.el.dom.value,true);
}
// reference to original value for reset
this.originalValue = this.getValue();
},
setValue: function(val,init) {
if (!init) {
this.cf.setValue(val);
if (val !== true && val !== false) {
this.tf.setValue(this.value);
}
this.cf.setValue(val);
if (val === true && this.checkType == 'radio') {
this.toggleValue();
}
} else {
if (this.checkType == "radio" && val !== true && val !== false) {
this.tf.setValue(this.value);
}
this.cf.setValue(this.checked || false);
}
},
setVisible: function(visible) {
if (visible) {
this.cf.show();
this.tf.show();
} else {
this.cf.hide();
this.tf.hide();
}
return this;
},
show: function() {
return this.setVisible(true);
},
hide: function() {
return this.setVisible(false);
},
updateHidden: function() {
if (this.isRendered) {
this.el.dom.value = this.tf.getValue();
}
},
updateValue: function() {
this.updateHidden();
return null;
},
validate: function() {
return this.tf.validate();
}
});
Ext.ux.form.RadioTextfield = Ext.extend(Ext.ux.form.CheckTextfield,{
checkType: 'radio'
});
Ext.ux.form.CheckboxTextfield = Ext.extend(Ext.ux.form.CheckTextfield,{
checkType: 'checkbox',
valueInField: false
});
Ext.reg('radiotext', Ext.ux.form.RadioTextfield);
Ext.reg('checktext', Ext.ux.form.CheckboxTextfield);

Usage:


<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<title>CheckboxTextfield</title>
<link rel="stylesheet" type="text/css" href="../../resources/css/ext-all.css"/>
<script type="text/javascript" src="../../adapter/ext/ext-base.js"></script>

<script type="text/javascript" src="../../ext-all-debug.js"></script>

<script type="text/javascript" src="Ext.ux.form.CheckboxTextfield.js"></script>
<script type="text/javascript">
Ext.onReady(function(){
Ext.QuickTips.init();

// turn on validation errors beside the field globally
Ext.form.Field.prototype.msgTarget = 'side';

var bd = Ext.getBody();

/*
* ================ Simple form =======================
*/
bd.createChild({tag: 'h2', html: 'Form 1 - Very Simple'});


var simple = new Ext.FormPanel({
labelWidth: 75, // label settings here cascade unless overridden
url:'save-form.php',
frame:true,
title: 'Simple Form',
bodyStyle:'padding:5px 5px 0',
width: 350,
defaults: {width: 230},
defaultType: 'textfield',

items: [{
fieldLabel: 'First Name',
name: 'first',
allowBlank:false
},{
fieldLabel: 'Last Name',
name: 'last'
},{
fieldLabel: 'Got Milk? (what brand)',
id: 'checktext',
xtype: 'checktext',
name: 'got milk'
},{
fieldLabel: 'one',
id: 'one',
xtype: 'radiotext',
name: 'gotmilk'
},{
fieldLabel: 'two',
id: 'two',
xtype: 'radiotext',
name: 'gotmilk'
},{
fieldLabel: 'three',
id: 'three',
xtype: 'radiotext',
name: 'gotmilk'
}
],

buttons: [{
text: 'Save'
},{
text: 'Cancel'
}]
});

simple.render(document.body);
});
</script>
</head>
<body>
</body>
</html>

BigTitus
18 Dec 2008, 5:57 AM
Yes! This is exactly what I was looking for! :D

One suggestion is to add



isValid: function() {
return this.tf.isValid();
}


Otherwise formBind (or validation at all?) won't work. At least in my case. Don't know if this is a proper solution but it works for me. And is there a certain reason for removing the name attributes?



this.hiddenName = this.name || this.id;
this.hiddenName = this.hiddenName + '-hidden';
this.el.dom.name = this.name || this.id;
if (this.checkType == 'radio') {
this.cf.el.dom.name = this.name || this.id;
this.cf.name = this.name || this.id;
} else {
// this.cf.el.dom.removeAttribute("name"); <--
}
// this.tf.el.dom.removeAttribute("name"); <--
this.isRendered = true;
this.cf.setValue(this.checked || false);
...


I commented these lines out since I want to submit both checkbox value and textfield value.

Nevertheless, great work! =D>
Thanks for sharing!

dreas
19 Dec 2008, 8:16 AM
And is there a certain reason for removing the name attributes?
By removing the name attributes he avoids submitting both the checkbox and the textfield. The hidden field is getting the textfield's value anyway (in the updateHidden function).

BigTitus
19 Dec 2008, 8:45 AM
Yes, that's clear. But in my case I also need to know on server side whether checkbox was selected at all. I just wanted to know if there could be any side effects when commenting out the mentioned lines.

medusadelft
20 Dec 2008, 6:02 AM
Great extension NeonMonk!

But when I do a post of the form, there is no value in the field although I typed something in (at this moment only tested with FF3). When I remove the lines suggested by BigTitus, I get the required information in two additional fields.

I renamed the 'id' and 'name' options to 'backupEmailTo'.

Maurice.

BigTitus
22 Dec 2008, 12:54 AM
As dreas said there is a hidden field which is submitting the value. Did you give an id to the component? And did you see you can give additional options to the fields by using textfieldConfig and checkConfig. Just fyi. ;)