Админка за 10 минут

Здравствуйте, уважаемое Хабрасообщество!

Я занимаюсь разработкой веб-сайтов. Как правило, это решения под индивидуальные потребности заказчиков. Поэтому я не использую готовые CMS, а предпочитаю складывать кирпичики самостоятельно. Конечно и админскую часть приходиться писать самостоятельно, поскольку она должна выполнять те функции, которые нужны заказчику, но и ничего лишнего не должно быть. И если написать несколько методов для редактирования данных это пол беды, то приходилось ещё и верстать приятный и удобный интерфейс.

Долгое время я использовал Twitter Bootstrap, но он не мог удовлетворить все потребности. Приходилось верстать дополнительные кнопочки и писать скрипты. Но вот однажды, я познакомился с замечательным UI-фреймворком KendoUI от Telerik. Что из этого получилось под катом.


О самом фреймворке уже писалась статья на Хабре. Мы будем использовать библиотеку Web-контролов KendoUI Web. Чтобы понимать, что можно создавать с её помощью можно посетить страничку с демо. Для построения контролов можно использовать как хелперы для ASP.NET, JSP или PHP, так и javascript-виджеты. Последние распространяются по лицензии GPL v3 License, поэтому я использовал именно их. Скачать тот или иной пакет можно здесь. На серверной стороне я использую ASP.NET MVC 4 с пакетом Microsoft ASP.NET Web API OData 4.0.0, уставить который можно с помощью команды PM> Install-Package Microsoft.AspNet.WebApi.OData

Для демонстрации создадим простой класс Article, и добавим ему три свойства разного типа
    public class Article
	{
		public int ID { get; set; }
		public string Title { get; set; }
		public bool Hidden { get; set; }
	}


Далее создадим ApiController для работы с данными. В своем примере я использую Entity Framework, поэтому сразу указываю Scaffolding options



После этого я изменяю только действие GetArticles, так показано в примере ниже. Прежде всего теперь оно возвращает ODataResult. А в качестве параметра принимает ODataQueryOptions, это коллекция сериализованных параметров строки запроса. В самом действии мы получаем коллекцию и общее количество элементов в ней. А после применяем к ней входящие параметры. В результате мы возвращаем коллекцию после применение к ней параметров и общее количество, оно необходимо для пагинации.

    public class ArticlesController : ApiController
    {
        private Storage db = new Storage();

        // GET api/Articles
		public ODataResult<Article> GetArticles(ODataQueryOptions options)
		{
			var items = db.Articles;
			var count = items.Count();
            var res = (IEnumerable<Article>)options.ApplyTo(items);

			return new ODataResult<Article>(res, null, count);
		}

        // GET api/Articles/5
        public Article GetArticle(int id)
        {
            Article article = db.Articles.Find(id);
            if (article == null)
            {
                throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.NotFound));
            }

            return article;
        }

        // PUT api/Articles/5
        public HttpResponseMessage PutArticle(int id, Article article)
        {
            if (ModelState.IsValid && id == article.ID)
            {
                db.Entry(article).State = EntityState.Modified;

                try
                {
                    db.SaveChanges();
                }
                catch (DbUpdateConcurrencyException)
                {
                    return Request.CreateResponse(HttpStatusCode.NotFound);
                }

                return Request.CreateResponse(HttpStatusCode.OK);
            }
            else
            {
                return Request.CreateResponse(HttpStatusCode.BadRequest);
            }
        }

        // POST api/Articles
        public HttpResponseMessage PostArticle(Article article)
        {
            if (ModelState.IsValid)
            {
                db.Articles.Add(article);
                db.SaveChanges();

                HttpResponseMessage response = Request.CreateResponse(HttpStatusCode.Created, article);
                response.Headers.Location = new Uri(Url.Link("DefaultApi", new { id = article.ID }));
                return response;
            }
            else
            {
                return Request.CreateResponse(HttpStatusCode.BadRequest);
            }
        }

        // DELETE api/Articles/5
        public HttpResponseMessage DeleteArticle(int id)
        {
            Article article = db.Articles.Find(id);
            if (article == null)
            {
                return Request.CreateResponse(HttpStatusCode.NotFound);
            }

            db.Articles.Remove(article);

            try
            {
                db.SaveChanges();
            }
            catch (DbUpdateConcurrencyException)
            {
                return Request.CreateResponse(HttpStatusCode.NotFound);
            }

            return Request.CreateResponse(HttpStatusCode.OK, article);
        }
    }


Вот и все. Самое время переходить к клиентской части. Для начала подключим KendoUI и JQuery на страницу.
    <link href="~/Content/kendo/kendo.common.min.css" rel="stylesheet" />
	<link href="~/Content/kendo/kendo.default.min.css" rel="stylesheet" />
	<script src="//ajax.googleapis.com/ajax/libs/jquery/1.8.0/jquery.min.js"></script>
	<script src="~/Scripts/kendo/kendo.web.min.js"></script>


