一個被全世界錯誤使用的表單控件--日曆控件

日曆就是日曆,但它不是合適的時間選擇器。 html

大部分的表單中凡是涉及時間選擇的,都會採用日曆選擇器。大部分UI框架也會默認提供。在許多場景中使用這種控件並不合適,特別是一些二流的公司甚至沒有和日曆同步時間。 app

好比: 框架

看起來挺炫,其實毫無心義。這個控件不會和字段內的時間同步,照理說控件顯示的時候應該反應字段內的日期和時間。若是我倖幸苦苦選擇到指定日期,不當心點錯了一格,我必須從新來一遍。 post

再有一個狀況是,若是是輸入生日,那麼必須在年份上下按鈕點100次(若是100歲的話)。 ui

採用html內置的選擇器是更加合理的時間選擇器。 this

如下是我寫的控件: spa

{fieldName: "publishdAt",inputLabel: '發表日期',years:"-10,+10",ftype:'datetime'/*date*/,required:true,validators:{},gridSize:"1-2"},

配置很好理解,years以當前時間(不必定後面再說)爲參考,-10,+10,固然是顯示先後10年(根據須要),若是是編輯,字段自己已經有了值,那麼這個先後是根據這個值的年份先後10年。 設計

這樣設計還有問題,若是年份超越了這個範圍,不是沒法輸入了嗎?當你點擊日期select的時候,最下方有一個「其它」選項,當你選擇這個值時,select會變成text字段,容許你輸入任何年份,當你輸入年份,text失去焦點以後,select字段又會回來。 code

還有一個隱藏的功能,就是當你改變年月或者月份的時候,這個控件的view會重畫,何須呢?由於月份不一樣,日期的範圍會變化。 orm

外觀:


最後是源代碼:

var Lang = Y.Lang,
    Ob = Y.Object,
    fieldsViewNs = Y.namespace("M3958.CusFieldView");

