Древовидный список на ASP.NET MVC 4

от автора

Добрый день! На хабре есть статья, в которой рассказывается как сделать древовидный список. Однако, в этой версии будет использован движок Razor, Entity Framework и др., а так же реализованы операции со списком. Данный вариант отличается простотой и быстротой реализации. Статья рассчитана на тех, кто уже знаком с ASP .NET MVC.

Возможности:

  • Отображение списка
  • Добавление элементов
  • Перемещение элементов
  • Удаление элементов

Используемые технологии:

  • Microsoft ASP. NET MVC 4
  • Entity Framework
  • Linq to Entity
  • Microsoft SQL Server (Local DB)

База данных

Так как все записи будут храниться в базе данных, то необходимо создать следующую таблицу:

public class News {    public int Id {get;set;} //Идентификатор новости    public int? ParentId {get;set;} //Идентификатор родительской новости    public string Title {get;set;} //Заголовок новости    public bool IsDeleted {get;set;} //Флаг удаления } 

Модели

Далее создадим модели для работы:
1. Обычно в моделях не используют напрямую записи из базы данных, поэтому создадим модель похожую на таблицу.

public class NewsModel {    public int Id {get;set;} //Идентификатор новости    public int? ParentId {get;set;} //Идентификатор родительской новости    public string Title {get;set;} //Заголовок новости } 

2. Список новостей.

public NewsListModel {    public int? Seed {get;set;} //Корневой элемент    public IEnumerable<NewsModel> News {get;set;} //Список новостей } 

Контроллер

Ниже представлен контроллер, который умеет выбирать, удалять, добавлять и перемещать новости.

public class NewsController : Controller {     public ActionResult Index()     {         using (NewsContext context = new NewsContext())         {             NewsListModel model = new NewsListModel()             {                 News = context.News.Where(x => !x.IsDeleted).ToArray().Select(x => new NewsModel(x))             };             return View(model);         }     }      [HttpPost]     [ValidateAntiForgeryToken]     public ActionResult Add(int? parentId, string title)     {         using (NewsContext context = new NewsContext())         {             var newNews = new News()             {                 ParentId = parentId,                 Title = title             };             context.News.Add(newNews);             context.SaveChanges();         }         return RedirectToAction("Index");     }      [ValidateAntiForgeryToken]     [HttpPost]     public ActionResult Move(int nodeId, int? newParentId)     {         if (nodeId == newParentId)         {             return RedirectToAction("Index");         }         using (NewsContext context = new NewsContext())         {             if (newParentId.HasValue && ContainsChilds(context, nodeId, newParentId.Value))             {                 return RedirectToAction("Index");             }             var node = context.News.Where(x => x.Id == nodeId).Single();             node.ParentId = newParentId;             context.SaveChanges();         }         return RedirectToAction("Index");     }      private bool ContainsChilds(NewsContext context, int parentId, int id)     {         bool result = false;         var inner = context.News.Where(x => x.ParentId == parentId && !x.IsDeleted).ToArray();         foreach (var node in inner)         {             if (node.Id == id && node.ParentId == parentId)             {                 return true;             }             result = ContainsChilds(context, node.Id, id);         }         return result;     }      [HttpPost]     public ActionResult Delete(int id)     {         using (NewsContext context = new NewsContext())         {             DeleteNodes(context, id);             context.SaveChanges();         }          return RedirectToAction("Index");     }      private void DeleteNodes(NewsContext context, int id)     {         var inner = context.News.Where(x => x.ParentId == id && !x.IsDeleted).ToArray();         foreach (var node in inner)         {             node.IsDeleted = true;             DeleteNodes(context, node.Id);         }         var deleted = context.News.Where(x => x.Id == id && !x.IsDeleted).Single();         deleted.IsDeleted = true;     } } 

Представление

Как видно из кода, большинство методов выполняются рекурсивно. Рекурсия позволяет достаточно лаконично реализовать операции. Так же рекурсия используется при отображении на странице. Рассмотрим вывод древовидного списка.

_TreeList.cshtml

@model MySLOTree.Models.NewsListModel  @if (Model.News.Where(x => x.ParentId == Model.Seed).Any()) {     <ul>         @foreach (var node in Model.News)         {             if (node.ParentId == Model.Seed)             {                 <a>@node.Title</a>                 MySLOTree.Models.NewsListModel inner = new MySLOTree.Models.NewsListModel                  {                     Seed = node.Id,                     News = Model.News                  };                 @Html.Partial("_TreeList", inner)             }          }        </ul> } 

Результат

Пустой список

Добавление элементов

Отображение списка

Сворачивание\Разворачивание списка

Перемещение элементов

Заключение

Для перемещения элементов в дереве используется drag and drop. Добавление и удаление элементов происходит путем нажатия соответствующих иконок. В статье опущены некоторые моменты. Все подробности можно увидеть в исходном коде.
Полный исходный код доступен на github.

P.S. С радостью отвечу на вопросы и обменяюсь опытом.

ссылка на оригинал статьи http://habrahabr.ru/post/206894/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *