2014年8月18日 星期一

ASP.NET MVC - 下拉選單的日期選擇器 Part.7 - Validation

前面有關下拉選單日期選擇器都講得差不多,該注意的地方甚至於不該講的都講了,但還是有個部分沒有提到,那就是資料驗證的部分,ASP.NET MVC 其中的一個特點就是資料驗證,從後端到前端的部分都有提供了功能與支援,尤其是前端與 jQuery Validation 的整合,我們在模型類別裡加註適當資料驗證的 Attribute,在前端的表單輸入就能夠做到資料驗證與錯誤訊息的顯示。

而我們到目前為止已經完成的 DateDropDownList 在資料驗證上還有什麼需要注意以及需要修改的地方呢?就看這一篇的說明。

 


我們在原本的 DemoModel 這個類別裡還沒有加上任何的 Data Annotation Attributes,所以就著手加上了 atttribute,三個屬性都設定為必要的,

image

直接跳到檢視頁面,在沒有輸入任何資料的情況下,直接按下「Create」送出 POST,理所當然地一定會顯示錯誤訊息,但是…… BirthDate 的部分卻沒有將錯誤訊息給顯示出來,

image

這裡就不拐彎抹角,雖然檢視頁面的原始碼裡是有 BirthDate 的 input element,而 BirthDate 的 attribute 裡也有資料驗證的訊息資料,但是 jQuery Validation 對於隱藏欄位或是不顯示的欄位是不會處理資料驗證與錯誤訊息顯示,

image

無法在頁面上按下送出表單的時候將錯誤訊息顯示,但是送到後端後的伺服器端資料驗證還是會檢查出 BirthDate 的錯誤,所以會再將驗證結果送回前端並且在檢視頁面上顯示驗證訊息,

image

image

雖然一樣是可以在伺服器端去驗證輸入的資料,但是在使用者的操作習慣與提供對使用者友善的操作介面,最好還是在送出表單前就要做好檢查並且顯示錯誤訊息。

前面有說到,jQuery Validation 對於隱藏欄位或是不顯示的欄位是不會處理資料驗證與錯誤訊息顯示,所以我們就讓 jQuery Validation 對 BirthDate 欄位的隱藏給忽略,

image

這麼一來當頁面上沒有選擇日期的情況下,按下 Create 時就會將錯誤訊息給顯示出來。

image

 

但如果並沒有將日期項目選擇完畢呢?是否也會顯示錯誤訊息?

image

不對呀,不應該是這樣的,我日期還沒有選完整,理當應該是錯誤的日期,所以應該要顯示錯誤訊息,但怎麼會沒有顯示呢?所以就直接觀察 BirthDate 這個輸入欄位的 value 為何,

image

可以看到在 BirthDate 的 value,年的部分是我們所選擇的項目,而月份與日期卻自動補上了現在的日期,但是在頁面上我們還沒有選擇月份與日期呀。

這是因為原本 dateDropDowns 的原始碼裡就是這樣的設計,

image

image

當沒有選擇月份與日期的時候,就會使用 _date 裡的日期資料,所以在 BirthDate 的 value 就會抓得到日期資料,但是這樣的做法是會造成一些問題的,因為使用者並沒有完成日期的選擇,但是前端程式卻給了一個預設的日期資料,這並不是使用者所輸入的正確日期資料,那麼在這樣的設計之下就會造成使用上的混亂,是必須要做適當的修改。

這邊的修改就是當使用者沒有完成日期的年、月、日的選擇,三個部分只要缺少一個就是錯誤,前端程式不應該自動的補上缺少的日期部分,而應該就使用者實際的輸入狀況來做判斷並顯示錯誤訊息,這邊的判斷以及錯誤訊息的顯示會是在使用者按下表單的 Submit 後再進行,以下是修改之後的完整程式內容,

jquery.dateLists.dev.js

