Forms 如何创建自定义ExtJS表单字段组件?
我想使用其中的其他ExtJS组件(例如TreePanel)创建自定义ExtJS表单字段组件。我怎样才能最容易地做到这一点 我已经阅读了的文档,但我不想通过Forms 如何创建自定义ExtJS表单字段组件?,forms,extjs,extjs4,Forms,Extjs,Extjs4,我想使用其中的其他ExtJS组件(例如TreePanel)创建自定义ExtJS表单字段组件。我怎样才能最容易地做到这一点 我已经阅读了的文档,但我不想通过fieldSubTpl定义字段主体。我只想编写创建ExtJS组件的代码,也许还有其他一些获取和设置值的代码 更新:总结目的如下: 这个新的部件应该安装在 表单GUI作为一个字段。它应该有 标签和同一路线(标签, 不需要的其他字段的 进一步的黑客攻击 可能吧,我有 要编写一些getValue,请使用setValue 逻辑。我宁愿将它嵌入到这个组
fieldSubTpl
定义字段主体。我只想编写创建ExtJS组件的代码,也许还有其他一些获取和设置值的代码
更新:总结目的如下:
- 这个新的部件应该安装在 表单GUI作为一个字段。它应该有 标签和同一路线(标签, 不需要的其他字段的 进一步的黑客攻击
- 可能吧,我有 要编写一些getValue,请使用setValue 逻辑。我宁愿将它嵌入到这个组件中,也不愿编写单独的代码,将内容复制到我还必须管理的进一步隐藏的表单字段中
我已经做过几次了。以下是我使用的一般过程/伪代码:
- 创建一个提供最有用重用的字段扩展(如果您只想获取/设置字符串值,通常为Ext.form.TextField)
- 在字段的
中,隐藏textfield,并使用afterrender
this.wrap=this.resizeEl=this.positionEl=this.el.wrap()围绕this.el创建一个包装元素
- 将任何组件渲染到
(例如,在配置中使用this.wrap
)renderTo:this.wrap
- 覆盖
和getValue
以与手动渲染的组件对话setValue
- 如果表单的布局发生更改,您可能需要在
侦听器中手动调整大小resize
- 不要忘记清理您在
方法中创建的任何组件李>beforeDestroy
祝你好运 遵循 此代码将创建一个可重用的TypeAhead/Autocomplete样式字段,用于选择语言
var langs = Ext.create( 'Ext.data.store', {
fields: [ 'label', 'code' ],
data: [
{ code: 'eng', label: 'English' },
{ code: 'ger', label: 'German' },
{ code: 'chi', label: 'Chinese' },
{ code: 'ukr', label: 'Ukranian' },
{ code: 'rus', label: 'Russian' }
]
} );
Ext.define( 'Ext.form.LangSelector', {
extend: 'Ext.form.field.ComboBox',
alias: 'widget.LangSelector',
allowBlank: false,
hideTrigger: true,
width: 225,
displayField: 'label',
valueField: 'code',
forceSelection: true,
minChars: 1,
store: langs
} );
只需将xtype设置为小部件名称,即可在表单中使用该字段:
{
xtype: 'LangSelector'
fieldLabel: 'Language',
name: 'lang'
}
下面是扩展Ext面板的自定义面板示例。您可以扩展任何组件,检查文档中可以使用的字段、方法和事件
Ext.ns('yournamespace');
yournamespace.MyPanel = function(config) {
yournamespace.MyPanel.superclass.constructor.call(this, config);
}
Ext.extend(yournamespace.MyPanel, Ext.Panel, {
myGlobalVariable : undefined,
constructor : function(config) {
yournamespace.MyPanel.superclass.constructor.apply(this, config);
},
initComponent : function() {
this.comboBox = new Ext.form.ComboBox({
fieldLabel: "MyCombo",
store: someStore,
displayField:'My Label',
typeAhead: true,
mode: 'local',
forceSelection: true,
triggerAction: 'all',
emptyText:'',
selectOnFocus:true,
tabIndex: 1,
width: 200
});
// configure the grid
Ext.apply(this, {
listeners: {
'activate': function(p) {
p.doLayout();
},
single:true
},
xtype:"form",
border: false,
layout:"absolute",
labelAlign:"top",
bodyStyle:"padding: 15px",
width: 350,
height: 75,
items:[{
xtype:"panel",
layout:"form",
x:"10",
y:"10",
labelAlign:"top",
border:false,
items:[this.comboBox]
},
{
xtype:"panel",
layout:"form",
x:"230",
y:"26",
labelAlign:"top",
border:false,
items:[{
xtype:'button',
handler: this.someAction.createDelegate(this),
text: 'Some Action'
}]
}]
}); // eo apply
yournamespace.MyPanel.superclass.initComponent.apply(this, arguments);
this.comboBox.on('select', function(combo, record, index) {
this.myGlobalVariable = record.get("something");
}, this);
}, // eo function initComponent
someAction : function() {
//do something
},
getMyGlobalVariable : function() {
return this.myGlobalVariable;
}
}); // eo extend
Ext.reg('mypanel', yournamespace.MyPanel);
现在这很酷。前几天,我做了一把小提琴来回答另一个问题,然后才意识到我离题了。这是你最后提醒我的问题。谢谢 因此,以下是从另一个组件实现自定义字段所需的步骤:
initComponent
方法中创建子项(而不是在渲染时)。这是因为外部代码可以合理地期望在initComponent
之后实例化组件的所有依赖对象(例如,向它们添加侦听器)
此外,您可以对自己友善,在调用super方法之前创建子对象。如果在super方法之后创建子方法,则可能会在子方法尚未实例化时调用字段的setValue
方法(请参见下文)
initComponent: function() {
this.childComponent = Ext.create(...);
this.callParent(arguments);
}
如您所见,我正在创建一个组件,这在大多数情况下都是您想要的。但是,您也可以尝试组合多个子组件。在这种情况下,我认为尽快回到众所周知的领域是明智的:也就是说,创建一个容器作为子组件,并在其中进行组合
翻译
然后是渲染问题。起初,我考虑使用渲染容器div,并让子组件在其中渲染自身。但是,在这种情况下,我们不需要模板特性,因此我们也可以使用该方法完全绕过它
我研究了Ext中的其他组件,以了解它们如何管理子组件的渲染。我在BoundList及其分页工具栏中找到了一个很好的示例(请参阅)。因此,为了获得子组件的标记,我们可以结合使用子组件的方法
下面是我们字段的getSubTplMarkup
实现:
getSubTplMarkup: function() {
// generateMarkup will append to the passed empty array and return it
var buffer = Ext.DomHelper.generateMarkup(this.childComponent.getRenderTree(), []);
// but we want to return a single string
return buffer.join('');
}
现在,这还不够。我们了解到组件呈现中还有另一个重要部分:调用子组件的方法。幸运的是,我们的自定义字段将在需要时调用自己的方法
finishRenderChildren: function() {
this.callParent(arguments);
this.childComponent.finishRender();
}
调整大小
现在,我们的子对象将被渲染到正确的位置,但它将不遵守其父字段的大小。对于表单字段来说,这尤其令人恼火,因为这意味着它不会遵守锚定布局
这很容易解决,我们只需要在调整父字段的大小时调整子字段的大小。从我的经验来看,这是一个很大的改进
onResize: function(w, h) {
this.callParent(arguments);
this.childComponent.setSize(w - this.getLabelWidth(), h);
}
Ext.define('My.form.field.TodoList', {
// Extend from Ext.form.field.Base for all the label related business
extend: 'Ext.form.field.Base'
,alias: 'widget.todolist'
// --- Child component creation ---
,initComponent: function() {
// Create the component
// This is better to do it here in initComponent, because it is a legitimate
// expectationfor external code that all dependant objects are created after
// initComponent (to add listeners, etc.)
// I will use this.grid for semantical access (value), and this.childComponent
// for generic issues (rendering)
this.grid = this.childComponent = Ext.create('Ext.grid.Panel', {
hideHeaders: true
,columns: [{dataIndex: 'value', flex: 1}]
,store: {
fields: ['value']
,data: []
}
,height: this.height || 150
,width: this.width || 150
,tbar: [{
text: 'Add'
,scope: this
,handler: function() {
var value = prompt("Value?");
if (value !== null) {
this.grid.getStore().add({value: value});
}
}
},{
text: "Remove"
,itemId: 'removeButton'
,disabled: true // initial state
,scope: this
,handler: function() {
var grid = this.grid,
selModel = grid.getSelectionModel(),
store = grid.getStore();
store.remove(selModel.getSelection());
}
}]
,listeners: {
scope: this
,selectionchange: function(selModel, selection) {
var removeButton = this.grid.down('#removeButton');
removeButton.setDisabled(Ext.isEmpty(selection));
}
}
});
// field events
this.grid.store.on({
scope: this
,datachanged: this.checkChange
});
this.callParent(arguments);
}
// --- Rendering ---
// Generates the child component markup and let Ext.form.field.Base handle the rest
,getSubTplMarkup: function() {
// generateMarkup will append to the passed empty array and return it
var buffer = Ext.DomHelper.generateMarkup(this.childComponent.getRenderTree(), []);
// but we want to return a single string
return buffer.join('');
}
// Regular containers implements this method to call finishRender for each of their
// child, and we need to do the same for the component to display smoothly
,finishRenderChildren: function() {
this.callParent(arguments);
this.childComponent.finishRender();
}
// --- Resizing ---
// This is important for layout notably
,onResize: function(w, h) {
this.callParent(arguments);
this.childComponent.setSize(w - this.getLabelWidth(), h);
}
// --- Value handling ---
// This part will be specific to your component of course
,setValue: function(values) {
var data = [];
if (values) {
Ext.each(values, function(value) {
data.push({value: value});
});
}
this.grid.getStore().loadData(data);
}
,getValue: function() {
var data = [];
this.grid.getStore().each(function(record) {
data.push(record.get('value'));
});
return data;
}
,getSubmitValue: function() {
return this.getValue().join(',');
}
});
Ext.define('MyApp.widget.MyGridField', {
extend: 'Ext.form.FieldContainer',
alias: 'widget.mygridfield',
layout: 'fit',
initComponent: function()
{
this.callParent(arguments);
this.valueGrid = Ext.widget({
xtype: 'grid',
store: Ext.create('Ext.data.JsonStore', {
fields: ['name', 'value'],
data: this.value
}),
columns: [
{
text: 'Name',
dataIndex: 'name',
flex: 3
},
{
text: 'Value',
dataIndex: 'value',
flex: 1
}
]
});
this.add(this.valueGrid);
},
setValue: function(value)
{
this.valueGrid.getStore().loadData(value);
},
getValue: function()
{
// left as an exercise for the reader :P
}
});
Ext.define('QWA.form.field.DateTime', {
extend: 'Ext.form.FieldContainer',
mixins: {
field: 'Ext.form.field.Field'
},
alias: 'widget.datetimefield',
layout: 'hbox',
width: 200,
height: 22,
combineErrors: true,
msgTarget: 'side',
submitFormat: 'c',
dateCfg: null,
timeCfg: null,
initComponent: function () {
var me = this;
if (!me.dateCfg) me.dateCfg = {};
if (!me.timeCfg) me.timeCfg = {};
me.buildField();
me.callParent();
me.dateField = me.down('datefield')
me.timeField = me.down('timefield')
me.initField();
},
//@private
buildField: function () {
var me = this;
me.items = [
Ext.apply({
xtype: 'datefield',
submitValue: false,
format: 'd.m.Y',
width: 100,
flex: 2
}, me.dateCfg),
Ext.apply({
xtype: 'timefield',
submitValue: false,
format: 'H:i',
width: 80,
flex: 1
}, me.timeCfg)]
},
getValue: function () {
var me = this,
value,
date = me.dateField.getSubmitValue(),
dateFormat = me.dateField.format,
time = me.timeField.getSubmitValue(),
timeFormat = me.timeField.format;
if (date) {
if (time) {
value = Ext.Date.parse(date + ' ' + time, me.getFormat());
} else {
value = me.dateField.getValue();
}
}
return value;
},
setValue: function (value) {
var me = this;
me.dateField.setValue(value);
me.timeField.setValue(value);
},
getSubmitData: function () {
var me = this,
data = null;
if (!me.disabled && me.submitValue && !me.isFileUpload()) {
data = {},
value = me.getValue(),
data[me.getName()] = '' + value ? Ext.Date.format(value, me.submitFormat) : null;
}
return data;
},
getFormat: function () {
var me = this;
return (me.dateField.submitFormat || me.dateField.format) + " " + (me.timeField.submitFormat || me.timeField.format)
}
});
Ext.define('app.view.form.field.CustomField', {
extend: 'Ext.form.field.Base',
requires: [
/* require further components */
],
/* custom configs & callbacks */
getValue: function(v){
/* override function getValue() */
},
setValue: function(v){
/* override function setValue() */
},
getSubTplData: [
/* most likely needs to be overridden */
],
initComponent: function(){
/* further code on event initComponent */
this.callParent(arguments);
}
});