MVC 5 Multiple Models - asp.net-mvc

Is it possible to have multiple #Model functions within one page?
I have a Views/Shared/_layout.cshtml page which has the following code to pull the navigation from an SQL Server 2012 database:
#model IEnumerable<WebApplication1.navigation_V>
<ul class="nav sf-menu clearfix">
#foreach (var item in Model) {
<li>#Html.MenuItem(item.title_TV, item.url_TV, "Home")</li>
}
</ul>
This works fine on all views within the Views/Home folder, however the View/Accounts/Login.cshtml file has the following code:
#model WebApplication1.Models.LoginViewModel
Now I get the following error when trying to view the Account/Login page:
The model item passed into the dictionary is of type 'System.Collections.Generic.List`1[WebApplication1.navigation_V]', but this dictionary requires a model item of type 'WebApplication1.Models.LoginViewModel'.
When writing the code I am not getting any red underline squiggles, the error only fires when trying to access the Account/Login page. This navigation function must be viewable on all pages, what I'm dreading next is actually getting the rest of the page content from the database in to these pages.
Any help would be much appreciated :-)
For further information I have included more code.
WebApplication1Entities.cs
using System;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
namespace WebApplication1
{
public partial class WebApplication1Entities : DbContext
{
public WebApplication1Entities()
: base("name=WebApplication1Entities")
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
throw new UnintentionalCodeFirstException();
}
public virtual DbSet<navigation_V> navigation_V { get; set; }
}
}
navigation_V.cs
using System;
using System.Collections.Generic;
namespace WebApplication1
{
public partial class navigation_V
{
public int navigation_ID { get; set; }
public string title_TV { get; set; }
public string url_TV { get; set; }
}
}
Controllers/HomeController.cs:
public static class MenuExtensions
{
public static MvcHtmlString MenuItem(
this HtmlHelper htmlHelper,
string text,
string action,
string controller
)
{
var li = new TagBuilder("li");
var routeData = htmlHelper.ViewContext.RouteData;
var currentAction = routeData.GetRequiredString("action");
var currentController = routeData.GetRequiredString("controller");
if (string.Equals(currentAction, action, StringComparison.OrdinalIgnoreCase) &&
string.Equals(currentController, controller, StringComparison.OrdinalIgnoreCase))
{
li.AddCssClass("active");
}
li.InnerHtml = htmlHelper.ActionLink(text, action, controller).ToHtmlString();
return MvcHtmlString.Create(li.ToString());
}
}
namespace WebApplication1.Controllers
{
public class HomeController : Controller
{
private WebApplication1Entities db = new WebApplication1Entities();
public ActionResult Index()
{
return View(db.navigation_V.ToList());
}
}
}
Views/Shared/Layout.cshtml
#model IEnumerable<WebApplication1.navigation_V>
<ul class="nav sf-menu clearfix">
#foreach (var item in Model) {
<li>#Html.MenuItem(item.title_TV, item.url_TV, "Home")</li>
}
</ul>
I hope this make things a little clearer.

Instead of rendering navigation IEnumerable in layout you can write child action in your Home controller like this:
[ChildActionOnly]
public ActionResult Navigation()
{
var navigationModel = ..
// your code to retrieve IEnumerable<WebApplication1.navigation_V>
// from DB
return View(navigationModel);
}
View for this action is Views/Home/Navigation.cshtml:
#{ Layout = null; } // preventing recursive rendering
#model IEnumerable<WebApplication1.navigation_V>
<ul class="nav sf-menu clearfix">
#foreach (var item in Model) {
<li>#Html.MenuItem(item.title_TV, item.url_TV, "Home")</li>
}
</ul>
And code for _layout.cshtml, replace your old navigation code with this one:
#{ Html.RenderAction("Navigation", "Home"); }

Related

MVC Error using Lists and Model