;
(function ($) {
    $.fn.dateDropDowns = function (options) {
 
        var defaults = {
            dateFormat: 'dd-mm-yy',
            monthNames: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
            yearStart: ((new Date()).getFullYear() - 100).toString(), yearEnd: (new Date()).getFullYear().toString(),
            taiwanCalendarYear: false,
            yearOption: '',
            monthOption: '',
            dayOption: ''
        };
        var options = $.extend(defaults, options);
 
        return this.each(function () {
            var obj = $(this);
            var body = obj.html();
 
            var _container_name = obj.attr('id') + '_dateLists';
            var _container_name_day = _container_name + '_day';
            var _container_name_month = _container_name + '_month';
            var _container_name_year = _container_name + '_year';
 
            var _startDate = obj.val();
            var _date = new Date();
            var _seperator = (defaults.dateFormat.indexOf('/') >; -1) ? '/' : '-';
 
            GetStartDate();
            AddLists();
            PopulateLists();
            SetupChangeHandlers();
 
            //=========================================================================
            function GetStartDate() {
                if (_startDate.length >; 0) {
                    var _dateSections = defaults.dateFormat.split(_seperator);
                    var _dateParts = _startDate.split(_seperator);
                    var _newDate = new Date();
                    for (_x = 0; _x < _dateParts.length; _x++) {
                        if (_dateSections[_x].toLowerCase().indexOf('d') >; -1) {
                            _newDate.setDate(_dateParts[_x]);
                        }
                        else if (_dateSections[_x].toLowerCase().indexOf('m') >; -1) {
                            _newDate.setMonth(_dateParts[_x] - 1);
                        }
                        else if (_dateSections[_x].toLowerCase().indexOf('y') >; -1) {
                            _newDate.setYear(_dateParts[_x]);
                        }
                    }
 
                    _date = _newDate;
                }
            }
 
            //=========================================================================
            function AddLists() {
                var _dateSections = defaults.dateFormat.split(_seperator);
                var _obj = obj;
                obj.replaceWith('<;div id="' + _container_name + '"></div>');
 
                var $targetElement = $('#' + _container_name);
                for (_x = 0; _x < _dateSections.length; _x++) {
 
                    if (_dateSections[_x].toLowerCase().indexOf('d') >; -1) {
                        $('<;select></select>')
                            .attr({
                                'id': _container_name_day + '_list',
                                'name': _container_name_day + '_list',
                                'style': 'margin: 5px;'
                            })
                            .appendTo($targetElement);
                    }
                    else if (_dateSections[_x].toLowerCase().indexOf('m') >; -1) {
                        $('<;select></select>')
                            .attr({
                                'id': _container_name_month + '_list',
                                'name': _container_name_month + '_list',
                                'style': 'margin: 5px;'
                            })
                            .appendTo($targetElement);
                    }
                    else if (_dateSections[_x].toLowerCase().indexOf('y') >; -1) {
                        $('<;select></select>')
                            .attr({
                                'id': _container_name_year + '_list',
                                'name': _container_name_year + '_list',
                                'style': 'margin: 5px;'
                            })
                            .appendTo($targetElement);
                    }
                }
 
                $targetElement.append(_obj);
                obj.hide();
            }
 
            //=========================================================================
 
            function PopulateLists() {
                PopulateDayList();
                PopulateMonthList();
                PopulateYearList();
            }
 
            function PopulateDayList() {
                /// <;summary>
                /// Populate Day List
                /// <;/summary>
 
                var _currentMonth = _date.getMonth() + 1;
                var _start = 1;
 
                _daysInMonth = GetMonthDays(_currentMonth, _date.getFullYear()) + 1;
                var $targetElement = $('#' + _container_name_day + '_list');
                var selectedDay = $targetElement.val();
                $targetElement.children().remove();
                $('<;option></option>')
                    .attr('value', '').text(defaults.dayOption)
                    .appendTo($targetElement);
                for (_x = _start; _x < _daysInMonth; _x++) {
                    var _selected = _startDate.length >; 0
                        ? (_date.getDate() == _x) ? 'selected="true"' : ''
                        : '';
                    $('<option ' + _selected + '></option>')
                        .attr('value', _x).text(_x).appendTo($targetElement);
                }
 
                if (selectedDay) {
                    var isOptionExists = $targetElement.find('option[value=' + selectedDay + ']').length >; 0;
                    $targetElement.val(isOptionExists ? selectedDay : '');
                }
            }
 
            function GetMonthDays(prmMonth, prmYear) {
                /// <;summary>
                /// Get the number of days for a given month
                /// <;/summary>
                /// <;param name="prmMonth"></param>
                /// <;param name="prmYear"></param>
                /// <;returns type=""></returns>
 
                var _daysInMonth = 31;
                if (prmMonth == 4 || prmMonth == 6 || prmMonth == 9 || prmMonth == 11) {
                    _daysInMonth = 30;
                }
                else if (prmMonth == 2) {
                    _daysInMonth = (prmYear % 4) == 0 ? 29 : 28;
                }
                return _daysInMonth;
            }
 
            function PopulateMonthList() {
                /// <;summary>
                /// Populate Month List
                /// <;/summary>
 
                var $targetElement = $('#' + _container_name_month + '_list');
                $targetElement.children().remove();
                $('<;option></option>')
                    .attr('value', '').text(defaults.monthOption)
                    .appendTo($targetElement);
                for (_x = 0; _x < 12; _x++) {
                    var _selected = _startDate.length >; 0
                        ? ((_date.getMonth()) == _x) ? 'selected="true"' : ''
                        : '';
                    $('<option ' + _selected + '></option>')
                        .attr('value', _x)
                        .text(defaults.monthNames[_x])
                        .appendTo($targetElement);
                }
            }
 
            function PopulateYearList() {
                /// <;summary>
                /// Populate Year List
                /// <;/summary>
 
                var $targetElement = $('#' + _container_name_year + '_list');
                $targetElement.children().remove();
 
                $('<;option></option>')
                    .attr('value', '').text(defaults.yearOption)
                    .appendTo($targetElement);
                var _yStart = parseInt(defaults.yearStart, 10);
                var _yEnd = parseInt(defaults.yearEnd, 10);
                if (_yEnd < _yStart) {
                    var temp = _yStart;
                    _yStart = _yEnd;
                    _yEnd = temp;
                }
 
                for (_x = _yEnd; _x >= _yStart; _x--) {
                    var _selected = _startDate.length >; 0
                        ? ((_date.getFullYear()) == _x) ? 'selected="true"' : ''
                        : '';
                    $('<option ' + _selected + '></option>')
                        .attr('value', _x)
                        .text(defaults.taiwanCalendarYear ? _x - 1911 : _x)
                        .appendTo($targetElement);
                }
            }
 
            //=========================================================================   
 
            function SetupChangeHandlers() {
 
                var $daySelect = $('#' + _container_name_day + '_list');
                var $monthSelect = $('#' + _container_name_month + '_list');
                var $yearSelect = $('#' + _container_name_year + '_list');
                $daySelect.change(function () {
                    _date.setDate($daySelect.val());
                    CreateDate();
                });
                $monthSelect.change(function () {
                    var _newMonth = parseInt($monthSelect.val(), 10);
                    var _days = _date.getDate();
 
                    _daysInMonth = GetMonthDays(_newMonth + 1, _date.getFullYear());
                    if (_days > _daysInMonth) {
                        _days = _daysInMonth;
                    }
 
                    var _newDate = new Date(_date.getFullYear(), _newMonth, _days, 0, 0, 0, 0);
                    _date = _newDate;
 
                    PopulateDayList();
                    CreateDate();
                });
                $yearSelect.change(function () {
                    var _newYear = $yearSelect.val();
                    var _days = _date.getDate();
                    var _month = _date.getMonth();
                    _daysInMonth = GetMonthDays(_month + 1, _newYear);
 
                    if (_days >; _daysInMonth) {
                        _days = _daysInMonth;
                    }
 
                    var _newDate = new Date(_newYear, _month, _days, 0, 0, 0, 0);
                    _date = _newDate;
 
                    PopulateDayList();
                    CreateDate();
                });
            }
 
            function CreateDate() {
 
                var $daySelect = $('#' + _container_name_day + '_list');
                var $monthSelect = $('#' + _container_name_month + '_list');
                var $yearSelect = $('#' + _container_name_year + '_list');
                var dayValue = $daySelect.val();
                var monthValue = $monthSelect.val();
                var yearValue = $yearSelect.val();
 
                var _newDate = defaults.dateFormat.toLowerCase();
                if (isEmpty(yearValue) && isEmpty(monthValue) && isEmpty(yearValue)) {
                    obj.val('');
                }
                else if (!isEmpty(yearValue) &;& !isEmpty(monthValue) && !isEmpty(dayValue)) {
                    var _days = parseInt(dayValue, 10);
                    var _month = parseInt(monthValue, 10) + 1;
                    var _year = parseInt(yearValue, 10);
                    var _dateFormat = defaults.dateFormat;
 
                    if (_dateFormat.indexOf('DD') >; -1 && _days.toString().length < 2) {
                        _days = '0' + _days;
                    }
                    if (_dateFormat.indexOf('MM') >; -1 && _month.toString().length < 2) {
                        _month = '0' + _month;
                    }
 
                    _newDate = _newDate.replace('dd', _days);
                    _newDate = _newDate.replace('mm', _month);
                    _newDate = _newDate.replace('yy', _year);
                    obj.val(_newDate);
                }
                else {
                    _newDate = _newDate.replace('dd', dayValue);
                    _newDate = _newDate.replace('mm', parseInt(monthValue, 10) + 1);
                    _newDate = _newDate.replace('yy', yearValue);
                    obj.val(_newDate);
                }
            }
 
            function isEmpty(str) {
                return (!str || 0 === str.length);
            }
        });
    };
})(jQuery);

