PDA

View Full Version : Ext.ux.FileSelector traditional inputType file



KajaSheen
12 Feb 2008, 1:48 PM
For all the folks who don't want to or can't use AJAX file upload in their application finally there is a replacement for TextField type: 'file'. I use regular form submit, there is no handling for any other type yet.

I used a TwinTriggerField and used the technique from http://www.shauninman.com/ (http://www.shauninman.com/archive/2007/09/10/styling_file_inputs_with_css_and_the_dom) with a little twist. The input will be made opaque and hovered over the triggerfield.

The inputs are configured with the {name} option.
The following vars are submitted with the form:
{name}: the file name, stripped from path info
{name}FILE: the uploaded file
{name}Keep: when the field is initialized with a value it thinks that one is trying to update a file. When just other properties as the name are changed my backend must know that there is no upload, that is when {name}Keep equals 1.

Also when initialized with a value the trigger to reset will go back to the initial value instead of empty field.

When canceling the file a new hidden input for the file will be created, which is the only way to reset the field, because JS doesn't allow to change the value of input[type:file].

Tested on Win FF, IE 7 and IE 6... Will check Mac sometimes this week,

[EDIT] Added the .zip with my example code. Unpack in your "ext/examples/form" folder so the images and stuff are found...

JS Code


/*
* Ext JS Library 2.0
* Copyright(c) 2006-2007, Ext JS, LLC.
* licensing@extjs.com
*
* http://extjs.com/license
*/

