Error when passing Model object to my view - asp.net-mvc

I have created the following model object:-
namespace MvcApplication1.Models
{
public class listofpack
{
public string packageName { get; set; }
public string packageId { get; set; }
}
}
Which i am populating its value on the controller as follow:-
public ActionResult Index()
{
ViewBag.Message = "Modify this template to jump-start your ASP.NET MVC application.";
using (var client = new WebClient())
{
var query = HttpUtility.ParseQueryString(string.Empty);
query["j_username"] = "kermit";
query["hash"] = "9449B5ABCFA9AFDA36B801351ED3DF66";
query["loginAs"] = "admin";
query["loginAs"] = User.Identity.Name;
var url = new UriBuilder("http://localhost:8080/jw/web/json/workflow/package/list");
url.Query = query.ToString();
string json = client.DownloadString(url.ToString());
var model = new JavaScriptSerializer().Deserialize<listofpack>(json);
return View(model);
// return Content(json, "application/json");
}
}
After that on the view i am trying to loop through the model as follow:-
#model IEnumerable<MvcApplication1.Models.listofpack>
#{
ViewBag.Title = "Home Page";
}
#foreach(var item in Model) {
#Html.ActionLink(item.packageId.ToString(), "about", "Home", new { id = "crm"},null)
}
but when i run the view i will get the following error:-
The model item passed into the dictionary is of type 'MvcApplication1.Models.listofpack', but this dictionary requires a model item of type 'System.Collections.Generic.IEnumerable`1[MvcApplication1.Models.listofpack]'.
BR
:::UPDATE::::
-----------------------------------------------------------------------------------------------------------------------------------------------------------------
i have updated the controller code to be:-
public ActionResult Index()
{
ViewBag.Message = "Modify this template to jump-start your ASP.NET MVC application.";
using (var client = new WebClient())
{
var query = HttpUtility.ParseQueryString(string.Empty);
query["j_username"] = "kermit";
query["hash"] = "9449B5ABCFA9AFDA36B801351ED3DF66";
query["loginAs"] = "admin";
query["loginAs"] = User.Identity.Name;
var url = new UriBuilder("http://localhost:8080/jw/web/json/workflow/package/list");
url.Query = query.ToString();
string json = client.DownloadString(url.ToString());
var model = new JavaScriptSerializer().Deserialize <List<listofpack>>(json);
return View(model);
}
}
and on the view:-
#model List<MvcApplication1.Models.listofpack>
#Model.Count();
#foreach(var item in Model) {
#Html.ActionLink(item.packageId.ToString(), "about", "Home", new { id = "crm"},null)
}
But the problem is that nothing will be displayed in the view and the .count() will return zero although there should be 5 jason objects passed to the view. So what might be going wrong??
Best Regards

var model = new JavaScriptSerializer().Deserialize<listofpack>(json);
is deserializing into a single listofpack object. Your view expects
#model IEnumerable<MvcApplication1.Models.listofpack>
maybe try something along this lines:
var model = new JavaScriptSerializer().Deserialize<IEnumerable<listofpack>>(json);

Read this question :
No parameterless constructor defined for type of 'System.String' during JSON deserialization
No parameterless constructor defined for type of 'System.Collections.Generic.IEnumerable`1[[MvcApplication1.Models.listofpack, MvcApplication1, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]'.
This exception means that the generic type "IEnumerable< listofpack >" u used doesn't have a constructor because it's an interface,so you should use "Dictionary" or "List"
Edit:
Here's an example:
I created a class and name it Employee:
public class Employee
{
public string lastName { get; set; }
public string firstName { get; set; }
}
then i wrote the following statements in the controller :
string jsonEmployees="{'employees': [{ 'firstName':'John' , 'lastName':'Doe' }, { 'firstName':'Anna' , 'lastName':'Smith' }, { 'firstName':'Peter' , 'lastName':'Jones' }]}";
var model = new JavaScriptSerializer().Deserialize<Dictionary<string,List<Employee>>>(jsonEmployees);
return View(model);
so try using "Dictionary< string,List< listOfpack >>".

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);
}

System.NullReferenceException when trying to iterate list in view

