使用avalon MVVM架構打造jquery ui的日曆

來源:互聯網
上載者:User

我直接把jquery datepicker的結構抄過來,類名也照搬。於是一個換膚的日曆就誕生了。

        <div ms-controller="datepicker">            <div id="ui-datepicker-div" class="ui-datepicker ui-widget ui-widget-content ui-helper-clearfix ui-corner-all" style="display:block">                 <div class="ui-datepicker-header ui-widget-header ui-helper-clearfix ui-corner-all">                    <a class="ui-datepicker-prev ui-corner-all" title="Prev"                        ms-click="prevMonth"                       ms-hover="ui-state-hover"                       ms-hover-1="ui-datepicker-prev-hover"                       >                        <span class="ui-icon ui-icon-circle-triangle-w">Prev</span></a>                    <a class="ui-datepicker-next ui-corner-all"  title="Next"                        ms-click="nextMonth"                       ms-hover="ui-state-hover"                       ms-hover-1="ui-datepicker-next-hover"                       >                        <span class="ui-icon ui-icon-circle-triangle-e">Next</span></a>                    <div class="ui-datepicker-title">                        <select  ms-each-month="$months" ms-if="changeMonth" ms-model="currentMonth" >                            <option value="{{month}}" ms-selected="currentMonth == month">{{month+1}}月</option>                        </select>                        <select  ms-each-year="candidateYears" ms-if="changeYear" ms-model="currentYear" >                            <option value="{{year}}" ms-selected="currentYear == year">{{year}}年</option>                        </select>                        {{title}}                    </div>                </div>                <table class="ui-datepicker-calendar" >                    <thead>                        <tr ms-each-date="$weeks">                            <th ms-class-ui-datepicker-week-end="$first">                                <span title="星期{{date}}">{{date}}</span>                            </th>                        </tr>                    </thead>                    <tbody ms-each-week="currentWeeks" ms-click="selectDay">                        <tr ms-each-day="week">                            <td ms-class-ui-datepicker-other-month="Number(day.split('-')[1]) != currentMonth"                                ms-class-ui-datepicker-week-end="$first || $last"                                ms-class-ui-state-disabled="day.split('-')[3] == 1"                                 ms-class-ui-datepicker-unselectable="day.split('-')[3] == 1"                                >                                <a class="ui-state-default"  ms-title='currentMonth' href="#"                                   ms-if="showOtherMonths || Number(day.split('-')[1]) == currentMonth"                                   ms-hover="ui-state-hover"                                   ms-class-ui-state-highlight="isToday"                                   >{{day.split('-')[2]}} </a>                            </td>                    </tbody>                </table>                <div class="ui-datepicker-buttonpane ui-widget-content" ms-if="showButtonPanel">                    <button type="button" class="ui-datepicker-current ui-state-default ui-priority-secondary ui-corner-all"                            ms-hover="ui-state-hover"                            ms-click="backToday"                            >Today</button>                    <button type="button" class="ui-datepicker-close ui-state-default ui-priority-primary ui-corner-all"                            ms-hover="ui-state-hover"                            >Done</button>                </div>            </div>            <link rel="stylesheet" ms-href="http://code.jquery.com/ui/1.10.3/themes/{{theme}}/jquery-ui.css" >            <div><input type="radio" ms-model="changeMonth" />可選擇月份</div>            <div><input type="radio" ms-model="changeYear" />可選擇年份</div>            <div><input type="radio" ms-model="showButtonPanel" />顯示按鈕面板</div>            <div><input type="radio" ms-model="showOtherMonths" />顯示其他月份的日期</div>            <div><select ms-model="theme">                    <option value='start'>start</option>                    <option value='smoothness'>smoothness</option>                    <option value='flick'>flick</option>                    <option value='sunny'>sunny</option>                    <option value='excite-bike'>excite bike</option>                    <option value='black-tie'>black tie</option>                    <option value='trontastic'>trontastic</option>                    <option value='swanky-purse'>swanky purse</option>                    <option value='le-frog'>le frog</option>                    <option value='blitzer'>blitzer</option>                    <option value='dot-luv'>dot luv</option>                    <option value='mint-choc'>mint-choc</option>                    <option value='hot-sneaks'>hot sneaks</option>                    <option value='south-street'> south street</option>                    <option value='humanity'>humanity</option>                    <option value='vader'>vader</option>                    <option value='eggplant'>eggplant</option>                    <option value='cupertino'>cupertino</option>                    <option value='overcast'>overcast</option>                </select>你喜歡的皮膚</div>            <p>你選擇的日期為 {{selectedDate | date('yyyy-MM-dd')}}</p>        </div>