После этого установим и настроим виджет. Подробнее о настройках виджета можно прочитать здесь.

<div id="grid"></div>
	<script>
		$(document).ready(function () {
			$("#grid").kendoGrid({
				dataSource: {
					pageSize: 3,
					serverSorting: true,
					serverFiltering: true,
					serverPaging: true,
					type: 'odata',
					transport: {
						read: {
							url: "/api/articles",
							dataType: "json",
							type: "GET"
						},
						create: {
							url: "/api/articles",
							dataType: "json",
							type: "POST"
						},
						update: {
							url: function (article) {
								return "/api/articles/" + article.ID
							},
							dataType: "json",
							type: "PUT"
						},
						destroy: {
							url: function (article) {
								return "/api/articles/" + article.ID
							},
							dataType: "json",
							type: "DELETE"
						}
					},
					schema: {
						data: function (data) {
							return data.Items;
						},
						total: function (data) {
							return data.Count;
						},
						model: {
							id: "ID",
							fields: {
								ID: { editable: false },
								Title: { type: "string", editable: true, nullable: false, validation: { required: true } },
								Hidden: { type: "boolean", editable: true }
							}
						}
					}
				},
				height: 250,
				filterable: true,
				sortable: true,
				pageable: true,
				toolbar: ["create"],
				editable: "popup",
				columns: [
					{ field: "ID", filterable: false, width: 50 },
					{ field: "Title", title: "Название", width: 300 },
					{ field: "Hidden", title: "Скрыт", width: 100 },
					{ command: ["edit", "destroy"], title: " ", width: "210px" }
				]
			});
		});
	</script>


Готово! Теперь можно запускать и проверять. Менее чем за 10 минут мы создали полноценный интерфейс для управления данными, с возможностью пагинации, сортировки и фильтрации. Удобный и приятный. Спасибо KendoUI и формату OData, а Вам за внимание.






Р.S. Кстати есть возможность локализации виджетов. Для этого необходимо подключить соответствующий скрипт из папки js/cultures, который поставляется в архиве с фреймворком.