I am very new to this and am doing a little project to get to know how it all works.
So I'm looking to create a header image area on each page by placing the code in the "_Layout.cshtml" file and attempting to control what image displays according to "ViewBag.Title".
I have broken the code up into the pages, followed by a pic. of the error. I just can't work out what the problem is.
HomeController.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
namespace WebSite_Project_1.Controllers
{
public partial class HomeController : Controller
{
public ActionResult Index()
{
return View();
}
public ActionResult About()
{
ViewBag.Message = "Your application description page.";
return View();
}
public ActionResult Contact()
{
ViewBag.Message = "Your contact page.";
return View();
}
[ActionName("Funny-Bones")]
public ActionResult FunnyBones()
{
ViewBag.Message = "This is funny bones.";
return View();
}
public class Headers
{
public string HeaderName { get; set; }
public string PageName { get; set; }
public int HeaderWidth { get; set; }
public int HeaderHeight { get; set; }
public string HeaderType { get; set; }
}
public ActionResult HeaderImages()
{
var model = new List<Headers>();
model.Add(new Headers { HeaderName = "home", PageName = "Home Page", HeaderWidth = 2200, HeaderHeight = 1172, HeaderType = ".png" });
model.Add(new Headers { HeaderName = "about", PageName = "About", HeaderWidth = 2200, HeaderHeight = 1172, HeaderType = ".png" });
model.Add(new Headers { HeaderName = "contact", PageName = "Contact", HeaderWidth = 2200, HeaderHeight = 1172, HeaderType = ".png" });
model.Add(new Headers { HeaderName = "funnybones", PageName = "Funny Bones", HeaderWidth = 2200, HeaderHeight = 1172, HeaderType = ".png" });
return View(model);
}
}
}
_Layout.cshtml
#model IEnumerable<WebSite_Project_1.Controllers.HomeController.Headers>
<div class="headersImage">
#foreach (var item in Model)
{
if (#item.PageName == #ViewBag.Title)
{
<img src="~/Content/Images/#item.HeaderName+#item.HeaderType" title="#item.HeaderName" />
}
}
</div>
#RenderBody()
The problem starts when I try and publish it and then i get this error pointing to Model in the "foreach" loop.
I'm not a 100% sure the loop is right, but haven't been able to get that far yet.
Link to MVC error
You should never specify a model for a layout. The model passed in will always be the one for the view, which almost invariably, will not be the same one the layout is wanting.
For things like this, you should use child actions. Essentially, just take your existing HeaderImages action, add the [ChildActionOnly] attribute (which prevents it from being routed to directly), and change the return to:
return PartialView("_HeaderImages", model);
You can call the view whatever you want, but essentially it would just have the following:
#model IEnumerable<WebSite_Project_1.Controllers.HomeController.Headers>
<div class="headersImage">
#foreach (var item in Model)
{
if (#item.PageName == #ViewBag.Title)
{
<img src="~/Content/Images/#item.HeaderName+#item.HeaderType" title="#item.HeaderName" />
}
}
</div>
Finally, in your layout, remove the model definition line and replace the header image code currently there with:
#Html.Action("HeaderImages", "Home")
EDIT
Sorry, I missed one thing. The child action will render in a separate context from the main view/layout (that's sort of the point). However, that means it has its own ViewBag, so you can't access the ViewBag from the main action directly. Instead, you'll need to pass it in as a route value:
#Html.Action("HeaderImages", "Home", new { title = ViewBag.Title })
Then, modify your child action to accept this param, and set its ViewBag:
[ChildActionOnly]
public ActionResult HeaderImages(string title)
{
...
ViewBag.Title = title;
return PartialView("_HeaderImages", model);
}

Dynamic menus from database in MVC

I have read some similar topics here and on the web, but I don't think I have seen one that would classify this as a duplicate, so I am going to go ahead and post it. I am currently loading my dynamic menus from the database like so:
public void LoadMenus()
{
var dbContext = new ContentClassesDataContext();
var menus = from m in dbContext.Menus
where m.MenuName != "Home" && m.MenuGroup == "RazorHome" && m.RoleID == "Facility"
orderby m.Sequence, m.MenuName
select m;
var html = "";
if (menus.Any())
{
html += "<span/>";
foreach (var menu in menus)
{
html = html + $"<a href='{menu.URL}'>{menu.MenuName}</a><br/>";
}
html += "<hr>";
}
Session["Menus"] = html;
}
LoadMenus() is in my controller class, so I am not able (to my knowledge) to use Razor syntax. I would prefer to load the menus from the view instead, so that I am able to use #Html.ActionLink(linkText, actionName, controllerName). Loading the HTML the way I am currently doing it will generate different link text depending on the current controller, so the links are not always correctly rendered. Is it possible to access the database from the view? Or perhaps to just pass in the content from the database from the controller to the view and then render the menu that way?
You should keep your html in the cshtml views.
You should pass the data through the viewmodel and not through the session.
1)
In the controller, get the menu data (in this example we fetch some fake data).
Create a viewmodel that can hold the menu data and pass it to the view, as shown below:
public class HomeController : Controller
{
public ActionResult Index()
{
var menu = GetMenu();
var vm = new ViewModel() {Menu = menu};
return View(vm);
}
private Menu GetMenu()
{
var menu = new Menu();
var menuItems = new List<MenuItem>();
menuItems.Add(new MenuItem() { LinkText = "Home" , ActionName = "Index", ControllerName = "Home"});
menuItems.Add(new MenuItem() { LinkText = "About", ActionName = "About", ControllerName = "Home" });
menuItems.Add(new MenuItem() { LinkText = "Help", ActionName = "Help", ControllerName = "Home" });
menu.Items = menuItems;
return menu;
}
}
2)
This is the viewmodel
public class ViewModel
{
public Menu Menu { get; set; }
}
This view is an example of how you could render the menu data as a html menu
#model WebApplication1.Models.ViewModel
<ul id="menu">
#foreach (var item in #Model.Menu.Items)
{
<li>#Html.ActionLink(#item.LinkText, #item.ActionName,
#item.ControllerName)</li>
}
</ul>
3)
This is the example menu classes used (representing your entities from the dbcontext)
public class Menu
{
public List<MenuItem> Items { get; set; }
}
public class MenuItem
{
public string LinkText { get; set; }
public string ActionName { get; set; }
public string ControllerName { get; set; }
}
Here are some links to get you started:
http://www.codeproject.com/Articles/585873/Basic-Understanding-On-ASP-NET-MVC
http://www.asp.net/mvc/overview/getting-started/introduction/getting-started