上面的結構分兩部分,最上的日曆,下面的一些表單元素用於控制日曆的配置。

            avalon.ready(function() {               avalon.define("datepicker", function(vm) {                    //配置                    vm.changeYear = false                    vm.changeMonth = false                    vm.minDate = new Date(2013, 3, 25);                    //vm.maxDate                    vm.showOtherMonths = false;                    vm.showButtonPanel = false;                    //目前時間                    vm.selectedDate = new Date;                    vm.currentDate = new Date;                    vm.currentMonth = vm.currentDate.getMonth();                    vm.currentYear = vm.currentDate.getFullYear();                    vm.currentWeeks = getWeeks(vm.currentDate);                    //顯示頂部的年份與月份                    vm.title = {                        get: function() {                            var format = "";                            if (!this.changeYear && this.changeMonth) {                                format = "yyyy年";                            } else if (this.changeYear && !this.changeMonth) {                                format = "MMMM";                            } else if (!this.changeYear && !this.changeMonth) {                                format = "MMMM yyyy年";                            }                            return format && avalon.filters.date(this.currentDate, format);                        }                    };                    //星期顯示                    vm.$weeks = "日一二三四五六".split("");                    //月份下拉式功能表                    vm.$months = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]                    //當月的日期                    function isDisabled(time) {                        var disabled = false;                        if (vm.minDate) {                            disabled = time < vm.minDate;                        }                        if (disabled && vm.maxDate) {                            disabled = time > vm.maxDate;                        }                        return disabled - 0;                    }                    function  getWeeks(cur) {                        vm.currentDate = cur;                        var year = cur.getFullYear();                        var month = cur.getMonth();//得到今天是幾月(0 ~ 11)                        var date = cur.getDate();  //得到今天是幾號 (1 ~ 31)                        cur.setMonth(month + 1);//改為下一個月,                        //由於日期是1 ~ 31, 0則是退到上一個月的最後一天,於是得到這個月的總天數                        cur.setDate(0);                        var num = cur.getDate();                        var next = 6 - cur.getDay();                        var dates = avalon.range(1, num + 1);                        dates = dates.map(function(d) {                            var time = new Date(year, month, d)                            return [year, month, d, isDisabled(time)].join("-");                        });                        cur.setMonth(month);                        cur.setDate(1);//得到當月的第一天                        var prev = cur.getDay();//0 ~ 6                        cur.setDate(date);//還原                        for (var i = 0; i < prev; i++) {//補上上一個月的日期                            cur = new Date(year, month, -1 * i)                            dates.unshift([year, cur.getMonth(), cur.getDate(), isDisabled(cur)].join("-"))                        }                        for (var i = 0; i < next; i++) {//補上下一個月的日期                            cur = new Date(year, month + 1, i + 1)                            dates.push([year, cur.getMonth(), cur.getDate(), isDisabled(cur)].join("-"))                        }                        var ret = [];                        while (dates.length) {//每行七個分組                            ret.push(dates.splice(0, 7));                        }                        return ret;//一個三維數組                    }                    //取得當年的前後20年                    function getYears() {                        var y = vm.currentYear;                        return avalon.range(y - 10, y + 10);                    }                    vm.candidateYears = getYears();                    //點擊事件                    vm.theme = "start";                    vm.nextMonth = function(e) {                        e.preventDefault()                        var d = vm.currentDate;                        var m = d.getMonth();                        d.setMonth(m + 1);                        m = d.getMonth();                        if (m === 0) {                            var y = d.getFullYear();                            vm.currentYear = y;                        }                        vm.currentMonth = m;                    };                    //點擊事件                    vm.prevMonth = function(e) {                        e.preventDefault()                        var d = vm.currentDate;                        var m = d.getMonth();                        d.setMonth(m - 1);                        m = d.getMonth();                        if (m === 11) {                            var y = d.getFullYear();                            vm.currentYear = y;                        }                        vm.currentMonth = m;                    };                    //偵聽                    vm.$watch("currentMonth", function(val) {                        var d = vm.currentDate;                        d.setMonth(val);                        vm.currentWeeks = getWeeks(d);                        vm.title = NaN;                    });                    vm.$watch("currentYear", function(val) {                        var d = vm.currentDate;                        d.setFullYear(val)                        vm.currentWeeks = getWeeks(d);                        vm.title = NaN;                    });                    //高亮當前選中的日期                    vm.selectDay = function(e) {                        var el = e.target;                        e.preventDefault()                        if (el.tagName === "A" && el.parentNode.tagName === "TD" && !/disabled/.test(el.className)) {                            var d = el.$scope.day.split('-')                            vm.selectedDate = new Date(d[0], d[1], d[2]);                        }                    };                    //高亮今天的日期                    var today = new Date;                    var atoday = [today.getFullYear(), today.getMonth(), today.getDate()];                    vm.isToday = function() {                        var day = this.$scope.day;                        return day.slice(0, day.lastIndexOf("-")) === atoday.join("-");                    };                    vm.backToday = function() {                        vm.currentMonth = atoday[1];                        vm.currentYear = atoday[0];                    }                });                avalon.scan();            });

這個JS代碼比起先前的更清晰,放棄使用$fire這個危險的操作,建議大家也不要用它。因為如果沒有嚴格的值變動檢測, 這很容易引起無限遞迴。
現在avalon已經完全重用現有的節點,不會像過去那樣每次都清空然後又添加。因此效能更好。


Prev

Next

{{month+1}}月{{year}}年

{{title}}

{{date}}
{{day.split('-')[2]}}
Today
Done

可選擇月份可選擇年份顯示按鈕面板顯示其他月份的日期startsmoothnessflicksunnyexcite bikeblack tietrontasticswanky pursele frogblitzerdot luvmint-chochot sneaks south streethumanityvadereggplantcupertinoovercast

選擇你喜歡的皮膚(註:這些樣式都受到部落格園染指了)

你選擇的日期為 {{selectedDate | date('yyyy-MM-dd')}}

所有JS代碼不到150行,就能涵蓋jquery ui datepicker(2000多行)的絕大多數功能。如果努力一點,把模板也封裝一下,其他功能也跟進,最多也是500行的規模。可謂MVVM的威力。而且這樣寫JS,可讀性非常好,思路不會像著jQuery那樣跟著CSS運算式——“這個元素是在哪裡,該添加類名還是移除類名……”

用了MVVM後,我們寫代碼的思路將是這個樣子

  1. 先把HTML搞出來;
  2. 把重複的部分轉換為一個each綁定,VM中對應一個數組;
  3. 把頁面視情況要顯示隱藏,要切換類名的東西,在VM中由一個布爾屬性來控制;
  4. 事件回調裡再來也不進行DOM操作,而是對這些數組進行shift, pop操作,布爾屬性進行反轉;
  5. 把這些邏輯封到一個VM中
  6. 最後對視圖進行綁定

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.