📜 ⬆️ ⬇️

ASP.NET MVC 4 RAZOR Dynamic Multi-Level Database Menu

As promised in the previous post of DropDownList, Set the “value” for the default option in MVC 4 , today I’ll tell you about building a dynamic multi-level menu with infinite nesting stored in the MsSQL database. I remember in my time on PCP this was also a puzzle for a couple of days. But for the MVC 4 with the RAZOR engine - I barely figured it out, although in the end, as always, nothing complicated or supernatural. The article is designed for those who do not know how. If you know how to do better - share. Let's get started

This manual assumes that you are already operating with the knowledge gained from reading these articles: Entity Framework in an ASP.NET MVC application . Or these: ASP.NET MVC 4 Tutorials

1) First you need to understand the structure of the database. This is the main thing. The theory can be found in the article Hierarchical data structures in relational databases . We will use the most simple structure, called "structure referring to the ancestor."

SQL code looks like this:
CREATE TABLE "CATALOG" ( "ID" INTEGER NOT NULL PRIMARY KEY, "NAME" VARCHAR(200) CHARACTER SET WIN1251 NOT NULL, "PARENT_ID" INTEGER ); 

')
Create a model in VS 2012:
 using System; using System.Collections.Generic; using System.Linq; using System.Web; namespace zf2.Models { public class NewsM { public int NewsMID { get; set; } public int ParentID { get; set; } public string Title { get; set; } public string AddTitle { get; set; } public string Description { get; set; } public string Content { get; set; } public DateTime ModDate { get; set; } } } 

In general, only the first three fields are major. But for clarity, I gave a working version used on my site.

2) Controller.
  public ActionResult NewsA(int id = 1) //id     { ViewBag.Menu = db.NewsMs.ToList(); // ,     . ViewBag.Id = id; return View(); } 


3) Partial Views (Partial View Scripts)
If you have not encountered them yet, do not worry. They differ from ordinary scripts only in that they are not called automatically. These are views for views so to speak.

We go in the daddy of Views-Shared-Right-click-Add-View: put a tick "Create as a partial view." Enter the name "_Menu". Why is underscore used? Yes, just for convenience and exclude matches of names. Since partial scripts are searched in all directories of the Shared type and the corresponding controller with various extensions. This is what gives out if you specify an incorrect script name:
      "_gMenu"         .     : ~/Views/Home/_gMenu.aspx ~/Views/Home/_gMenu.ascx ~/Views/Shared/_gMenu.aspx ~/Views/Shared/_gMenu.ascx ~/Views/Home/_gMenu.cshtml ~/Views/Home/_gMenu.vbhtml ~/Views/Shared/_gMenu.cshtml ~/Views/Shared/_gMenu.vbhtml 

I think it is clear.
Go ahead.
In "_Menu.cshtml" copy the following code:
 @{ List<zf2.Models.NewsM> menuList = ViewBag.Menu; } <ul class="menu"> @foreach (var mp in menuList.Where(p => p.ParentID == 0)){ <li> @Html.ActionLink(mp.Title, ViewContext.RouteData.GetRequiredString("action"), new { id=mp.NewsMID }) @if( menuList.Count(p=>p.ParentID == mp.NewsMID ) > 0){ @:<ul> } @RenderMenuItem(menuList,mp) @if( menuList.Count(p=>p.ParentID == mp.NewsMID ) > 0){ @:</ul> } </li> } </ul> @helper RenderMenuItem(List<zf2.Models.NewsM> menuList, zf2.Models.NewsM mi) { foreach (var cp in menuList.Where(p => p.ParentID == mi.NewsMID)) { @:<li> @Html.ActionLink(cp.Title, ViewContext.RouteData.GetRequiredString("action"), new { id=cp.NewsMID }) if(menuList.Count(p=>p.ParentID == cp.NewsMID) > 0) { @:<ul> } @RenderMenuItem(menuList,cp) if(menuList.Count(p=>p.ParentID == cp.NewsMID) > 0) { @:</ul> } else { @:</li> } } } 