Im a bit new at mvc, and i dont find out what am i miss. When i launch the login, in the view at foreach (var item in Model) <- the Model gets null, and stops with a System.NullReferenceException. A dont really have a clue why, and i hope somebody can give some advice what's wrong with the following code or where to start looking for the error.
The model:
public class LoginModels
{
public string UserLogin { get; set; }
public string Address { get; set; }
public string Password { get; set; }
public List<string> emailSubject { get; set; }
}
The controller:
public ActionResult Login(string address, string password, LoginModels model)
{
using (Imap imap = new Imap())
{
try
{
imap.ConnectSSL("imap.gmail.com");
imap.Login(address, password);
imap.SelectInbox();
List<long> uids = imap.Search(Flag.All);
model.emailSubject = new List<string>();
foreach (long uid in uids)
{
var eml = imap.GetMessageByUID(uid);
IMail email = new MailBuilder().CreateFromEml(eml);
model.emailSubject.Add(email.Subject);
}
Session["user"] = new LoginModels() { UserLogin = address, Address = address };
return RedirectToAction("Index", "Home", model.emailSubject);
}
catch (Exception e)
{
ViewBag.exceptionMessage = e;
return View("LoginFailed");
}
}
The view:
#using TheOnlineArchivator.Models;
#model List<TheOnlineArchivator.Models.LoginModels>
#{
ViewBag.Title = "Home";
}
#{
var user = Session["user"] as LoginModels;
if (user != null)
{
<h2>You are logged on as #user.Address</h2>
<table>
#foreach (var item in Model)
{
foreach (var elem in item.emailSubject)
{
<tr>
<td>#elem</td>
</tr>
}
}
</table>
}
}
It looks like you forgot to pass an instance of List<TheOnlineArchivator.Models.LoginModels> to the view when you rendered this view inside your controller action. What you have shown so far is your Login controller action but you didn't show us your Home/Index action. Inside this action you should make sure that you are passing a non-null model to the view:
public class HomeController : Controller
{
public ActionResult Index()
{
List<LoginModels> model = ... go get your model from somewhere and make sure it is not null
return View(model);
}
}

How to render partial view using url

I'm using ASP.NET MVC 4 and have a prepared url in a view like this:
var sectoionUrl = Url.Action("DisplayArticleSection", new { id = sectionId });
Is there any helper to render a partial view using prepared sectionUrl instead of creating it again via helper:
#Html.Action("DisplayArticleSection", new { id = sectionId })
?
Something like this pseudocode:
#Html.RenderUrl(sectionUrl)
Custom HtmlHelper:
public static class RenderUrlHelper
{
public static void RenderUrl(this HtmlHelper helper, string originUrl)
{
var originRequest = helper.ViewContext.RequestContext.HttpContext.Request;
if (!Uri.IsWellFormedUriString(originUrl, UriKind.Absolute))
{
originUrl = new Uri(originRequest.Url, originUrl).AbsoluteUri;
}
int queryIdx = originUrl.IndexOf('?');
string queryString = null;
string url = originUrl;
if (queryIdx != -1)
{
url = originUrl.Substring(0, queryIdx);
queryString = originUrl.Substring(queryIdx + 1);
}
// Extract the data and render the action.
var request = new HttpRequest(null, url, queryString);
var response = new HttpResponse(new StringWriter());
var httpContext = new HttpContext(request, response);
var routeData = RouteTable.Routes.GetRouteData(new HttpContextWrapper(httpContext));
var values = routeData.Values;
string controllerName = values["controller"] as string;
string actionName = values["action"] as string;
var valuesCollection = HttpUtility.ParseQueryString(queryString);
RouteValueDictionary routeValues = new RouteValueDictionary(valuesCollection.AllKeys.ToDictionary(k => k, k => (object)valuesCollection[k]));
helper.RenderAction(actionName, controllerName, routeValues);
}
}
Some code to test custom HtmlHelper:
TestController:
public class TestController : Controller
{
public ActionResult Index(string title, int number)
{
TestModel model = new TestModel { Title = title, Number = number };
return PartialView(model);
}
}
TestController Index view:
#model TestModel
<h1>Title: #Model.Title</h1>
<h2>Number: #Model.Number</h2>
TestModel:
public class TestModel
{
public string Title { get; set; }
public int Number { get; set; }
}
Usage in any view:
#{
var actionUrl = Url.Action("Index", "Test", new { number = 123, title = "Hello Url Renderer" });
Html.RenderUrl(actionUrl);
}
You can use Following HTML Code To Render partail View Using
#Html.RenderPartial("NameOfPartialView")
#Html.Partial("NameOfPartialView")
OR
You can create a custom HTML Helper to Create Functionality like,
#Html.RenderUrl(sectionUrl)
Following url shows step by step how to create Custom HTML Helper.
Create Custom HTML Helper

How to hide id in the url (MVC3)

