Kendo UI Date Picker 民國年格式 | Kendo UI Date Picker Taiwan calander format

背景

一個 Grid,在編輯時用 popup,內容是 template,
有個日期欄位,
使用者想要輸入 1060101 或者 10601011735 這種日期或日期時間格式
不想要選取也不想要打「-」、空白或「:」

研究了一下也提問在官方討論區了,官方是說不支援民國年這種格式,
實際上你輸入 1900 年之前的日期,也會無法 set,
所以也不能用民國 106 年直接套上 DatePicker/DateTimePicker,而且萬年曆也會不對,

這裡就用 textbox 自幹一個字串轉日期的方法
(如果想要讓後端收到的值仍然有「-」、空白或「:」,是可以搭配 mask)

首先在原本的 template 的時間輸入框前面加入一個 input textbox

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<input
class="k-textbox"
data-bind="value:myDateTW"
id="myDateTW"
name="myDateTW"
placeholder="民國年yyyMMdd"
style="width: 200px;"
tabindex="110"
/>
<span class="k-invalid-msg" data-for="myDateTW"></span>
<input
data-bind="value:myDate"
id="myDate"
name="myDate"
style="display: none; width: 250px;"
tabindex="110"
/>
<span class="k-invalid-msg" data-for="myDate"></span>

然後準備好字串轉日期、日期轉字串的處理,
這裡列的方法是我很直觀的去寫,沒有做什麼效能分析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
//傳入值與格式,分析與轉換成JS日期物件
function parseTWDate(val, format) {
var result = { result: true, msg: '', DateObj: null };

if (typeof val === 'undefined') {
result.result = false;
return result;
}

//只接受數字與字串型態
if (typeof val !== 'number' && typeof val !== 'string') {
result.result = false;
return result;
}
//檢查格式字串
if (
format !== 'yyyMMdd' &&
format !== 'yyyMMddHHmm' &&
format !== 'yyyMMddHHmmss'
) {
result.result = false;
result.msg = '不支援的日期格式設定';
return result;
}
//只接受數字不接受符號
if (/^\d+$/.test(val) === false) {
result.result = false;
return result;
}
var str = '';

//數字型態轉文字
if (typeof val === 'number') {
str = val.toString();
} else {
str = val;
}

//從後往前兩位兩位取出字串,轉數字後做驗證
var sec = 0;
var min = 0;
var hr = 0;
var day = 0;
var mon = 0;
var year = 0;
var movingCount = 0;
if (format === 'yyyMMddHHmmss') {
if (str.length < 11) {
result.result = false;
return result;
}
sec = parseInt(
str.substring(str.length - movingCount - 2, str.length - movingCount),
10
);
if (sec === NaN || sec > 59 || sec < 0) {
result.result = false;
return result;
}
movingCount += 2;
}
if (format === 'yyyMMddHHmm' || format === 'yyyMMddHHmmss') {
if (str.length < 9) {
result.result = false;
return result;
}
min = parseInt(
str.substring(str.length - movingCount - 2, str.length - movingCount),
10
);
if (min === NaN || min > 59 || min < 0) {
result.result = false;
return result;
}
movingCount += 2;

hr = parseInt(
str.substring(str.length - movingCount - 2, str.length - movingCount),
10
);
if (hr === NaN || hr > 23 || hr < 0) {
result.result = false;
return result;
}
movingCount += 2;
}
if (str.length < 5) {
result.result = false;
return result;
}
day = parseInt(
str.substring(str.length - movingCount - 2, str.length - movingCount),
10
);
if (day === NaN || day > 31 || day < 1) {
result.result = false;
return result;
}
movingCount += 2;

mon = parseInt(
str.substring(str.length - movingCount - 2, str.length - movingCount),
10
);
if (mon === NaN || mon > 12 || mon < 1) {
result.result = false;
return result;
}
movingCount += 2;

year = parseInt(str.substring(0, str.length - movingCount), 10);
if (year === NaN || year < 1) {
result.result = false;
return result;
}
//JS的日期的月份是0 base
result.DateObj = new Date(year + 1911, mon - 1, day, hr, min, sec);

return result;
}
//將日期選擇轉成民國年
function getTWDate(val, format) {
var rawYear = val.getFullYear();
var twYear = rawYear - 1911;
var rawMonth = val.getMonth() + 1;
var twMonth = rawMonth.toString().padStart(2, '0');
var rawDay = val.getDate();
var twDay = rawDay.toString().padStart(2, '0');
var strTWDate = twYear + twMonth + twDay;
if (format === 'yyyMMddHHmm' || format === 'yyyMMddHHmmss') {
var hr = val.getHours().toString().padStart(2, '0');
var min = val.getMinutes().toString().padStart(2, '0');
strTWDate = strTWDate + hr + min;
if (format === 'yyyMMddHHmmss') {
var sec = val.getSeconds().toString().padStart(2, '0');
strTWDate = strTWDate + sec;
}
}
return strTWDate;
}