程式的部分就不做說明,主要修改的部分是在最後的 CreateDate 這個 function。

 

執行結果

當沒有輸入任何資料的時候會顯示必填的訊息

image

當沒有完成日期的選擇,當按下 Submit 時就會顯示錯誤訊息

image

image

當完成日期的選擇後,再按下 Submit 就會移除錯誤訊息

image

其實這邊是可以做成當日期都有選擇時就立刻移除錯誤訊息,而不用等到按下 Submit 的時候,不過這邊就留待各位去自行修改啦~

 


全部總共七篇有關下拉選單的日期選擇器就到這邊告一段落,應該是沒有其他的東西要做說明,這七篇都是相當基本的內容,但都是在 ASP.NET MVC 開發上會遇到也應該注意的內容,主要目的就是要告訴大家如何靈活應用 ASP.NET MVC 以及善用 Editor Templates 與 Model Binding 所帶來的好處,擺脫以往那種所謂「組裝」或是「拼裝」的舊觀念與作法。

 

系列文章:

ASP.NET MVC - 下拉選單的日期選擇器 Part.1

ASP.NET MVC - 下拉選單的日期選擇器 Part.2

ASP.NET MVC - 下拉選單的日期選擇器 Part.3 - Editor Templates

ASP.NET MVC - 下拉選單的日期選擇器 Part.4 - Editor Templates

ASP.NET MVC - 下拉選單的日期選擇器 Part.5 - Editor Templates

ASP.NET MVC - 下拉選單的日期選擇器 Part.6 - @helper ?

ASP.NET MVC - 下拉選單的日期選擇器 part.7 - Validation

 

以上

 

以上

3 則留言:

  1. 心得分享而已啦,大家可以一起討論。

    回覆刪除
  2. 謝謝 Kevin 前輩提供七篇MVC學習篇章,比起小弟的共獻「為不足道」

    回覆刪除
    回覆
    1. 千萬別這麼說,將心得寫成文章分享出來,都是貢獻。

      刪除

提醒

千萬不要使用 Google Talk (Hangouts) 或 Facebook 及時通訊與我聯繫、提問,因為會掉訊息甚至我是過了好幾天之後才發現到你曾經傳給我訊息過,請多多使用「詢問與建議」(在左邊,就在左邊),另外比較深入的問題討論,或是有牽涉到你實作程式碼的內容,不適合在留言板裡留言討論,請務必使用「詢問與建議」功能(可以夾帶檔案),謝謝。