fieldsViewNs.CusDateTimeField = Y.Base.create('cusDateTimeField', Y.View, [], {
    events: {
        'input': {blur: 'onInputBlur'}
    },
    /**
     * 控制input的寬度能夠經過設置width style.
     */
    selectTpl: Y.Template.Micro.compile(
            '<select class="<%= this.dmodel.type %>" style="display:inline;margin-left:2px;">' +
                '<% for (var i=this.dmodel.range[0]; i<=this.dmodel.range[1];i++) { %>' +
                    '<option value=<%= i %><%= (i == data.dmodel.cur) ? " selected" : "" %>><%= i + data.dmodel.postfix %></option>' +
                '<% } %>' +
           '</select>'
            ),
    template: Y.Template.Micro.compile(
            '<label for="<%= this.guid %>"><%= this.fieldDescription.inputLabel %></label>' +
            '<input type="text" placeholder="年份" style="width:50px;display:none;">'+
            '<select id="<%= this.guid %>" class="<%= this.dmodel.type %>" style="display:inline;">' +
                '<% for (var i=this.dmodel.range[0]; i<=this.dmodel.range[1];i++) { %>' +
                    '<option value=<%= i %><%= (i == data.dmodel.cur) ? " selected" : "" %>><%= i + data.dmodel.postfix %></option>' +
                '<% } %>' +
                '<option value="otheryear">其它</option>' +
            '</select>'
    ),
    initializer: function (){
        var container = this.get('container'),
            self = this,
            model = this.get('model'),
            fieldDescription = this.get('fieldDescription');
        
        model.after('clientValidateErrorChange',function(e){
            var validateResult = e.newVal;

            if (!validateResult) {
                return;
            }
            if (validateResult.isForField(fieldDescription.fieldName)) {
                if (validateResult.msg) {
                    container.addClass("validate-error");
                } else {
                    container.removeClass("validate-error");
                }
            }
        },this);
        
        this.on('destroy',function(e){
            container.all('select').detachAll();
        });
    },
    render: function () {
        var container = this.get('container'),
            fieldDescription = this.get('fieldDescription'),
            formStyle = this.get('formStyle'),
            label = fieldDescription.inputLabel,
            tnum =  fieldDescription.ftype === 'datetime' ? 6 : 3,
            helpInline = fieldDescription.helpInline,
            self = this,
            html,
            yselect,
            mselect,
            dselect,
            docfragment,
            guid = Y.guid(),
            dmodel,
            idx = 1,
            inputNode;
        
        if (container.all('select')) {
            container.all('select').detachAll();
        }
        
        if(Lang.isFunction(helpInline)){
            helpInline = helpInline.call(this);
        }
         
        if(Lang.isFunction(label)){
            label = label.call(this);
        }
        
        if(fieldDescription.required && (Lang.isBoolean(fieldDescription.required) || Lang.isString(fieldDescription.required))){
            label = '<strong>' + label + '</strong>';
        }
        
        dmodel = this.createDmodel();
        html = this.template({
                guid: guid,
                dmodel: dmodel[0],
                fieldDescription: fieldDescription
            }
        );
        
        container.setHTML(html);
        
        docfragment = Y.one(Y.config.doc.createDocumentFragment());
        
        for (;idx < tnum;idx++) {
            docfragment.append(this.selectTpl({
                    dmodel: dmodel[idx]
                }
            ));
        }
        
        container.append(docfragment);
        
        if (formStyle === 'aligned') {
            container.addClass('pure-control-group');
        } else {
            if (fieldDescription.gridSize) {
                container.addClass('pure-u-' + fieldDescription.gridSize);
            } else {
                container.addClass('pure-u-1');                
            }
        }
        
        yselect = container.one('select.year');
        mselect = container.one('select.month');
        dselect = container.one('select.date');
        
        yselect.after('change',function(){
            self.afterSelectChange('y');
        });
        
        mselect.after('change',function(){
            self.afterSelectChange('m');
        });
        
        dselect.after('change',function(){
            self.afterSelectChange('d');
        });
        
        return this;
    },
    createDmodel: function(){
        var container = this.get('container'),
            fieldDescription = this.get('fieldDescription'),
            years = fieldDescription.years,
            model = this.get('model'),
            value = model.get(fieldDescription.fieldName),
            now = value ? new Date(value) : new Date(),
            self = this,
            yearstart,
            yearend,
            dmodel = [],
            yearr = years.split(",");

        if ((yearr[0].indexOf('-') === -1) && (yearr[0].indexOf('+') === -1)) { //沒有加減號,值如今
            yearstart = now.getFullYear();
        } else {
            yearstart = now.getFullYear() + parseInt(yearr[0]);
        }
            
        if ((yearr[1].indexOf('-') === -1) && (yearr[1].indexOf('+') === -1)) { //沒有加減號,值如今
            yearend = now.getFullYear();
        } else {
            yearend = now.getFullYear() + parseInt(yearr[1]);
        }
        

        dmodel.push({range:[yearstart,yearend],cur:now.getFullYear(),type:'year',postfix:'年'});
        dmodel.push({range:[1,12],cur:now.getMonth() + 1,type:'month',postfix:'月'});
        dmodel.push({range:[1,Y.M3958.Util.DateTime.monthEnd(now.getFullYear(),now.getMonth())],cur:now.getDate(),type:'date',postfix:'日'});
        dmodel.push({range:[0,23],cur:now.getHours(),type:'hour',postfix:'時'});
        dmodel.push({range:[0,59],cur:now.getMinutes(),type:'minute',postfix:'分'});
        dmodel.push({range:[0,59],cur:now.getSeconds(),type:'second',postfix:'秒'});
        
        return dmodel;

    },
    getInputValue : function(){
        var container = this.get('container'),
            input = container.one('select');
        
        return input.get('value');
    },
    getDateTimeLong : function(){
        return Y.M3958.UtilsClass.DateUtils.tolong(this.getInputValue());
    },
    _getValue : function(){
        var container = this.get('container'),
            inputs = container.all('select'),
            value = [];
        inputs.each(function(nd){
            value.push(parseInt(nd.get('value'),10));
        });
        return value;
    },
    saveValue : function(){
        var fieldDescription = this.get('fieldDescription'),
            model = this.get('model'),
            v = this._getValue(),
            dtv = new Date();
        
        Y.Array.each(v,function(one,idx){
            if (idx === 0) {
                dtv.setFullYear(one);
            } else if (idx === 1) {
                dtv.setMonth(one - 1);
            } else if (idx === 2) {
                dtv.setDate(one);
            } else if (idx === 3) {
                dtv.setHours(one);
            } else if (idx === 4) {
                dtv.setMinutes(one);
            } else if (idx === 5) {
                dtv.setSeconds(one);
            }
        });
            
        if (model) {
            model.set(fieldDescription.fieldName,dtv.getTime()); 
        }
    },
    afterSelectChange: function(t){
        var fieldDescription = this.get('fieldDescription'),
            container = this.get('container'),
            yselect = container.one('select.year'),
            dinput = container.one('input'),
            model = this.get('model');
            
        if (t === 'y' && yselect.get('value') === 'otheryear') {
            dinput.setStyle('display','inline');
            yselect.setStyle('display','none');
            dinput.focus();
            return;
        }
        this.saveValue();
        
        if (model && model.validateOneField) {
            model.validateOneField(fieldDescription);
        }
        
        if (t === 'm' || t === 'y') {
            this.render();
        }
    },
    onInputBlur: function() {
        var fieldDescription = this.get('fieldDescription'),
            container = this.get('container'),
            yselect = container.one('select.year'),
            dvalue = container.one('input').get('value'),
            model = this.get('model'),
            value = model.get(fieldDescription.fieldName),
            d = new Date(value);
        /**
         * 只是改變year的值,其它的不變。
         */
        if (dvalue) {
            d.setFullYear(parseInt(dvalue,10));
            model.set(fieldDescription.fieldName,d.getTime());
            this.render();
        }
        
    }
},{
    ATTRS: {
        container: {
            valueFn: function () {
                return Y.Node.create('<div></div>');
            }
        },
        fieldDescription : {
            value : null
        },
        model: {
            value: null
        }
    }
});
這個select change事件不會冒泡(ie<10),因此必須拙劣的在每一個select上面訂閱事件,看官若是有辦法,不要吝嗇告訴我一聲。
相關文章
相關標籤/搜索