ExtJS 4 - 如何在必填字段上标记红色星号

24

我有一个问题,需要在一个fieldLabel旁边添加红色星号,当一个字段被标记为“必填”(或allowBlank: false)时。

在ExtJS3中,我们可以通过以下方式轻松地覆盖Ext.layout.FormLayout来实现这个哈克:

Ext.override(Ext.layout.FormLayout, {
    getTemplateArgs: function(field) {
        var noLabelSep = !field.fieldLabel || field.hideLabel;
        var labelSep = (typeof field.labelSeparator == 'undefined' ? this.labelSeparator : field.labelSeparator);
        if (!field.allowBlank) labelSep += '<span style="color: rgb(255, 0, 0); padding-left: 2px;">*</span>';
        return {
            id: field.id,
            label: field.fieldLabel,
            labelStyle: field.labelStyle||this.labelStyle||'',
            elementStyle: this.elementStyle||'',
            labelSeparator: noLabelSep ? '' : labelSep,
            itemCls: (field.itemCls||this.container.itemCls||'') + (field.hideLabel ? ' x-hide-label' : ''),
            clearCls: field.clearCls || 'x-form-clear-left'
        };
    }
});

但是在 ExtJS4 中这是不可能的。 FormLayout 不再适用,标签实际上是通过使用名为 Ext.form.Labelable 的 mixins 由 Ext.form.field.Base 渲染的。

可悲的是,无论是扩展 Ext.form.Labelable 还是覆盖 Ext.form.Labelable 都对我无济于事。从 Ext.form.field.Base 扩展的组件不会受到任何影响。即使我交换了 mixins,模板仍然无法工作。

所以这里是我的解决方案,在 Ext.form.field.Base 上进行了非常严格的覆盖,它的工作原理如下(查看我的示例

这仅适用于 ExtJS 4.0.7。要在 ExtJS 4.0.2a 上使用它,您需要根据 4.0.2a 中找到的一个修改 labelableRenderTpl /src/form/Labelable.js

(function() {

    var overrides =  {
        labelableRenderTpl: [
            '<tpl if="!hideLabel && !(!fieldLabel && hideEmptyLabel)">',
                '<label id="{id}-labelEl"<tpl if="inputId"> for="{inputId}"</tpl> class="{labelCls}"',
                    '<tpl if="labelStyle"> style="{labelStyle}"</tpl>>',
                    '<tpl if="fieldLabel">{fieldLabel}{labelSeparator}</tpl>',
                    '<tpl if="!allowBlank"><span style="color:red">*</span></tpl>',
                '</label>',
            '</tpl>',
            '<div class="{baseBodyCls} {fieldBodyCls}" id="{id}-bodyEl" role="presentation">{subTplMarkup}</div>',
            '<div id="{id}-errorEl" class="{errorMsgCls}" style="display:none"></div>',
            '<div class="{clearCls}" role="presentation"><!-- --></div>',
            {
                compiled: true,
                disableFormats: true
            }
        ],

        /**
         * @protected
         * Generates the arguments for the field decorations {@link #labelableRenderTpl rendering template}.
         * @return {Object} The template arguments
         */
        getLabelableRenderData: function() {
            var me = this,
                labelAlign = me.labelAlign,
                labelCls = me.labelCls,
                labelClsExtra = me.labelClsExtra,
                labelPad = me.labelPad,
                labelStyle;

            // Calculate label styles up front rather than in the Field layout for speed; this
            // is safe because label alignment/width/pad are not expected to change.
            if (labelAlign === 'top') {
                labelStyle = 'margin-bottom:' + labelPad + 'px;';
            } else {
                labelStyle = 'margin-right:' + labelPad + 'px;';
                // Add the width for border-box browsers; will be set by the Field layout for content-box
                if (Ext.isBorderBox) {
                    labelStyle += 'width:' + me.labelWidth + 'px;';
                }
            }

            return Ext.copyTo(
                {
                    inputId: me.getInputId(),
                    fieldLabel: me.getFieldLabel(),
                    labelCls: labelClsExtra ? labelCls + ' ' + labelClsExtra : labelCls,
                    labelStyle: labelStyle + (me.labelStyle || ''),
                    subTplMarkup: me.getSubTplMarkup(),
                    allowBlank: me.allowBlank
                },
                me,
                'hideLabel,hideEmptyLabel,fieldBodyCls,baseBodyCls,errorMsgCls,clearCls,labelSeparator',
                true
            );
        }
    };


    //Both field.Base and FieldContainer are affected, so need to cater for.
    Ext.override(Ext.form.field.Base, overrides);
    Ext.override(Ext.form.FieldContainer, overrides);


})();

所以我给所有必填字段添加了漂亮的星号。

问题在于,有没有更容易实现类似功能的方式?覆盖方法比较严格,最好能使用mixin,但是mixin无法覆盖行为。

注意

这是因为我定制了需要从基本TextComboFieldContainer扩展的字段。 扩展字段中的mixin甚至不会影响模板。它们太固执了。也许目前最好的方法是通过覆盖基类... 查看工作示例


不错...这也是我下个月要面对的问题。 - sra
谢谢。到目前为止,这是唯一可行的解决方案。Mixin不能真正覆盖Ext.form.field.Base中的Mixin。我想知道是否有更好的解决方案。 - Lionel Chan
11个回答

28

我有一个更为简洁的解决方案。我建议使用表单的 'beforeadd' 事件,像这样:

Ext.define('Ext.ux.form', {
    extend: 'Ext.form.Panel',
    initComponent: function() {
      this.on('beforeadd', function(me, field){
        if (!field.allowBlank)
          field.labelSeparator += '<span style="color: rgb(255, 0, 0); padding-left: 2px;">*</span>';
      });
      this.callParent(arguments);
    }
});

这里有一个演示


我已经将你的代码包含在我的扩展面板中,它运行得非常好!如果没有更好的答案,那么你可能会得到奖励 :) 尽管两个答案看起来都很棒,但是你的更好,因为我已经在使用扩展的 form.Panel。非常感谢你!太棒了! - Lionel Chan
我知道allowBlank只是一个配置而不是属性,但是如果在组件渲染后设置allowBlank = true,这将不起作用。 - Tiago Sippert

