Предыдущий ролик Следующий ролик  

Видео урок: Implementing a page grid with Knockout

Основы ASP.NET MVC 5

В этом видео я покажу вам, как с помощью Knockout быстро реализовать сетку, содержащую элементы для разбивки текста по страницам и предназначенную для отображения истории операций по текущему счету. Мы добавим функциональность, которая позволяет распечатывать выписку по счету. Зададим значение href с помощью Url.Action, чтобы создать URL для действия Statement контроллера CheckingAccount. И передадим сюда параметр id, значение которого будет совпадать со значением свойства checkingAccountId, уже имеющегося во ViewBag.

Затем добавим в CheckingAccount новое действие. Действие Statement принимает параметр id - идентификатор текущего счета. Этот счет мы возьмем из базы данных с помощью метода Find. На этот раз мы воспользуемся свойством навигации Transactions, которое позволяет получать быстрый доступ ко всем операциям этого счета и передавать их в представление в виде списка. Я уже подготовил соответствующее представление Statement, которое расположено в файлах упражнений в папке Chapter 7 -> Start.

Просто перетащим это представление в папку Views -> CheckingAccount и откроем его. Как видите, в нем есть директива model, задающая совокупность операций. Таблице, которая будет отображать эти операции, присвоено несколько bootstrap-классов, для того чтобы она выглядела более привлекательно. Наша модель операций очень проста. Даты операций нам были не нужны, поэтому я поместил сюда идентификатор операции для того, чтобы в нашей сетке было хотя бы 2 столбца. В нижнем колонтитуле таблицы расположены элементы для разбивки информации по страницам.

На данный момент это просто иконки "стрелка влево" и "стрелка вправо". Но мы связываем события нажатия на эти стрелки с поведением модели представления и отображаем между ними номер текущей страницы. Под скриптом библиотеки Knockout расположена вспомогательная функция, форматирующая суммы операций таким образом, чтобы в них оставалось только 2 знака после запятой. Далее идет модель представления, а за ней - вызов applyBindings, активирующий эту модель. Модель представления обладает следующими свойствами. Массив, содержащий все операции, которые должны отображаться в сетке.

В нем будут содержаться все базовые операции, а не только операции текущей страницы. Нужно пояснить, что эти операции будут попадать на страницу в виде JSON-массива. Затем идет свойство pageSize. Это количество операций, отображаемых на каждой странице. На протяжении всего жизненного цикла страницы значения этих двух свойств изменяться не будут. Поэтому необязательно делать их наблюдаемыми. Нашим первым наблюдаемым свойством будет currentPage, поскольку очевидно, что при изменении его значения необходимо обновить страницу. Пусть начальным значением его будет "1".

Кстати, если бы мне нужно было сделать свойство transactions наблюдаемым, например, чтобы можно было редактировать количество операций в сетке, тогда вместо ko.observable я использовал бы ko.observableArray. Список currentTransactions будет вычисляемым, наблюдаемым объектом, поскольку является подмножеством базовой совокупности операций и вычисляется на основании значения свойства currentPage. Чтобы получить это подмножество, нужно вычислить начальный и конечный индекс массива, а затем можно воспользоваться методом Slice.