Поделиться публикацией
Комментарии 37
    –6
    Yii framework, Gii генератор, менее чем за минуту. www.yiiframework.com/doc/guide/1.1/ru/quickstart.first-app#sec-3
      +3
      Может тот кто минусует может сказать почему я не прав?
        +6
        Может потому-что:
        Yii is a high-performance PHP framework best for developing Web 2.0 applications.

          –1
          Ну да, пойду напишу статью как это сделать на PHP framework'e
            +1
            А в чём проблема? Я вот пишу на PHP, но не считаю для себя зазорным читать эту и другие статьи про ASP.Net, в своё время даже копался в исходниках их первого MVC, много интересного подсмотрел.
              –3
              В PHP всё ещё нет ООП!!1 ©
            +1
            А если еще шаблона под бутстрап переделать, то «из коробки» будет
            –11
            Не проще WP поставить?
              0
              а что за WP? я не в курсе
                0
                Перечитал статью и понял что вы немножко про другое. WP я имел в виду WordPress. Но Вам типа кастом нужен, и вы про интерфейс чисто пишете. Так что мой комент вообще не в кассу, за что и извиняюсь.
              0
              Если уж вы пишете, что не пользуетесь CMS, то почему бы не пойти дальше и не отказаться от админки? Вот есть у нас список новостей… есть страница новости — ну и добавляем кнопки «Создать», «Редактировать», «Удалить». Прямо туда — если пользователь, имеющий доступ к этому авторизован.

              Полностью, конечно, отказаться не выйдет — можно добавить скрытые разделы для пользователей, наделенных соответствующими правами. Например, раздел управления пользователями.
                +1
                Почему вы так уверены, что админки — это атрибут исключительно CMS?

                Встраивать некоторые дополнительные элементы администрирования в интерфейс бывает удобным дополнением, но чтобы полностью смешать клиент и адмику — ну уж нет.
                  0
                  Резонный вопрос — почему?

                  К примеру, мне надо отредактировать новость, которая у меня перед глазами. Я жму и редактирую… и все. А так мне надо куда-то там лезть, искать эту новость и так далее.
                    +1
                    Функционал андминки не ограничивается созданием/редактированием новостей/статей. Самое банальное — управление пользователями — на фронте нет списка пользователей, куда добавлять инструменты для управления? А если там статистика, задачи по расписанию, управление самой структурой сайта и многие другие вещи которые не представлены явно на фронте но используются там? Ну и даже для новостей/статей, если операция нечастая то ваш подход допустим, а вот если их много, то может потребоватся дополнительный функционал для удобства работы с ними, фильтрации, поиски, различные сортировки.
                      –2
                      Внимательно почитайте мой комментарий
                  +1
                  Делали и такое. Так сказать «Режим конструктора». Но не совсем удобно для разработки, все на куче. Да и заказчик чаще всего просит отдельную админку.
                    +5
                    Странные заказчики. Лично мы уже давным давно отказались от админок. И я не могу сказать, что кто-то из заказчиков смутился — наоборот, это позиционируется как упрощение. Гораздо проще объяснить человеку, как управлять сайтом в этом случае… точнее — вообще не надо объяснять. Он авторизован и у него на видном месте элементы управления.

                    А статичные элементы страницы — различные тексты и прочее — мы вообще теперь исполняем так. Когда авторизован тот, кто уполномочен их менять, они снабжаются кнопкой, по клику на которую всплывает визуальный редактор… Это чудесное чувство, когда у тебя даже не спрашивают «а как сделать <что угодно>».
                    0
                    Если доработать SiteCake, то можно обойтись и без админки.
                      0
                      Довольно интересно… однако это просто такой вот очень замысловатый визуальный редактор. Но интересно, да. Хотя примерно то же самое можно сделать с помощью ckeditor. Примерно.
                    0
                    И всего-то 400 долларов! (-:
                      –1
                      700 баксов за KendoUI в этом году уже перевели?
                      Ах да есть и за 400 вариант. Но если .NET то лучше уж Kendo UI Complete for ASP.NET MVC за 999
                        0
                        Есть и бесплатная версия, которую я и использовал.
                          0
                          Trial?
                            +3
                            Нет, Open Source. По ссылке, которую я указывал в статье. Kendo UI Web.
                              0
                              Спасибо! Или ранее они ее еще лучше прятали. Или бесплатной версии совсем не было. Будем пользовать.
                                +1
                                Но если они под GPL, и Вы передаёте свои программы заказчику, то должны их тоже публиковать под GPL. Или всё крутится только на ваших серверах и заказчику не даёте ни бинарников, ни исходников?
                                  0
                                  А нет, это не поможет — Javascript передаётся в любом случае, так что как минимум весь ваш frontend под GPL.
                                    0
                                    А в чем проблема при передаче заказчику кода под GPL?

                                    GPL не обязывает заказчика или разработчика выкладывать исходники в паблик. Она лишь обязывает разработчика передать исходники пользователю. В данном случае пользователь один — заказчик, и исходники ему переданы. GPL соблюдена.
                                      0
                                      В данном случае пользователь Javascript-кода — любой посетитель страницы. Каждая страница должна включать копию GPL в комментариях, минимизировать их нельзя и т.д.
                                        0
                                        Как-то упустил этот момент. Но JS исходники и так передаются, правда не факт что в удобочитаемом виде. Так что формально вроде как GPL соблюдается.
                                          0
                                          Так GPL требует передать именно что в удобочитаемом, в том самом, в котором было написано :) «the preferred form of the work for making modifications to it». Ну и, как я упомянул, нужно включить сообщение, что страница лицензирована под GPL.
                            0
                            Хорошо бы еще:
                            — написать общий хендлер для всех таблиц в проекте
                            — вынести код инициализациии таблицы в Html-хелпер или куда-нибудь еще
                            — предусмотреть навигацию между страницами с разными гридами, и с разной фильтрацией. Ну, чтоб можно было переходить по ссылке из строки мастер-таблицы на страницу с таблицой details с нужным фильтром.
                            — продумать как это дело удобно кастомизировать, если потребуется

                            Ну, всякое такое. А так-то грид прицепить готовый — дело не хитрое.
                              0
                              Вообще это очень распространенная задача — сделать CRUD с красивыми формами и табличками, по готовой структуре данных, прилагая к этому минимум усилий. Но я не видел пока хороших решений.
                                0
                                ExtJS? Или вы хотите типа: $ crud generate --from mysql://localhost:3306/db/table --to /var/www/ --with-mega-design?
                                  0
                                  Одним ExtJS сыт не будешь — нужна еще серверная часть, подключение самого ExtJS, и т.п.

                                  Хочется максимально интегрированного в фреймворк механизма, чтобы можно было сначала сделать CRUD на существующую таблицу в одну строчку, а потом потихоньку настраивать, навешивать на это мясо из всяких юзабилити и прочих красот.

                                  Вариант сгенерировать, как в случае с MVC Scaffolding, мне очень не нравится — с ним от входа получаешь такой простор для копипасты и говнокода, что дух захватывает.
                                    0
                                    Согласен с Вами. но как конструктор — он достаточно неплох, если надо быстро нарисовать фронтенд к БД/управлялке чем либо…
                              0
                              GPL.
                              Не нужно.

                              Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                              Самое читаемое