6

您仍然可以像在ExtJS3中一样重写布局组件,但由于没有fieldLayout,因此我已经重写了Ext.layout.Layout。这与molecule man的解决方案非常相似,但更通用。可适用于表单以外的其他容器中使用的字段。

Ext.override(Ext.layout.Layout, {
    renderItem: function(item, target, position) {
      if (item && !item.rendered && item.isFieldLabelable && item.fieldLabel && item.allowBlank == false) {
        item.fieldLabel += ' <span class="req" style="color:red">*</span>';
      }
      this.callOverridden(arguments);
    }
});

这个方案比你的简单,但不一定更好。可以看一个在 fieldsets 中使用的例子here


不错!我个人也会使用扩展锚点布局而不是覆盖。 - Molecular Man
你们让我很难决定。我喜欢两个答案!该死!几天后我会给你们点赞好吗?两个都可行,但我更喜欢Molecule的答案,因为我还扩展了一个自定义表单面板,所以很可能会使用他的(不需要污染js文件或打开另一个js文件)。 - Lionel Chan

5

您还可以覆盖和扩展没有任何功能,只需创建一个控制器动作,如下所示:

Ext.define('MyApp.controller.MyForm', {
    extend: 'Ext.app.Controller',

    markMandatoryFields: function(field, options) {
        if (field && field.isFieldLabelable && field.fieldLabel && field.allowBlank == false) {
            field.fieldLabel += ' <span class="req" style="color:red">*</span>';
        }
    },

    init: function() {
        this.control({
            "field": {
                beforerender: this.markMandatoryFields
            }
        });
    }
});

5

对于 Ext JS 4.1.1,这个可以正常工作:

Ext.define('com.ideas.widgets.Base', {
    override : 'Ext.form.field.Base',
    initComponent : function()
    {
        if(this.allowBlank!==undefined && !this.allowBlank)
        {
            if(!this.labelSeparator)
            {
                this.labelSeparator = "";
            }
            this.labelSeparator += '<span style="color:red">*</span>';
        }
        this.callParent(arguments);
    }
});

我在应用程序目录下直接创建了一个文件Overrides.js。通过在app.js文件的require[]数组中添加Overrides.js,将其钩入我的应用程序中。非常好用,感谢您的建议! - John Gordon
是的,那也是我使用它的方式。 :) - Varun Achar
1
你可以设置labelClsExtra 属性,以便更加灵活。我认为使用一个 CSS 类来标识必填字段的标签是更好的方法。我使用了 this.labelClsExtra = 'x-form-required-field';,然后在 CSS 中类似于 label.x-form-required-field { text-decoration: underline; } 这样的方式实现。 - Jonathan Morales Vélez

4
一种更优雅的方法是为标记为 allowBlank=false 的任何字段标签添加 CSS 类,并在 CSS 中设置必填指示器的样式。
Ext.define('Ext.ux.form', {

    extend: 'Ext.form.Panel',

    listeners: {
        'beforeadd': function(){
            if (!field.allowBlank) {
                field.labelClsExtra = 'x-required';
            }
        }
    }

});

您可以使用:after伪类工具在CSS中设置字段标签的样式:
.x-required:after {
    content: ' *';
    color: red;
    font-weight: bold;
}

4

Extjs 4.1

当字段有 fieldLabel 时,请使用:

fieldLabel: 'Name',
allowBlank: false,    
afterLabelTextTpl: "<span style="color:red;font-weight:bold" data-qtip="Required">*</span>"