然後在原本的 datasource 的 schema 中加入一個自訂的民國年欄位,類型是字串
這個欄位不會回傳值給 API,只是用來 data bind,
裡面做自定義驗證方法,在民國年字串輸入框跳開時,去跑日期解析,
成功就把日期物件帶回 model,失敗就給予錯誤訊息並回傳 false;
原本的欄位也給他一個驗證是如果跑到了驗證就檢查他的值是否可以解析成日期,
這裡弄了很久是不知如何避免偶爾會出現原欄位跳出必填的問題,
還有編輯時無法透過 model.set 讓他自己 data bind 去改變前端 DOM 的值,
set 下去 model 的值竟然不會改,只能手動去塞值&改 DOM,
改值的時候也要先改 DOM 在做 SET,不然也會有怪現象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
var dataSource = {
schema: {
model: {
fields: {
myDate: {
type: 'date',
validation: {
required: true,
dateFormat: function (input, option) {
if (input.is("[name='myDate']")) {
if (Date.parse(input.val()) !== NaN) {
$('#myDateTW').removeAttr('data-TWdate-msg');
$('#myDate').removeAttr('data-required-msg');
return true;
} else {
$('#myDateTW').attr('data-TWdate-msg', '格式民國年yyyMMdd');
return false;
}
} else {
return true;
}
},
},
},
myDateTW: {
type: 'string',
validation: {
required: true,
TWdate: function (input, option) {
if (input.is("[name='myDateTW']")) {
var val = input.val();
var format = 'yyyMMdd';
var result = parseTWDate(val, format);
if (result.result) {
input.removeAttr('data-TWdate-msg');
//帶回model
var source = $('#myDateTW')[0].kendoBindingTarget.source;
$('#myDate').val(result.DateObj);
source.set('myDate', result.DateObj);
return true;
} else {
input.attr('data-TWdate-msg', '格式民國年' + format);
//帶回model
var source = $('#myDateTW')[0].kendoBindingTarget.source;
$('#myDate').val(val);
source.set('myDate', val);
return false;
}
} else {
return true;
}
},
},
},
},
},
},
};

再來是 grid 新增或編輯時需要把 model 的 date 欄位清空與轉換

1
2
3
4
5
6
7
8
9
10
11
12
13
14
edit: function(e) {
var arg = e;
if (e.model.isNew()) {
//日期型態先清空
e.model.set("myDate", null);
} else {
//民國年要先轉好
var myDate = e.model.get("ㄗㄩDate");
var strMyDateTW = getTWDate(myDate, "yyyMMdd");
e.model.myDateTW = strMyDateTW;
e.model.set("myDateTW", strMyDateTW);
$("#myDateTW").val(strMyDateTW);
}
}

這樣就可以輸入民國年日期轉成西元年日期,編輯時也可以把西元年日期轉回民國年日期了