Receive empty data set in ASP.Net MVC

I am so puzzled with this:
I have two identical set of code for retrieving data from database. One returns data and the other does not, even though data is expected.
The following is the one that returns empty set. There are sample data in the corresponding table.
This is the controller code:
namespace MVCPart1_5.Controllers
{
public class DepartmentController : Controller
{
// GET: Department
public ActionResult Index()
{
DepartmentContext oDepartmentContext = new DepartmentContext();
List<Department> oDepartments = oDepartmentContext.Departments.ToList();
return View(oDepartments);
}
}
}
These are the models:
namespace MVCPart1_5.Models
{
[Table("Department")]
public class Department
{
[Key]
public int Code { get; set; }
public string Name { get; set; }
}
}
namespace MVCPart1_5.Models
{
public class DepartmentContext : DbContext
{
public DbSet<Department> Departments { set; get; }
}
}
and finally the view:
#model IEnumerable<MVCPart1_5.Models.Department>
#using MVCPart1_5.Models;
#{
ViewBag.Title = "Index";
}
<h2>Department Set</h2>
<div style="font-family :Arial">
<ul>
#foreach (Department department in #Model)
{
<li>
#Html.ActionLink(department.Name, "Details", "Employee", new { departmentCode = department.Code })
</li>
}
</ul>
</div>
I also have the following in the global:
protected void Application_Start()
{
Database.SetInitializer<MVCPart1_5.Models.EmployeeContext>(null);
Database.SetInitializer<MVCPart1_5.Models.DepartmentContext>(null);
AreaRegistration.RegisterAllAreas();
RouteConfig.RegisterRoutes(RouteTable.Routes);
}
}
Thanks in advance for your help.
If your oDepartments variable is not filled with the data you should examine a connection string in your application because it's probably wrong (so it connects to another database).
Because your DbContext class is DepartmentContext and the default Entity Framework approach is to name connection string the same you should look for a connection string named DepartmentContext. Then try to connect to a database using credentials found in that connection string. I'm 99% sure you'll find empty Department table.
Remember that even if you connect with the same database server you could connect to wrong database.

mvc-4 generate list in view