如果字段没有fieldLabel,请使用配置选项的组合,hideLabel配置必须为false:
//hideLabel: false
name: 'lastName',
emptyText: "Last Name",
allowBlank: false,    
labelWidth: 0,
fieldLabel: '',
hideEmptyLabel: false,
afterLabelTextTpl: '<span style="color:red;font-weight:bold" data-qtip="Required">*</span>'

此外,您可以将此解决方案与Drasill插件混合使用,以便一次轻松地自定义所有字段。

2
我已经为此制作了一个插件。
适用于ExtJS 4.1(至少)。
使用方法如下:
Ext.create('Ext.form.Panel', {
  ...
  plugins : "formlabelrequired"
  ...
});

或者,自定义“星号”:
Ext.create('Ext.form.Panel', {
  ...
  plugins : [{ptype:"formlabelrequired", asterisk:" (mandatory)"}]
  ...
});

这是插件的代码:

/**
 * Plugin (ptype = 'formlabelrequired') that adds "asterisk" to labels
 * for Fields with "allowBlank: false".
 */
Ext.define('Ext.ux.plugin.form.LabelRequired', {

        extend: 'Ext.AbstractPlugin',

        alias: 'plugin.formlabelrequired',

        asterisk: ' <span class="required"> *</span>',

        constructor: function() {

            this.callParent(arguments);

        },

        init: function(formPanel) {
            formPanel.on('beforerender', this.onBeforeRender, this);
        },

        /**
         * @private
         * Adds asterisk to labels.
         */
        onBeforeRender: function(formPanel) {

            var i, len, items;

            items = formPanel.query('[allowBlank=false]');

            for (i = 0, len = items.length; i < len; i++) {
                item = items[i];
                item.afterLabelTextTpl = (item.afterLabelTextTpl || "") + this.asterisk;
            }

            return true;

        }

    });

1
其实我认为使用fieldSubTpl和/或labelableRenderTpl添加星号比使用事件监听器更简洁。事件可以被停止,监听器可以被分离。
我认为OP(Lionel Chan)的担心是使用Ext.override有点不规范,他是100%正确的。但如果我们在表单配置级别传递自定义tpl,则情况就不那么糟糕了:
Ext.create('Ext.form.Panel',{
    defaults:{
        fieldSubTpl:['<input id="{id}" type="{type}" ', 
        '<tpl if="name">name="{name}" </tpl>', 
        '<tpl if="size">size="{size}" </tpl>', 
        '<tpl if="tabIdx">tabIndex="{tabIdx}" </tpl>', 
        'class="{fieldCls} {typeCls}" autocomplete="off" />',
        '<span>',
        '<tpl if="allowBlank==false">*</tpl>',
        '</span>',
        {
            compiled: true, 
            disableFormats: true
    }]},
    items : [{
        xtype : 'textfield',.....

模板可能有问题,我还没有尝试过。


这是一种更干净的方法,但也是一种错误的方法。像fieldSubTpl这样设置默认值是错误的,因为即使文档中说它属于Ext.form.field.Base并且有一个默认值,实际上它在几乎所有其他字段类中都被覆盖了。至于labelableRenderTpl,我们可以重写类并修改tpl,或者将其设置为默认值,因为基本字段只从labelable中获取它,如果没有定义,则只需获取它。但问题又来了,对于labelableRenderTpl,allowBlank数据未发送,因为它不被认为对标签必要。 - nscrob
同意你们关于labelableRenderTpl和fieldSubTpl的观点。我在想也许正确的方法是将空白状态视为错误状态,以某种方式使用"invalidText" 和 "msgTarget"来样式化 * ,并在字段被渲染时调用 "validate"。因此,* 将会显示出来,因为初始值是无效的。 - Chao
不,那种方法有点过于严厉了,因为即使在无效字段下面也会呈现红色波浪线,看起来很致命! :) - Lionel Chan
哈哈,没错,但我们可以进行样式设计。我想我试图做的是尽可能多地使用现有的ExtJS库,同时确保视图和逻辑之间有分离关系。 - Chao
但我仍然认为4行黑客代码比大量修改更加强大。我的表单已经定义,因此取消事件的排列组合较少,所以应该没问题。 - Lionel Chan

1
如果您不想覆盖任何内容,请将星号放在labelSeparator中:
{
  xtype: 'textfield',
  fieldLabel: 'Name',
  name: 'requestor_name',
  allowBlank: false,
  labelSeparator : ': <span style="color:red">*</span>'
}

0

有很多方法可以做到这一点,其中一些你可以在上面找到,我建议的是:

 {
    xtype : 'label',
    html:'<span>First Name</span><span style="color: rgb(255, 0, 0); padding-left: 2px;">*</span>',
    width:50,
 }

你可以将星号和标签文本放在同一个HTML属性中。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接