Here lies the magic.

@foreach (var mp in menuList.Where(p => p.ParentID == 0)) - parses and displays the names with ParentID = 0.

@RenderMenuItem(menuList,mp) - we call the view helper, which recursively completes all nesting for each “Rueta” item.

@helper RenderMenuItem(List<zf2.Models.NewsM> menuList, zf2.Models.NewsM mi) - this is the view helper itself, within which the recursion is organized.

@Html.ActionLink(mp.Title, ViewContext.RouteData.GetRequiredString("action"), new { id=mp.NewsMID }) - create links. I use standard routing. The name of the controller is automatically substituted. The name of the action and the parameter Id - specify "manually."
ViewContext.RouteData.GetRequiredString("action") - we get the name of the action. Similarly, you can get the name of the controller.
new { id=mp.NewsMID } - set the Id parameter.
mp.Title - Link Name

Next, create another partial view script called "_Content".
In it we will display the contents of the selected article by the transmitted Id.
The code is:
 @{ List<zf2.Models.NewsM> menuList = ViewBag.Menu; } @ViewBag.Id @foreach (var mp in menuList.Where(p => p.NewsMID == ViewBag.Id)) { @mp.Content @mp.AddTitle @mp.Description } 


4) The main view script. I have it called as the name of the action in the controller - NewsA.cshtml
In it, we simply call our partial view scripts and display the title.
 @{ ViewBag.Title = "NewsA"; } @{ List<zf2.Models.NewsM> menuList = ViewBag.Menu; } <div class="row"> <div class="span3"style="background-color: #e6e6e6;"> @Html.Partial("_Menu") </div> <div class="span6" style="background-color: #e6e6e6;"> @Html.Partial("_Content") </div> </div> 


, - Bootstrap - CSS . :

. . :
image

:
.

Entity Framework
.
, .
:
using System; using System.Collections.Generic; using System.Data.Entity; using System.Linq; using System.Web; using zf2.Models; namespace zf2.DAL { public class ZfInitializer : DropCreateDatabaseIfModelChanges<ZfContext> { protected override void Seed(ZfContext context) { var newsMs = new List<NewsM> { new NewsM { NewsMID = 1, ParentID = 0, Title = "Carson", AddTitle = "Carson", Description = "Carson", Content = "Carson" , ModDate = DateTime.Parse("2005-09-01") }, new NewsM { NewsMID = 2, ParentID = 0, Title = "Carson", AddTitle = "Carson", Description = "Carson", Content = "Carson" , ModDate = DateTime.Parse("2005-09-01") }, new NewsM { NewsMID = 3, ParentID = 1, Title = "Carson", AddTitle = "Carson", Description = "Carson", Content = "Carson" , ModDate = DateTime.Parse("2005-09-01") }, new NewsM { NewsMID = 4, ParentID = 1, Title = "Carson", AddTitle = "Carson", Description = "Carson", Content = "Carson" , ModDate = DateTime.Parse("2005-09-01") }, new NewsM { NewsMID = 5, ParentID = 2, Title = "Carson", AddTitle = "Carson", Description = "Carson", Content = "Carson" , ModDate = DateTime.Parse("2005-09-01") }, new NewsM { NewsMID = 6, ParentID = 3, Title = "Carson", AddTitle = "Carson", Description = "Carson", Content = "Carson" , ModDate = DateTime.Parse("2005-09-01") }, new NewsM { NewsMID = 7, ParentID = 2, Title = "Carson", AddTitle = "Carson", Description = "Carson", Content = "Carson" , ModDate = DateTime.Parse("2005-09-01") }, }; newsMs.ForEach(s => context.NewsMs.Add(s)); context.SaveChanges(); } } }


...
  , -   Bootstrap -   CSS .     : 

. . :
image

:
.

