Это часть 2 живого руководства, вы не сможете следовать ему, если не пройдете часть 1. (Часть 3 здесь)
Добро пожаловать назад. Здесь, в Неми, тихое и солнечное воскресенье, нет ничего лучше, чем послушать благоприятную мелодию и продолжить работу над датпикером.
Вот что у нас получилось:
Прежде чем мы пойдем дальше, мне нужно сделать существующий код более рациональным, например:
Переименуйте переменные и функции:
- viewDate - ›navDate
- changeViewDate - ›changeNavMonth
- canChange - ›canChangeNavMonth
changeNaveMonth теперь принимает только один аргумент:
changeNavMonth(num: number){ if(this.canChangeNavMonth(num)){ this.navDate.add(num, 'month'); } }
canChangeNavMonth также принимает только один аргумент:
canChangeNavMonth(num: number): boolean{ const clonedDate = moment(this.navDate); clonedDate.add(num, 'month'); const minDate = moment().add(-1, ‘month’); const maxDate = moment().add(1, ‘year’); return clonedDate.isBetween(minDate, maxDate); }
Это было необходимо? Нет, но это делает его более читабельным. Теперь давайте отключим кнопку с шевроном, если дата выходит за пределы указанного диапазона. Все, что нам нужно сделать, это добавить такую директиву к нашим кнопкам:
[disabled] = “!canChangeNavMonth(-1)”
В заключение, это новые datepicker.component.ts и datepicker.component.html:
Намного лучше. И вот чего мы добились:
А теперь пора сделать и этот небольшой заголовок буднего дня динамичным.
5 - Маленький заголовок буднего дня
Оказывается, у каждого региона свой официальный первый день недели. В Великобритании воскресенье. В Италии, где я сейчас нахожусь, понедельник. Последовательность представлена числами от 0 до 6 как в датах JS, так и в объектах Moment.js. В Moment.js на будний день можно ссылаться по его номеру в соответствии с языковыми стандартами с помощью метода weekday (). Итак, британское воскресенье или итальянский понедельник (в зависимости от региона) будет:
moment().weekday(0);
Чтобы отобразить его в удобочитаемом формате, всего с тремя буквами, как нам нужно:
moment().weekday(0).format(‘ddd’);
Будет отображено «Sun» (локаль «en») или «lun» (локаль «it»; да, в этой локали оно не пишется с заглавной буквы). Чтобы отобразить заголовок, все, что нам нужно сделать, - это перебрать эту строку кода по массиву чисел от 0 до 6. На самом деле. Итак, в нашем компоненте мы можем объявить пустой массив строк, представляющих наш заголовок (weekDaysHeaderArr), и заполнить его в ngOnInit с помощью простой функции makeHeader:
И, наконец, замените жестко запрограммированный заголовок на ngFor в нашем datepicker.component.html:
Мы сделали это! Вот локаль "it":
К сожалению, похоже, что в Angular нет встроенного конвейера с заглавными буквами. Мы подумаем об этом позже. Хорошая работа!
6 - Фактические… даты!
Мы могли бы делать в нашем шаблоне всевозможные запутанные ngFor - ngIf - ngSwitch, чтобы правильно отображать эти маленькие числа. Но я предпочитаю ограничить логику в файле ts и передать шаблону простой массив для итерации.
Наша цель - создать массив, содержащий нули для любого пустого пространства сетки и номер дня для любого дня месяца. Кроме того, я также хочу знать, доступна ли дата для выбора. Имеет смысл? Допустим, в отображаемом месяце 30 дней, он начинается во вторник, и мы используем локаль «en», массив должен быть примерно таким:
[{value: 0, available: false}, {value: 0, available: false}, {value: 1, available: true}, {value: 2, available: true},..., {value: 30, available: true}, {value: 0, available: false}, {value: 0, available: false}]
Для любого данного месяца длина этого массива будет равна: количеству дней в месяце + количеству дней между первым днем недели в соответствии с локалью, в которой мы находимся, и первым днем месяца + количество дней между последним днем месяца и последним днем недели в локали.
Холодно, мы используем Moment.js. Чтобы получить дату, представляющую первый день месяца, который мы отображаем (не забывайте всегда клонировать дату, прежде чем изменять ее, если вы не собираетесь ее менять):
const firstDayDate = moment(navDate); firstDayDate.startOf('month');
Чтобы узнать номер дня недели и, следовательно, сколько пустых ячеек нам нужно распечатать перед выводом чисел:
const initialEmptyCells = firstDayDate.weekday();
Точно так же мы можем подсчитать, сколько пустых ячеек содержит массив в конце (на этот раз проверяя, сколько ячеек нам нужно пройти оттуда до конца недели):
const lastDayDate = moment(navDate); lastDayDate.endOf('month'); const lastEmptyCells = 6 - lastDayDate.weekday();
Чтобы узнать общее количество отображаемых дней в месяце:
const daysInMonth = navDate.daysInMonth();
Итак, массив, который мы хотим построить, имеет следующую длину:
const arrayLength = initialEmptyCells + lastEmptyCells + daysInMonth;
Теперь, когда мы знаем его длину, мы можем приступить к его созданию с помощью цикла for:
let gridArr: Array<number> = []; for(let i = 0; i< arrayLength; i++){ let obj = {}; if(i<initialEmptyCells || i>initialEmptyCells + daysInMonth -1){ obj.value = 0; obj.available = false; } else { obj.value = i - initialEmptyCells +1; obj.available = isAvailable(i - initialEmptyCells +1); } gridArr.push(obj); }
Где isAvailable - это функция, которая будет принимать число и проверять, доступен этот день или нет. На данный момент он будет просто возвращать true все время, кроме числа 5, просто чтобы посмотреть, как выглядит отключенная кнопка:
isAvailable(num: number){ if(num === 5){return false}; else {return true}; }
Блин, теперь мне нужно написать код ... подождите ...
Итак, мы объявляем переменную gridArr, которая будет содержать наш массив, и функцию makeGrid, которая создаст его в зависимости от отображаемой текущей даты:
Мы будем вызывать функцию ngOnInit и каждый раз обновлять navDate.
Теперь мы можем, наконец, избавиться от жестко запрограммированной сетки в нашем шаблоне, пройдя цикл по gridArr и добавив класс «is-disabled», если день недоступен:
И вот результат (помните, мы отключили каждую пятую просто ради этого):
Молодец!! Теперь нам просто нужно захватить дату, когда мы ее выберем. Но для этого нам понадобится… часть 3 !!