I have a little problem with getting my View to generate properly.
I get Error: Model does not contain a public definition for 'GetEnumerator'
I have tried to change my generic list into a IEnumerable list but then it popped a few more errors in the code I couldnt get rid of, Im not sure if I have to add it to my UploadedFile class somehow ?
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace Mvc_fileUploader.Models
{
public class UploadedFile
{
public string Name { get; set; }
public string Path { get; set; }
public long Size { get; set; }
}
}
Controller:
[HttpGet]
public ActionResult UploadedFiles()
{
var uploadedFiles = new List<UploadedFile>();
var files = Directory.GetFiles(Server.MapPath("~/fileUploads/"));
foreach (var file in files)
{
var fileInfo = new FileInfo(file);
var uploadedFile = new UploadedFile();
uploadedFile.Name = Path.GetFileName(file);
uploadedFile.Size = fileInfo.Length;
uploadedFile.Path = ("~/fileUploads/") + Path.GetFileName(file);
uploadedFiles.Add(uploadedFile);
}
return View();
View:
#model Mvc_fileUploader.Models.UploadedFile
#{
ViewBag.Title = "UploadedFiles";
}
<h2>UploadedFiles</h2>
<table style="background-color:lightpink; border:solid 2px black;">
<tr>
<td>Name</td>
<td>Size</td>
<td>Preview</td>
</tr>
#foreach (var file in Model)
{
<tr>
<td>#file.Name</td>
</tr>
}
</table>
the source on github: https://github.com/xoxotw/mvc_fileUploader
Need to set the model of the view to be
IEnumerable<Mvc_fileUploader.Models.UploadedFile>
[EDIT]
You're not returning the model. In your controller action; add the list you've created to the View() call, eg:
[HttpGet]
public ActionResult UploadedFiles()
{
var uploadedFiles = new List<UploadedFile>();
var files = Directory.GetFiles(Server.MapPath("~/fileUploads/"));
// do stuff
return View(uploadedFiles);
#model Mvc_fileUploader.Models.UploadedFile
It is used when only one model is passed to the view. If u want to pass list of model, then u have to write like following.
IEnumerable<Mvc_fileUploader.Models.UploadedFile>

asp.net mvc - Trying to have a partial view in my layout and getting:

I'm trying to use a partial view through a controller in my layout like so:
_Layout.cshtml:
<ul id="menu">
<li>#Html.Action("Menu")</li>
<li>#Html.ActionLink("Home", "Index", "Home")</li>
<li>#Html.ActionLink("About", "About", "Home")</li>
<li>#Html.ActionLink("Contact", "Contact", "Home")</li>
</ul>
MenuController.cs:
public class MenuController : Controller
{
//
// GET: /Menu/
public ActionResult Index()
{
var menu = new Models.Menu()
{
Items = new List<Models.MenuItem>()
{
new Models.BaseMenuBtn() { Label = "One", Link= "Home", CssClass="a"},
new Models.BaseMenuBtn() { Label = "One", Link= "Home", CssClass="b"}
}
};
return View();
}
}
Models:
Menu.cs:
public class Menu : MenuItem
{
public List<MenuItem> Items { get; set; }
public override string Render()
{
StringBuilder sb = new StringBuilder("<div>");
foreach (var item in Items)
{
sb.Append(item.Render());
}
sb.Append("</div>");
return sb.ToString();
}
public Menu()
{
Items = new List<Models.MenuItem>()
{
new Models.BaseMenuBtn() { Label = "One", Link= "Home", CssClass="a"},
new Models.BaseMenuBtn() { Label = "One", Link= "Home", CssClass="b"}
};
}
}
MenuItem.cs:
public abstract class MenuItem
{
public string CssClass { get; set; }
public abstract string Render();
}
BaseMenuBtn.cs
public class BaseMenuBtn : MenuItem
{
public string Label { get; set; }
public string Link { get; set; }
public override string Render()
{
return string.Format("<div href='{0}' class='{1}'> {2} </div>", this.Link , this.CssClass, this.Label);
}
}
And, from the line I added in the layout, I'm getting this error:
Error executing child request for handler
'System.Web.Mvc.HttpHandlerUtil+ServerExecuteHttpHandlerAsyncWrapper'.
Please help.
If you're trying to generate HTML from a model, you really should be creating an extension to the HtmlHelper class. For instance, I created this for a site nav extension:
public static HtmlString MenuLink(this HtmlHelper helper, string text, string actionName, string controllerName, object routeValues)
{
string currentAction = helper.ViewContext.RouteData.GetRequiredString("action");
string currentController = helper.ViewContext.RouteData.GetRequiredString("controller");
var li = new TagBuilder("li");
if (actionName.ToLower() == currentAction.ToLower() && controllerName.ToLower() == currentController.ToLower())
{
li.AddCssClass("selected");
}
li.InnerHtml = helper.ActionLink(linkText: text, actionName: actionName, controllerName: controllerName, routeValues: routeValues, htmlAttributes: null).ToHtmlString();
return MvcHtmlString.Create(li.ToString());
}
Once I register the namespace of my extensions in the web.config files, I can call this helper like so:
#Html.MenuLink("Contact", "contact", "home", new { })
which generates this for me:
<li class="selected">
Contact
</li>
You should be able to follow this pattern and adjust the code above to fit your needs; if not, ask and I'll see if I can come up with a simple/naive example.

Resources