Entity Framework
.
, .
:
using System; using System.Collections.Generic; using System.Data.Entity; using System.Linq; using System.Web; using zf2.Models; namespace zf2.DAL { public class ZfInitializer : DropCreateDatabaseIfModelChanges<ZfContext> { protected override void Seed(ZfContext context) { var newsMs = new List<NewsM> { new NewsM { NewsMID = 1, ParentID = 0, Title = "Carson", AddTitle = "Carson", Description = "Carson", Content = "Carson" , ModDate = DateTime.Parse("2005-09-01") }, new NewsM { NewsMID = 2, ParentID = 0, Title = "Carson", AddTitle = "Carson", Description = "Carson", Content = "Carson" , ModDate = DateTime.Parse("2005-09-01") }, new NewsM { NewsMID = 3, ParentID = 1, Title = "Carson", AddTitle = "Carson", Description = "Carson", Content = "Carson" , ModDate = DateTime.Parse("2005-09-01") }, new NewsM { NewsMID = 4, ParentID = 1, Title = "Carson", AddTitle = "Carson", Description = "Carson", Content = "Carson" , ModDate = DateTime.Parse("2005-09-01") }, new NewsM { NewsMID = 5, ParentID = 2, Title = "Carson", AddTitle = "Carson", Description = "Carson", Content = "Carson" , ModDate = DateTime.Parse("2005-09-01") }, new NewsM { NewsMID = 6, ParentID = 3, Title = "Carson", AddTitle = "Carson", Description = "Carson", Content = "Carson" , ModDate = DateTime.Parse("2005-09-01") }, new NewsM { NewsMID = 7, ParentID = 2, Title = "Carson", AddTitle = "Carson", Description = "Carson", Content = "Carson" , ModDate = DateTime.Parse("2005-09-01") }, }; newsMs.ForEach(s => context.NewsMs.Add(s)); context.SaveChanges(); } } }


...
, - Bootstrap - CSS . :

. . :
image

:
.

Entity Framework
.
, .
:
using System; using System.Collections.Generic; using System.Data.Entity; using System.Linq; using System.Web; using zf2.Models; namespace zf2.DAL { public class ZfInitializer : DropCreateDatabaseIfModelChanges<ZfContext> { protected override void Seed(ZfContext context) { var newsMs = new List<NewsM> { new NewsM { NewsMID = 1, ParentID = 0, Title = "Carson", AddTitle = "Carson", Description = "Carson", Content = "Carson" , ModDate = DateTime.Parse("2005-09-01") }, new NewsM { NewsMID = 2, ParentID = 0, Title = "Carson", AddTitle = "Carson", Description = "Carson", Content = "Carson" , ModDate = DateTime.Parse("2005-09-01") }, new NewsM { NewsMID = 3, ParentID = 1, Title = "Carson", AddTitle = "Carson", Description = "Carson", Content = "Carson" , ModDate = DateTime.Parse("2005-09-01") }, new NewsM { NewsMID = 4, ParentID = 1, Title = "Carson", AddTitle = "Carson", Description = "Carson", Content = "Carson" , ModDate = DateTime.Parse("2005-09-01") }, new NewsM { NewsMID = 5, ParentID = 2, Title = "Carson", AddTitle = "Carson", Description = "Carson", Content = "Carson" , ModDate = DateTime.Parse("2005-09-01") }, new NewsM { NewsMID = 6, ParentID = 3, Title = "Carson", AddTitle = "Carson", Description = "Carson", Content = "Carson" , ModDate = DateTime.Parse("2005-09-01") }, new NewsM { NewsMID = 7, ParentID = 2, Title = "Carson", AddTitle = "Carson", Description = "Carson", Content = "Carson" , ModDate = DateTime.Parse("2005-09-01") }, }; newsMs.ForEach(s => context.NewsMs.Add(s)); context.SaveChanges(); } } }


...

Source: https://habr.com/ru/post/174531/


All Articles