Ext.ux.form.BrowseButton组件的使用(上传浏览按钮) 示例
效果:
创建调用HTML:
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title></title>
<link rel="stylesheet" type="text/css" href="extjs/resources/css/ext-all.css" />
<script type="text/javascript" src="extjs/adapter/ext/ext-base.js"></script>
<script type="text/javascript" src="extjs/ext-all.js"></script>
<script type="text/javascript" src="./Ext.ux.form.BrowseButton.js"></script>
<script type="text/javascript" src="./ScreenshotPanel.js"></script>
<style type="text/css">
</style>
<script>
Ext.onReady(function(){
Ext.QuickTips.init();
new Ext.ux.ScreenshotsPanel({
renderTo : document.body,
title: 'Screenshot Adder - Debug = false, meaning Browse butons are hidden.',
frame: true,
debug: false
});
});
</script>
</head>
<body>
</body>
</html>
ScreenshotsPanel.js文件源码:
/**
* @class Ext.ux.ScreenshotPanel
* @extends Ext.Panel
* Renders a browse button for demonstration purposes.
* @constructor
* @param {Object} config The config object
*/
Ext.ux.ScreenshotsPanel = Ext.extend(Ext.Panel, {
/*
* Config options
*/
/**
* For whether to draw the panel (and it's BrowseButtons) in debug mode.
* @type Boolean
*/
debug: false,
/*
* Private properties
*/
/**
* Panel for displaying the files that have selected for upload.
* @type Ext.Panel
*/
filePanel: null,
/**
* Form for uploading screenshot images.
* Is only instantiated if a screenshot is selected to upload.
* @type Ext.form.BasicForm
*/
addScreenshotForm: null,
/*
* Protected methods
*/
/**
* @see Ext.Panel.initConfiguration
*/
initComponent: function(){
var browseButtonBaseConfig = {
xtype: 'browsebutton',
handler: this.onAddScreenshot,
scope: this,
tooltip: 'Click to upload a new screenshot.',
inputFileName: 'screenshot',
debug: this.debug // set to true to see the "Browse" overlay
};
this.filePanel = new Ext.Panel({
frame: true,
title: 'selected files',
buttonAlign: 'center',
buttons: [{
text: 'Upload (does not work and will fail)',
handler: this.uploadScreenshots,
scope: this
}]
});
Ext.apply(this, {
tbar: [
new Ext.ux.form.BrowseButton(Ext.apply({
text: 'tbar - explicit'
}, browseButtonBaseConfig)),
Ext.apply({
text: 'tbar - xtype'
}, browseButtonBaseConfig)
],
items: [
new Ext.ux.form.BrowseButton(Ext.apply({
text: 'items - explicit'
}, browseButtonBaseConfig)),
Ext.apply({
text: 'items - xtype'
}, browseButtonBaseConfig),
this.filePanel
],
buttons: [
new Ext.ux.form.BrowseButton(Ext.apply({
text: 'buttons - explicit'
}, browseButtonBaseConfig)),
Ext.apply({
text: 'buttons - xtype'
}, browseButtonBaseConfig) // doesn't work correctly as Ext uses the config object to instaniate an Ext.Button
]
});
Ext.ux.ScreenshotsPanel.superclass.initComponent.call(this);
},
/*
* Private methods
*/
/**
* Handler for the add screenshot button.
* @param {Ext.ux.form.BrowseButton} browseButton The browse button where "Add Screenshot" was clicked.
* @private
*/
onAddScreenshot: function(browseButton){
if (!this.addScreenshotForm) { // if the form hasn't been created
var addScreenshotFormEl = this.filePanel.body.createChild({
tag: 'form',
style: 'display:none'
});
this.addScreenshotForm = new Ext.form.BasicForm(addScreenshotFormEl, {
url: '/AddScreenshot',
fileUpload: true
});
}
var inputFileEl = browseButton.detachInputFile();
inputFileEl.appendTo(this.addScreenshotForm.getEl());
this.filePanel.body.createChild({
tag: 'div',
html: inputFileEl.dom.value
});
},
/**
* Handler for the upload screenshots button
*/
uploadScreenshots: function(){
this.addScreenshotForm.submit({
params: {
extraParam1: 'value1'
},
success: this.onAddScreenshotSuccess,
failure: this.onAddScreenshotFailure,
scope: this,
waitTitle: 'Uploading Screenshot',
waitMsg: 'Your screenshot is being uploaded...'
});
},
/**
* Callback for when a screenshot has been successfully added.
* @param {Ext.form.BasicForm} form the form for which the uploading occurred.
* @param {Ext.form.Action} action the action of the form submit
* @private
*/
onAddScreenshotSuccess: function(form, action){
// remove the file input element so that it doesn't get uploaded again with the next screenshot
var inputFileEl = this.addScreenshotForm.getEl().child('input');
inputFileEl.remove();
Ext.Msg.show({
title: 'Screenshot Upload Success',
msg: 'Your screenshot was successfully uploaded.',
buttons: Ext.Msg.OK,
minWidth: 300
});
},
/**
* Callback for when a screenshot has not been successfully added.
* @param {Ext.form.BasicForm} form the form for which the uploading occurred.
* @param {Ext.form.Action} action the action of the form submit
* @private
*/
onAddScreenshotFailure: function(form, action){
// remove the file input element so that it doesn't get uploaded again with the next screenshot
var inputFileEl = this.addScreenshotForm.getEl().child('input');
inputFileEl.remove();
var errorMessageTemplate = new Ext.XTemplate('<p>Your screenshot was not uploaded. The server reported the following errors:</p>', '<tpl for="errors">', '<p>- {.}</p>', '</tpl>');
Ext.Msg.show({
title: 'Screenshot Upload Failed',
msg: errorMessageTemplate.applyTemplate(action.result),
buttons: Ext.Msg.OK,
minWidth: 300
});
}
});
Ext.ux.form.BrowseButton.js文件源码:
Ext.namespace('Ext.ux.form');
/**
* @class Ext.ux.form.BrowseButton
* @extends Ext.Button
* Ext.Button that provides a customizable file browse button.
* Clicking this button, pops up a file dialog box for a user to select the file to upload.
* This is accomplished by having a transparent <input type="file"> box above the Ext.Button.
* When a user thinks he or she is clicking the Ext.Button, they're actually clicking the hidden input "Browse..." box.
* Note: this class can be instantiated explicitly or with xtypes anywhere a regular Ext.Button can be except in 2 scenarios:
* - Panel.addButton method both as an instantiated object or as an xtype config object.
* - Panel.buttons config object as an xtype config object.
* These scenarios fail because Ext explicitly creates an Ext.Button in these cases.
* Browser compatibility:
* Internet Explorer 6:
* - no issues
* Internet Explorer 7:
* - no issues
* Firefox 2 - Windows:
* - pointer cursor doesn't display when hovering over the button.
* Safari 3 - Windows:
* - no issues.
* @author loeppky - based on the work done by MaximGB in Ext.ux.UploadDialog (http://extjs.com/forum/showthread.php?t=21558)
* The follow the curosr float div idea also came from MaximGB.
* @see http://extjs.com/forum/showthread.php?t=29032
* @constructor
* Create a new BrowseButton.
* @param {Object} config Configuration options
*/
Ext.ux.form.BrowseButton = Ext.extend(Ext.Button, {
/*
* Config options:
*/
/**
* @cfg {String} inputFileName
* Name to use for the hidden input file DOM element. Deaults to "file".
*/
inputFileName: 'file',
/**
* @cfg {Boolean} debug
* Toggle for turning on debug mode.
* Debug mode doesn't make clipEl transparent so that one can see how effectively it covers the Ext.Button.
* In addition, clipEl is given a green background and floatEl a red background to see how well they are positioned.
*/
debug: false,
/*
* Private constants:
*/
/**
* @property FLOAT_EL_WIDTH
* @type Number
* The width (in pixels) of floatEl.
* It should be less than the width of the IE "Browse" button's width (65 pixels), since IE doesn't let you resize it.
* We define this width so we can quickly center floatEl at the mouse cursor without having to make any function calls.
* @private
*/
FLOAT_EL_WIDTH: 60,
/**
* @property FLOAT_EL_HEIGHT
* @type Number
* The heigh (in pixels) of floatEl.
* It should be less than the height of the "Browse" button's height.
* We define this height so we can quickly center floatEl at the mouse cursor without having to make any function calls.
* @private
*/
FLOAT_EL_HEIGHT: 18,
/*
* Private properties:
*/
/**
* @property buttonCt
* @type Ext.Element
* Element that contains the actual Button DOM element.
* We store a reference to it, so we can easily grab its size for sizing the clipEl.
* @private
*/
buttonCt: null,
/**
* @property clipEl
* @type Ext.Element
* Element that contains the floatEl.
* This element is positioned to fill the area of Ext.Button and has overflow turned off.
* This keeps floadEl tight to the Ext.Button, and prevents it from masking surrounding elements.
* @private
*/
clipEl: null,
/**
* @property floatEl
* @type Ext.Element
* Element that contains the inputFileEl.
* This element is size to be less than or equal to the size of the input file "Browse" button.
* It is then positioned wherever the user moves the cursor, so that their click always clicks the input file "Browse" button.
* Overflow is turned off to preven inputFileEl from masking surrounding elements.
* @private
*/
floatEl: null,
/**
* @property inputFileEl
* @type Ext.Element
* Element for the hiden file input.
* @private
*/
inputFileEl: null,
/**
* @property originalHandler
* @type Function
* The handler originally defined for the Ext.Button during construction using the "handler" config option.
* We need to null out the "handler" property so that it is only called when a file is selected.
* @private
*/
originalHandler: null,
/**
* @property originalScope
* @type Object
* The scope originally defined for the Ext.Button during construction using the "scope" config option.
* While the "scope" property doesn't need to be nulled, to be consistent with originalHandler, we do.
* @private
*/
originalScope: null,
/*
* Protected Ext.Button overrides
*/
/**
* @see Ext.Button.initComponent
*/
initComponent: function(){
Ext.ux.form.BrowseButton.superclass.initComponent.call(this);
// Store references to the original handler and scope before nulling them.
// This is done so that this class can control when the handler is called.
// There are some cases where the hidden file input browse button doesn't completely cover the Ext.Button.
// The handler shouldn't be called in these cases. It should only be called if a new file is selected on the file system.
this.originalHandler = this.handler || null;
this.originalScope = this.scope || window;
this.handler = null;
this.scope = null;
},
/**
* @see Ext.Button.onRender
*/
onRender: function(ct, position){
Ext.ux.form.BrowseButton.superclass.onRender.call(this, ct, position); // render the Ext.Button
this.buttonCt = this.el.child('.x-btn-center em');
this.buttonCt.position('relative'); // this is important!
var styleCfg = {
position: 'absolute',
overflow: 'hidden',
top: '0px', // default
left: '0px' // default
};
// browser specifics for better overlay tightness
if (Ext.isIE) {
Ext.apply(styleCfg, {
left: '-3px',
top: '-3px'
});
} else if (Ext.isGecko) {
Ext.apply(styleCfg, {
left: '-3px',
top: '-3px'
});
} else if (Ext.isSafari) {
Ext.apply(styleCfg, {
left: '-4px',
top: '-2px'
});
}
this.clipEl = this.buttonCt.createChild({
tag: 'div',
style: styleCfg
});
this.setClipSize();
this.clipEl.on({
'mousemove': this.onButtonMouseMove,
'mouseover': this.onButtonMouseMove,
scope: this
});
this.floatEl = this.clipEl.createChild({
tag: 'div',
style: {
position: 'absolute',
width: this.FLOAT_EL_WIDTH + 'px',
height: this.FLOAT_EL_HEIGHT + 'px',
overflow: 'hidden'
}
});
if (this.debug) {
this.clipEl.applyStyles({
'background-color': 'green'
});
this.floatEl.applyStyles({
'background-color': 'red'
});
} else {
this.clipEl.setOpacity(0.0);
}
this.createInputFile();
},
/*
* Private helper methods:
*/
/**
* Sets the size of clipEl so that is covering as much of the button as possible.
* @private
*/
setClipSize: function(){
if (this.clipEl) {
var width = this.buttonCt.getWidth();
var height = this.buttonCt.getHeight();
if (Ext.isIE) {
width = width + 5;
height = height + 5;
} else if (Ext.isGecko) {
width = width + 6;
height = height + 6;
} else if (Ext.isSafari) {
width = width + 6;
height = height + 6;
}
this.clipEl.setSize(width, height);
}
},
/**
* Creates the input file element and adds it to inputFileCt.
* The created input file elementis sized, positioned, and styled appropriately.
* Event handlers for the element are set up, and a tooltip is applied if defined in the original config.
* @private
*/
createInputFile: function(){
this.inputFileEl = this.floatEl.createChild({
tag: 'input',
type: 'file',
size: 1, // must be > 0. It's value doesn't really matter due to our masking div (inputFileCt).
name: this.inputFileName || Ext.id(this.el),
// Use the same pointer as an Ext.Button would use. This doesn't work in Firefox.
// This positioning right-aligns the input file to ensure that the "Browse" button is visible.
style: {
position: 'absolute',
cursor: 'pointer',
right: '0px',
top: '0px'
}
});
this.inputFileEl = this.inputFileEl.child('input') || this.inputFileEl;
// setup events
this.inputFileEl.on({
'click': this.onInputFileClick,
'change': this.onInputFileChange,
scope: this
});
// add a tooltip
if (this.tooltip) {
if (typeof this.tooltip == 'object') {
Ext.QuickTips.register(Ext.apply({
target: this.inputFileEl
}, this.tooltip));
} else {
this.inputFileEl.dom[this.tooltipType] = this.tooltip;
}
}
},
/**
* Handler when the cursor moves over the clipEl.
* The floatEl gets centered to the cursor location.
* @param {Event} e mouse event.
* @private
*/
onButtonMouseMove: function(e){
var xy = e.getXY();
xy[0] -= this.FLOAT_EL_WIDTH / 2;
xy[1] -= this.FLOAT_EL_HEIGHT / 2;
this.floatEl.setXY(xy);
},
/**
* Handler when inputFileEl's "Browse..." button is clicked.
* @param {Event} e click event.
* @private
*/
onInputFileClick: function(e){
e.stopPropagation();
},
/**
* Handler when inputFileEl changes value (i.e. a new file is selected).
* @private
*/
onInputFileChange: function(){
if (this.originalHandler) {
this.originalHandler.call(this.originalScope, this);
}
},
/*
* Public methods:
*/
/**
* Detaches the input file associated with this BrowseButton so that it can be used for other purposed (e.g. uplaoding).
* The returned input file has all listeners and tooltips applied to it by this class removed.
* @param {Boolean} whether to create a new input file element for this BrowseButton after detaching.
* True will prevent creation. Defaults to false.
* @return {Ext.Element} the detached input file element.
*/
detachInputFile: function(noCreate){
var result = this.inputFileEl;
if (typeof this.tooltip == 'object') {
Ext.QuickTips.unregister(this.inputFileEl);
} else {
this.inputFileEl.dom[this.tooltipType] = null;
}
this.inputFileEl.removeAllListeners();
this.inputFileEl = null;
if (!noCreate) {
this.createInputFile();
}
return result;
},
/**
* @return {Ext.Element} the input file element attached to this BrowseButton.
*/
getInputFile: function(){
return this.inputFileEl;
},
/**
* @see Ext.Button.disable
*/
disable: function(){
Ext.ux.form.BrowseButton.superclass.disable.call(this);
this.inputFileEl.dom.disabled = true;
},
/**
* @see Ext.Button.enable
*/
enable: function(){
Ext.ux.form.BrowseButton.superclass.enable.call(this);
this.inputFileEl.dom.disabled = false;
}
});
Ext.reg('browsebutton', Ext.ux.form.BrowseButton);