Ext.ux.FileSelector = Ext.extend(Ext.form.TwinTriggerField, {
initComponent : function(){
Ext.ux.FileSelector.superclass.initComponent.call(this);

this.triggerConfig = {
tag:'span', cls:'x-form-twin-triggers', cn:[
{tag: "img", src: Ext.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
{tag:'span', cls:'ux-cabinet', cn:[
{tag: "img", id: this.id + "Selector", src: Ext.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class},
{tag: "div", id: this.id + "Wrapper", cls: "ux-input-file-wrapper", cn: [
{tag: "input", name: this.name + "FILE", type: "file", cls: "ux-file", id: this.id + "File"}
]}
]},
{tag: "input", name: this.name + "Keep", type: "checkbox", cls:"x-hidden", id: this.id + "Keep", value: 1}

]};

var v = this.getRawValue();
if(v.length > 0){
this.hasValue = true;
this.uxInitialValue = v;
}

this.on("render", this.uxStylizeTrigger, this);
},

uxInitialValue: false,

name: 'file',
readOnly: true,
validationEvent:false,
validateOnBlur:false,
trigger1Class:'x-form-clear-trigger',
trigger2Class:'x-form-search-trigger',
hideTrigger1:true,
width:250,
hasValue : false,
paramName : false,



onTrigger1Click : function(){
if(this.hasValue){
this.triggers[0].hide();
this.hasValue = false;

if(this.uxInitialValue) {
var keep = Ext.get(this.id + "Keep");
keep.dom.checked = true;

this.setValue(this.uxInitialValue);
} else {
this.setValue('');
}


var input = Ext.get(this.id + "File");
input.remove();

var wrapper = Ext.get(this.id + "Wrapper");
Ext.DomHelper.append(wrapper, {tag: "input", name: this.name + "FILE", type: "file", cls: "ux-file", id: this.id + "File"});

var input_new = Ext.get(this.id + "File");
input_new.on("change", this.uxHandleFile, this);
}
},

onTrigger2Click : function(){
this.hasValue = true;
this.triggers[0].show();


var keep = Ext.get(this.id + "Keep");
keep.dom.checked = false;


},

uxHandleFile : function() {

var input = Ext.get(this.id + "File");

//if this is unix style structure replace / with \
var filePath = input.dom.value.replace(/\//g, '\\');

//extract the filename from the value
var indexPos = filePath.lastIndexOf('\\');
var fileName = filePath.substring(indexPos + 1);

this.setValue(fileName);

search = /(zip|tar|gz)$/i;

if(search.test(fileName)) {
//alert('archive');
//createArchiveCheck(input);
}

this.onTrigger2Click();
},

uxStylizeTrigger : function(){

if(this.hasValue) {
var keep = Ext.get(this.id + "Keep");
keep.dom.checked = true;
}

var trigger = Ext.get(this.id + "Selector");
var wrapper = Ext.get(this.id + "Wrapper");
var input = Ext.get(this.id + "File");

input.on("change", this.uxHandleFile, this);

trigger.file = wrapper;
trigger.on("mousemove", function(e) {

var pageX = e.xy[0];
var pageY = e.xy[1];

ox = this.getX();
oy = this.getY();

var x = pageX - ox;
var y = pageY - oy;
var w = this.file.getWidth();
var h = this.file.getHeight();

this.file.setTop(y - (h / 2) - 5 + 'px');
this.file.setLeft(x - (w - 15) + 'px');
});
}
});

bloudon
13 Feb 2008, 8:28 AM
Line 84 has a syntax error: the forward slash in the regexp is not being escaped.

To fix, change this:


var filePath = input.dom.value.replace(///g, '\\');

... to this:


var filePath = input.dom.value.replace(/\//g, '\\');

bloudon
13 Feb 2008, 9:16 AM
This is excellent, by the way. I've been wanting a simple, drop-in replacement for the traditional file selector, and this provides.

Just a couple nitpicks:

Adding the suffix 'FILE' to the file input field name prevents this from being used as a direct replacement for a normal file selector since server-side code must then be changed to recognize the new name. Replacing all instances of 'this.name + "FILE"' with 'this.name' eliminated that problem.

Using the 'name' property for the IDs of generated elements will cause a problem when there are multiple forms in simultaneous existence. It would be better to work with the unique ID already provided to or generated by the parent class, or to generate unique IDs of your own.

KajaSheen
13 Feb 2008, 4:03 PM
Tried to fix the reg_replace, but it is an error from the vbcode I guess. The source is exactly what you fixed.

How do I need to escape for the post to show the \?

When you use that remember that there are two fields with the {name} because the input from the triggerfield uses the same {name} for the input field. Digging into the source there might be a function that can be overridden to change that the file name will be submitted maybe as {name}Name...

Thanks for the input, I will post an updated version soon...

bloudon
14 Feb 2008, 6:40 AM
Tried to fix the reg_replace, but it is an error from the vbcode I guess. The source is exactly what you fixed.

How do I need to escape for the post to show the \?

No clue. Posting the extension in a ZIP is probably the best option.


When you use that remember that there are two fields with the {name} because the input from the triggerfield uses the same {name} for the input field.

Ah, yes, you are correct. I renamed that to something else without any loss of functionality.


Digging into the source there might be a function that can be overridden to change that the file name will be submitted maybe as {name}Name...

I realize it comes down to personal preference, in the end. It is a simple enough task to change the names for my purposes.

Maybe you could add config options to customize the individual names? Something like:


var oField = new Ext.ux.FileSelector({
fileName: 'file',
keepName: 'file-keep'
});

The default values could then be the name + 'FILE' and name + 'Keep' you currently have.

dantheman
14 Feb 2008, 9:13 AM
I will give this a look. I rolled my own using the method PPK outlines
at quirksmode.org, but it was buggy in IE6... 8-|

Thanks for this work!
--dan

Lloyd K
14 Nov 2008, 7:34 AM
This doesn't seem to work with Ext 2.1+, both the trigger field AND the file input field are shown and the trigger doesn't cause browse to work.

mystix
14 Nov 2008, 7:42 AM
This doesn't seem to work with Ext 2.1+, both the trigger field AND the file input field are shown and the trigger doesn't cause browse to work.

Have you tried @loeppky's BrowseButton (http://extjs.com/forum/showthread.php?t=29032) extension as an alternative?

Lloyd K
14 Nov 2008, 7:44 AM
Yes, this isn't useful for me I specifically need it as a trigger.

Lloyd K
14 Nov 2008, 7:49 AM
Got it working now :)

mystix
14 Nov 2008, 7:54 AM
Got it working now :)

you might want to detail how you did it? :-?

voidstar
25 Jan 2009, 12:22 PM
Any ideas on how to get this widget to show in a fieldset?

KRavEN
6 Apr 2009, 7:35 AM
you might want to detail how you did it? :-?
Fix was to get the css out of the html file in the zip file of the first post and use it, at least thats what I had to do.