Начальный индекс вычисляется по формуле: номер текущей страницы за вычетом 1, умноженный на значение свойства pageSize. Обратите внимание, поскольку свойство currentPage не является вычисляемым, для получения значения этого свойства нужно вызвать его как функцию. Конечный индекс вычисляется как "начальный индекс + значение свойства pageSize". Наконец, есть еще два метода, предназначенных для перехода на следующую или предыдущую страницу. Теперь нам нужно связать свойства и методы модели представления с разметкой и сформировать JSON-массив операций. Но перед тем как это сделать, добавим некоторые начальные данные для одного из моих счетов, чтобы получить действительно удобное для анализа количество операций. Итак, в файлах упражнений в папке Chapter 7 -> Start есть файл под названием "Seed data". Я просто скопирую эти строки и вставлю их в метод Seed, находящийся в папке Migrations в классе Configuration.cs. Независимо от того, существует ли в системе пользователь Администратор, в самом низу метода Seed я добавлю несколько операций для текущего счета с идентификатором 5, который на самом деле является счетом Администратора. Если вы работаете с собственной версией и хотите использовать эти начальные данные, просто убедитесь, что счет с указанным идентификатором существует в вашей базе данных. Чтобы добавить эти данные в вашу базу данных, нужно просто выполнить команду Update-Database в консоли диспетчера пакетов. Вроде как все работает. Давайте вернемся к нашему представлению. Далее мне нужно внедрить мой C# список операций в JavaScript в виде JSON-массива. Делается это очень легко - с помощью синтаксиса Razor в классе JsonConvert.

Для этого нужно пространство имен Newtonsoft.Json. Его мы получаем через пакет NuGet, который уже установлен в шаблоне этого проекта. Итак, я буду использовать JsonConvert.SerializeObject и передам в него свою модель. Кроме того, нам нужно передать сюда экземпляр JsonSerializerSettings. Это нужно для того, чтобы сообщить конвертеру о необходимости игнорирования циклических ссылок, которые встречаются между операциями и текущими счетами, поскольку в каждом объекте хранится ссылка на другой объект. Сделать это можно, задав для ReferenceLoopHandling значение ReferenceLoopHandling.Ignore. Но проблема в том, что по умолчанию Razor пропускает закодированные HTML строки. Нужно убедиться, что используемые нами кавычки и JSON-данные не закодированны как HTML-сущности. Чтобы решить эту проблему, можно просто обернуть все это в Html.Raw. Обратите внимание, что в элементе tbody только одна строка.

Ничего страшного в этом нет, поскольку Knockout может использовать это в качестве шаблона, если мы добавим к tbody атрибут data-bind и присвоим ему значение "foreach:currentTransactions". Что касается ячеек, я добавлю атрибут data-bind со значением "text:Id" и еще один data-bind со значением formattedPrice, передав в него переменную Amount. В код элементов управления можно добавить атрибут data-bind и присвоить ему значение "click:", а затем указать название функции: previousPage и nextPage.

Не забывайте, что нам еще нужно показать здесь номер текущей страницы. Это еще один атрибут data-bind со значением "text:currentPage". Вот теперь мы готовы провести первое испытание. Перейдем на главную страницу, поскольку я хочу удостовериться в том, что нажимаю на ссылку, передающую параметр Id, и запустим приложение. Если нажать кнопку "Выписка по счету" (Print statement), то мы увидим постраничную сетку. Мы еще не касались того, что происходит, когда пользователь пытается перейти на последнюю страницу результатов или на первую.

Я не хочу, чтобы кто-то переходил на страницу 4 или 0, или страницу с отрицательным номером. Итак, давайте решим эту проблему. Для стрелки влево все очень просто. Можно просто воспользоваться привязкой visible и указать, что эта стрелка должна отображаться только тогда, когда номер текущей страницы больше 1. Над стрелкой вправо придется немного потрудиться, потому что необходимо вычислить, какая страница является последней. Можно вычислять это в виде свойства нашей модели представления. Поместим его после свойства currentPage. Нам нужно будет просто взять длину массива Transactions и разделить ее на значение свойства pageSize.

Но если мы получим число с остатком, то нужно будет округлить его, чтобы у нас оставалась дополнительная страница для нескольких последних операций. Поэтому мы помещаем этот код в Math.Ceil, чтобы выделить целую часть этого числа. Затем то же самое можно сделать с visible и проверить, действительно ли номер текущей страницы меньше номера последней. Сохраним изменения, обновим страницу и посмотрим, что получилось. Теперь все готово. Мы только что с нуля создали славную сетку с разбивкой информации по страницам, конечно, не без помощи Knockout.