ASP.NET MVC: Делаем голосование на сайте при помощи Knockout
Сайтостроение | создано: 15.01.2013 | опубликовано: 15.01.2013 | обновлено: 13.01.2024 | просмотров: 12170
В предыдущей статье экспериментировали с формой обратной связи. Магия Knockout позволила максимально быстро и просто реализовать функционал формы обратной связи. На этот раз попробуем что-нибудь поинтереснее, например, "голосование".
В качестве вступления
Я буду строить решение на базе проекта, который был опубликован в предыдущей статье “ASP.NET MVC: И снова про форму обратной связи или куда еще втыкнуть Knockout”. Хочу чтобы статья получилась короткая, но максимально информативная.
Задача на эту статью – “реализовать голосование на сайте, с отображением результатов”.
Рис.1 “Опрос на сайте (пользователь еще не голосовал)”
И второй вариант отображения формы, это когда пользователь уже проголосовал.
Рис.2 “Опрос на сайте (пользователь уже голосовал)”
Звучит и выглядит просто, и поэтому я стороной обойду загрузку данных с сервера, запись данных на сервер, чтение настроек и проверку cookies, чтобы по возможности максимально рассказать о Knockout. Итак, если учесть, что все приготовления выполнены, то можно сразу приступить к программированию.
Входные данные
В папке Scripts/App создаю новый файл site.vm.polls.js. Теперь первым делом определю формат данных для объекта (Poll) (листинг 1):
var pollData = { "question": "За двумя зайцами погонишься...", "answers": [ { votes: 11, name: "ни одного не поймаешь." }, { votes: 5, name: "больше двух не поймаешь." }, { votes: 15, name: "ни одного волка не поймаешь." }, { votes: 9, name: "порвешься на две части." }, { votes: 39, name: "зачем бегать, лучше на авто!" } ], "selectedAnswer": null};
Это, так сказать, входные данные. В моем случае, они “жестко” прописаны в коде. Вы же можете сделать получение данных для опроса с сервера, через Web API, или с сервиса WCF. Немного комментариев относительно представленного кода.
- Строка 2: Сам вопрос.
- Строка 3-8: Это предложенные варианты ответа на поставленный вопрос.
- Строка 10: Ответ, который возможно уже дал пользователь, посещая сайт. Вы можете хранить его в базе данных или даже в cookies. Сейчас он у меня null, поэтому форма должна отобразить список ответов и вопрос. А если поле было заполнено (выбранный ответ), то форма должна отобразить результаты голосования.
Класс Answer (Ответ)
В предыдущем листинге (листинг 1) в строка 3 эти самые ответы на вопрос. “Завернем” класс в JavaScript (листинг 2):
site.vm.Answer = function (value, total) { var isSelected = ko.observable(false), votes = ko.observable(total), name = ko.observable(value); return { isSelected: isSelected, name: name, votes: votes }; },
Всё просто, единственное, что хотелось быть отметить, что свойство isSelected будет указыват на то выбран ли ответ в списке вариантов или нет.
Класс Poll (Опрос)
Этот класс и будет представляет “входные данные” (см. листинг 1). Самый большой класс в моем решении (листинг 3).
site.vm.Poll = function (data) { var selectedItem = ko.observable(new site.vm.Answer()), question = ko.observable(data.question), answers = ko.observableArray([]), answer = ko.observable(), maxVotes = ko.observable(), init = function () { var max = 0; var i; for (i in data.answers) { var cur = data.answers[i].votes; if (cur && cur>max) { max = cur; } } max = Math.round(max * 1.1); maxVotes(max); for (i in data.answers) { answers.push(new site.vm.Answer(data.answers[i].name, data.answers[i].votes)); } if (data.selectedAnswer) { answer(data.selectedAnswer); } }, setSelected = function (item) { if (!item.isSelected()) { selectedItem(item.name()); ko.utils.arrayForEach(answers(), function (i) { if (i.isSelected && i.isSelected()) { i.isSelected(false); } }); item.isSelected(true); } }; init(); return { max: maxVotes, selectedItem: selectedItem, question: question, answers: answers, answer: answer, setSelected: setSelected }; };
И опять немного комментариев:
- Строка 2: Представляет выбранный пользователем ответ.
- Строка 6: Вообще-то, это свойство (maxVotes) я ввел только для того, чтобы правильно работал jQuery UI Progressbar, при помощи которого я планирую отображать результаты.
- Строка 7: Инициализация класса на основании входных данных. Запуск инициализации происходит в строке 35.
ViewModel для формы
На форме может быть много опросов, в моем варианте он один. Создаем ViewModel для формы Index.cshtml. Вот так выглядит pollViewModel (листинг 4):
site.vm.pollViewModel = function () { var message = ko.observable("Выберите ваш вариант ответа на вопрос."), poll = site.vm.Poll(pollData), save = function () { poll.answer(poll.selectedItem()); }; return { save: save, poll: poll, message: message }; }();
Строка 2: Сообщение для пользователя. Мне не пригодилось, но можно использовать для вывода ошибок при работе сервиса.
Строка 3: Создаем экземпляр класса голосования (см. листинг 3).
Строка 4: Функция сохранения результатов голосования. В моем решении это просто обновления свойства answer. Но вы можете отправить результат на сервер или сохранить в cookies.
Магия Knockout – applyBindings
Голосование (Poll) я планирую показать на главной странице (то есть в котроллере Home, метод – Index). Я открыл представление (index.cshtml) и полностью его отчистил, и добавил туда такую разметку:
<div data-bind="ifnot: poll.answer"> <h2 data-bind="text: poll.question">h2> <h4 data-bind="text: message">h4> <div data-bind="foreach: poll.answers"> <div data-bind="click: $parent.poll.setSelected" class="big"> <input type="radio" value="true" data-bind="checked: isSelected().toString()" class="poll" /> <span data-bind="text: name, css: {'selected': isSelected}">span> div> div> <p> <button data-bind="click: save">голосоватьbutton> p> div> <div data-bind="if: poll.answer"> <h2 data-bind="text: poll.question() ' ' poll.answer()">h2> <div data-bind="foreach: poll.answers"> <span data-bind="text: name" >span> <div data-bind="progressbar: {value: votes(), max: $parent.poll.max()}">div> <br/> div> div>
Всё на форме просто и, надеюсь, понятно. Единственное, что хотелось бы отметить, для отображения результатов в шаблоне (строка 18) используется bindingHandler, который я назвал “progressbar”. Когда вы установите nuget-пакет JsJite, у вас в папке scripts/app появится файл site.bindingHandlers.js, в котором уже много полезных “штучек”.
Заключение
В качестве заключения хочу предложить демо-проект и поэкспериментировать.