Problem
In my project i decided to imlement a custom menu provider using a db stored entity "Section".
So the section is mapped to the following Model:
public class TopMenuItemModel : BaseTrivitalModel
{
public TopMenuItemModel()
{
ChildItems = new List<TopMenuItemModel>();
}
public int ItemId { get; set; }
public string RouteUrl { get; set; }
public string Title { get; set; }
public string SeName { get; set; }
public IList<TopMenuItemModel> ChildItems { get; set; }
}
And the view for the model:
#model TopMenuModel
<nav id="main-nav">
#T("HomePage")
#foreach (var parentItem in Model.MenuItems)
{
#parentItem.Title
}
</nav>
My Default route is:
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional },
new[] { "Trivital.Web.Controllers" }
);
Controller for the menu:
public class CommonController : BaseTrivitalController
{
...
public ActionResult TopMenu()
{
var sections = _sectionService.GetCollectionByParentId(0, true);
var model = new TopMenuModel();
model.MenuItems = sections.Select(x =>
{
var item = new TopMenuItemModel()
{
ItemId = x.Id,
Title = x.GetLocalized(s => s.Title, _workContext.WorkingLanguage.Id, true, true),
SeName = x.GetSeName(),
RouteUrl = "",
};
return item;
})
.ToList();
return PartialView(model);
}
}
}
Now I have a SectionController where I have an ActionResult method:
//section main page
public ActionResult Section(string seName)
{
var section = _sectionService.Get(1);
if (section == null || section.Deleted || !section.Published)
return RedirectToAction("Index", "Home");
//prepare the model
var model = PrepareSectionPageModel(section);
return View(model);
}
My current Route for the Section (that gives me host/sectionSeName-id):
routes.MapLocalizedRoute(
"section", // Route name
"{SeName}"+ "-" + "{sectionId}", // URL with parameters
new { controller = "Sections", action = "Section" },
new { sectionId = #"\d+" }
);
Now I need to get my Url looks like this (without id, just the section name):
host/sectionSeName
Is there anyway to hide the Id in the url to make the urls look SEO-friendly, but available for the controller?
You can try utilizing the urlMappings in your web.config. Specify something like the following:
<urlMappings enabled="true">
<add url="~/somedirectory/" mappedUrl="~/somedirectory/1/"/>
</urlMappings>
Though, I don't think anything will work unless each section has it's own unique name. Otherwise you'll have some conflicting URLs.
You may also want to consider doing some custom work as well using IIS's rewrite module:
http://www.iis.net/learn/extensions/url-rewrite-module/using-the-url-rewrite-module
The company I work for uses this for it's KB article system, which is similar to your situation, and it works pretty well. (folder/id)

read implicit return type in Razor MVC View

I'm kind of new to razor MVC, and I'm wondering how can I read the values I return in the view?
My code is like this:
public ActionResult Subject(int Category)
{
var db = new KnowledgeDBEntities();
var category = db.categories.Single(c => c.category_id == Category).name;
var items = from i in db.category_items
where i.category_id == Category
select new { ID = i.category_id, Name = i.name };
var entries = from e in db.item_entry
where items.Any(item => item.ID == e.category_item_id)
select new { ID = e.category_item_id, e.title };
db.Dispose();
var model = new { Name = category, Items = items, Entries = entries };
return View(model);
}
Basically, I return an anonymous type, what code do I have to write to read the values of the anonymous type in my view?
And if this is not possible, what would be the appropriate alternative?
Basically, I return an anonymous type
Nope. Ain't gonna work. Anonymous types are emitted as internal by the compiler and since ASP.NET compiles your views into separate assemblies at runtime they cannot access those anonymous types which live in the assembly that has defined them.
In a properly designed ASP.NET MVC application you work with view models. So you start by defining some:
public class MyViewModel
{
public string CategoryName { get; set; }
public IEnumerable<ItemViewModel> Items { get; set; }
public IEnumerable<EntryViewModel> Entries { get; set; }
}
public class ItemViewModel
{
public int ID { get; set; }
public string Name { get; set; }
}
public class EntryViewModel
{
public int ID { get; set; }
public string Title { get; set; }
}
and then you adapt your controller action to pass this view model to the view:
public ActionResult Subject(int Category)
{
using (var db = new KnowledgeDBEntities())
{
var category = db.categories.Single(c => c.category_id == Category).name;
var items =
from i in db.category_items
where i.category_id == Category
select new ItemViewModel
{
ID = i.category_id,
Name = i.name
};
var entries =
from e in db.item_entry
where items.Any(item => item.ID == e.category_item_id)
select new EntryViewModel
{
ID = e.category_item_id,
Title = e.title
};
var model = new MyViewModel
{
CategoryName = category,
Items = items.ToList(), // be eager
Entries = entries.ToList() // be eager
};
return View(model);
}
}
and finally you strongly type your view to the view model you have defined:
#model MyViewModel
#Model.Name
<h2>Items:</h2>
#foreach (var item in Model.Items)
{
<div>#item.Name</div>
}
<h2>Entries:</h2>
#foreach (var entry in Model.Entries)
{
<div>#entry.Title</div>
}
By the way to ease the mapping between your domain models and view models I would recommend you checking out AutoMapper.
Oh, and since writing foreach loops in a view is kinda ugly and not reusable I would recommend you using display/editor templates which would basically make you view look like this:
#model MyViewModel
#Model.Name
<h2>Items:</h2>
#Html.DisplayFor(x => x.Items)
<h2>Entries:</h2>
#Html.DisplayFor(x => x.Entries)
and then you would define the respective display templates which will be automatically rendered for each element of the respective collections:
~/Views/Shared/DisplayTemplates/ItemViewModel:
#model ItemViewModel
<div>#item.Name</div>
and ~/Views/Shared/DisplayTemplates/EntryViewModel:
#model EntryViewModel
<div>#item.Title</div>

Resources