Using 'Html.EditorFor' to pass data to controller using ViewBag - asp.net

I have an issue using ViewBag with Html.EditorFor. I am trying to pass data from a form field named "ID" from view 'Create.chtml' back to controller. I would like to use ViewBag for this. When I tried to do as I did in the View below, it's throwing an error:
Compiler Error Message: CS1973: 'System.Web.Mvc.HtmlHelper' has no applicable method named 'EditorFor' but appears to have an extension method by that name. Extension methods cannot be dynamically dispatched. Consider casting the dynamic arguments or calling the extension method without the extension method syntax.
Please let me know where I am doing it wrong.
Controller:
[HttpPost]
public ActionResult Create(TABLE_CODES dc)
{
try
{
using (var db = new InpEntities())
{
TABLE_CODES codes = new TABLE_CODES(); //TABLE_CODES has data with various columns - ID, NAME, DATE, SOURCE etc.
ViewBag.keys = codes;
db.AddToTABLE_CODES(dc);
db.SaveChanges();
}
return RedirectToAction("Index");
catch
{
return View();
}
}
View: Create.chtml
#{
ViewBag.Title = "Create";
}
<h2>Create</h2>
#using (Html.BeginForm()) {
#Html.ValidationSummary(true)
<fieldset>
<div class="editor-label">ID</div>
<div class="editor-field">
#Html.EditorFor(ViewBag.keys.ID) #****** THIS IS NOT WORKING ********#
</div>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>

#Html.EditorFor requires a strongly-typed model. It cannot be used with data from the ViewBag.
If you must use ViewBag to pass data to the view, use #Html.TextFor(...) instead to manually setup the input field.
However, I recommend you use a strongly-typed model.
Edit:
Model:
public class MyModel
{
public TABLE_CODES Keys { get; set; }
}
Controller:
var model = new MyModel();
model.Keys = new TABLE_CODES();
return View(model);
View:
#model MvcApplication1.MyModel
....
#Html.EditorFor(m => m.Keys)

Related

"The model item passed into the dictionary is of type '" [duplicate]

This question and community wiki answer has been added to assist in closing out numerous unanswered questions as discussed in this meta post.
I have some code and when it executes, it throws an exception saying:
The model item passed into the dictionary is of type Bar but this dictionary requires a model item of type Foo
What does this mean, and how do I fix it?
The error means that you're navigating to a view whose model is declared as typeof Foo (by using #model Foo), but you actually passed it a model which is typeof Bar (note the term dictionary is used because a model is passed to the view via a ViewDataDictionary).
The error can be caused by
Passing the wrong model from a controller method to a view (or partial view)
Common examples include using a query that creates an anonymous object (or collection of anonymous objects) and passing it to the view
var model = db.Foos.Select(x => new
{
ID = x.ID,
Name = x.Name
};
return View(model); // passes an anonymous object to a view declared with #model Foo
or passing a collection of objects to a view that expect a single object
var model = db.Foos.Where(x => x.ID == id);
return View(model); // passes IEnumerable<Foo> to a view declared with #model Foo
The error can be easily identified at compile time by explicitly declaring the model type in the controller to match the model in the view rather than using var.
Passing the wrong model from a view to a partial view
Given the following model
public class Foo
{
public Bar MyBar { get; set; }
}
and a main view declared with #model Foo and a partial view declared with #model Bar, then
Foo model = db.Foos.Where(x => x.ID == id).Include(x => x.Bar).FirstOrDefault();
return View(model);
will return the correct model to the main view. However the exception will be thrown if the view includes
#Html.Partial("_Bar") // or #{ Html.RenderPartial("_Bar"); }
By default, the model passed to the partial view is the model declared in the main view and you need to use
#Html.Partial("_Bar", Model.MyBar) // or #{ Html.RenderPartial("_Bar", Model.MyBar); }
to pass the instance of Bar to the partial view. Note also that if the value of MyBar is null (has not been initialized), then by default Foo will be passed to the partial, in which case, it needs to be
#Html.Partial("_Bar", new Bar())
Declaring a model in a layout
If a layout file includes a model declaration, then all views that use that layout must declare the same model, or a model that derives from that model.
If you want to include the html for a separate model in a Layout, then in the Layout, use #Html.Action(...) to call a [ChildActionOnly] method initializes that model and returns a partial view for it.
This question already has a great answer, but I ran into the same error, in a different scenario: displaying a List in an EditorTemplate.
I have a model like this:
public class Foo
{
public string FooName { get; set; }
public List<Bar> Bars { get; set; }
}
public class Bar
{
public string BarName { get; set; }
}
And this is my main view:
#model Foo
#Html.TextBoxFor(m => m.Name, new { #class = "form-control" })
#Html.EditorFor(m => m.Bars)
And this is my Bar EditorTemplate (Bar.cshtml)
#model List<Bar>
<div class="some-style">
#foreach (var item in Model)
{
<label>#item.BarName</label>
}
</div>
And I got this error:
The model item passed into the dictionary is of type 'Bar', but this
dictionary requires a model item of type
'System.Collections.Generic.List`1[Bar]
The reason for this error is that EditorFor already iterates the List for you, so if you pass a collection to it, it would display the editor template once for each item in the collection.
This is how I fixed this problem:
Brought the styles outside of the editor template, and into the main view:
#model Foo
#Html.TextBoxFor(m => m.Name, new { #class = "form-control" })
<div class="some-style">
#Html.EditorFor(m => m.Bars)
</div>
And changed the EditorTemplate (Bar.cshtml) to this:
#model Bar
<label>#Model.BarName</label>
Observe if the view has the model required:
View
#model IEnumerable<WFAccess.Models.ViewModels.SiteViewModel>
<div class="row">
<table class="table table-striped table-hover table-width-custom">
<thead>
<tr>
....
Controller
[HttpGet]
public ActionResult ListItems()
{
SiteStore site = new SiteStore();
site.GetSites();
IEnumerable<SiteViewModel> sites =
site.SitesList.Select(s => new SiteViewModel
{
Id = s.Id,
Type = s.Type
});
return PartialView("_ListItems", sites);
}
In my case I Use a partial view but runs in normal views
Consider the partial map.cshtml at Partials/Map.cshtml. This can be called from the Page where the partial is to be rendered, simply by using the <partial> tag:
<partial name="Partials/Map" model="new Pages.Partials.MapModel()" />
This is one of the easiest methods I encountered (although I am using razor pages, I am sure same is for MVC too)
First you need to return an IEnumerable version of your model to the list view.
#model IEnumerable<IdentityManager.Models.MerchantDetail>
Second, you need to return a list from the database. I am doing it via SQL Server, so this is code I got working.
public IActionResult Merchant_Boarding_List()
List<MerchantDetail> merchList = new List<MerchantDetail>();
var model = new MerchantDetail();
try
{
using (var con = new SqlConnection(Common.DB_CONNECTION_STRING_BOARDING))
{
con.Open();
using (var command = new SqlCommand("select * from MerchantDetail md where md.UserGUID = '" + UserGUID + "'", con))
{
using (SqlDataReader reader = command.ExecuteReader())
{
while (reader.Read())
{
model.biz_dbaBusinessName = reader["biz_dbaBusinessName"].ToString();
merchList.Add(model);
}
}
}
}
}
catch (Exception ex)
{
}
return View(merchList);
Passing the model value that is populated from a controller method to a view
public async Task<IActionResult> Index()
{
//Getting Data from Database
var model= await _context.GetData();
//Selecting Populated Data from the Model and passing to view
return View(model.Value);
}
one more thing.
if your view is a partial/sub page and the model for that partial view is null for some reason (e.g no data) you will get this error. Just need to handle the null partial view model

Including a model within a model, I think?

I am extremely new to ASP/MVC3/C# but currently undertaking a project that requires me to list all the users in the database as a drop down list, ideally I want to filter this so that it only shows users in certain roles (i.e. either Admin, Staff or Student).
However my problem lies in the following, I have a controller (Requests.cs) with a method that does the following, and allows me to go to http://server/Requests/UserList:
public ActionResult UserList()
{
// Create our view model
var users = Membership.GetAllUsers();
var model = new StudentListViewModel
{
Users = users.OfType<MembershipUser>().Select(x => new SelectListItem
{
Value = x.ProviderUserKey.ToString(),
Text = x.UserName
})
};
return View(model);
}
A view model that looks like this:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.ComponentModel.DataAnnotations;
using System.Web.Mvc;
namespace AcademicRegistry.ViewModels
{
public class StudentListViewModel
{
[Display(Name = "select user")]
public string SelectedUser { get; set; }
public IEnumerable<SelectListItem> Users { get; set; }
}
}
And the relevant view:
#model AcademicRegistry.ViewModels.StudentListViewModel
#using (Html.BeginForm())
{
#Html.LabelFor(x => x.SelectedUser)
#Html.DropDownListFor(x => x.SelectedUser, Model.Users)
<button type="submit">OK</button>
}
All of these work fine (in fact this is all code I found from another post on here) and this view displays what I want, i.e. a dropdown list with the value set to the userId and the text displayed as the username.
However I want this to be displayed within a different view, namely EditUser, however this already has a model associated with it (#model AcademicRegistry.Models.Requests):
#model AcademicRegistry.Models.Requests
#{
ViewBag.Title = "Edit";
}
<h2>Edit</h2>
<script src="#Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>
#using (Html.BeginForm()) {
#Html.ValidationSummary(true)
<fieldset>
<legend>Requests</legend>
#Html.HiddenFor(model => model.Id)
<div class="editor-label">
#Html.LabelFor(model => model.StudentId)
</div>
<div class="editor-field">
*Wanting the dropdown list to show here*
</div>
<p>
<input type="submit" value="Save" />
</p>
</fieldset>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
I was wondering if anyone knew how I would go about having my drop down show where it needs to? This dropdown will be used on quite a few pages so ideally needs to be re-usable, and as I mentioned filtered to only show users that fall in to the 3 roles I have setup.
You would need to place the code that currently gets the list of users and creates the SelectListItem list into a seperate method that other controllers can access.
You would then add the Users property to the ViewModels that will be displaying this list:
public IEnumerable<SelectListItem> Users { get; set; }
So at this point, you have the list that you need to display. You can then either create a user control that takes the list and renders it, or just use the built-in DropDownList as you currently have:
#Html.DropDownListFor(x => x.SelectedUser, Model.Users)

Passing JSON data from controller action to a razor view

I would like to know how can I pass data from controller to view in mvc3 razor.
The view is in .cshtml file.
I have a variable in the controller where I am storing the data I need to push to the listbox control in the view.
var results = data.Select(item => new { id = item, label = item.Value, value = item.Key });
Doing this:
return Json(results, JsonRequestBehavior.AllowGet);
Gives me only a popup with the data which needs to be pushed to the listbox:
In the view the listbox resides in accordion control:
<div id="accordion">
#{int i=0;}
#foreach (var item in Model.Parameters)
{
<h3>#Html.LabelFor(m => item.Name, item.Prompt)</h3>
<div>
<div class="editor-field">
<select multiple id="#("Select" +item.Name)" name="#("Select" +item.Name)"></select>
</div>
</div>
i++;
}
</div>
So, I would like to know what should I do to push the items to the listbox instead of showing a popup for the control
Beginner in MVC, Thanks for your understanding.
Thanks in advance, Laziale
EDIT: Json format output
{System.Linq.Enumerable.WhereSelectListIterator<System.Collections.Generic.KeyValuePair<string,string>,<>f__AnonymousType1<System.Collections.Generic.KeyValuePair<string,string>,string,string>>}
returning JSON to your razor view is probably not the best method. I would suggest use a viewModel which is a c# class by itself.
namespace Test
{
public class ViewModel
{
public bool GivingAPresentation { get; set; }
}
}
public class MyController: Controller
{
public virtual ActionResult Index()
{
var model=new ViewModel(){GivingAPresentation =true;}
return View(model);
}
}
Your view code:
#model Test.ViewModel <!-- Note the full namespace -->
<br>GivingAPresentation: #Model.GivingAPresentation </br>
If you are forced to work with a JSON object that is returned from your action then you need to deserialize it first and then work with that object. you can read this post http://www.drowningintechnicaldebt.com/ShawnWeisfeld/archive/2010/08/22/using-c-4.0-and-dynamic-to-parse-json.aspx on how to parse JSON to a c# dynamic object.
Let me know if that helps.

Simple Form update without DB persistence

I have a simple ASP.NET MVC 3 dummy app (just learning MVC coming from WebForms).
And I'm pretty confused about how to update a form without actually having some DB stuff in between. I just have a form with a textbox and after I press the button I want to see the string in uppercase. But my nothing happens.
The controller:
public ActionResult Index()
{
ToUppercaseModel model = new ToUppercaseModel { TheString = "testing" };
return View(model);
}
[HttpPost]
public ActionResult Index(ToUppercaseModel model)
{
model.TheString = model.TheString.ToUpper();
return View(model);
}
The model:
public class ToUppercaseModel
{
[Display(Name = "My String")]
public string TheString { get; set; }
}
And the view:
#using (Html.BeginForm()) {
<div>
<div class="editor-label">
#Html.LabelFor(m => m.TheString)
</div>
<div class="editor-field">
#Html.TextBoxFor(m => m.TheString)
</div>
<p>
<input type="submit" value="Convert" />
</p>
</div>
}
This is a simple as it gets I think. Now obviously the return View(model); in the 2nd Index method is not working. I saw some stuff about RedirectToAction() and storing the data in TempData. Most example just submit some id, but since I don't have db that does not work.
If I do this:
return RedirectToAction("Index", model);
I get a
No parameterless constructor defined for this object.
Error Message. This should be simple, no? I think I understand the Post/Redirect/Get concept, but don't see how to apply it for something simple as this.
Thanks for some clarification.
When MVC renders a view it will use the attempted value of a field rather than the model's value if it exists (eg in a datefield I put "Tuesday", this won't model bind but you'll want to show the user the field with their input and highlighted as invalid), you're changing the model's value but not the attempted value.
The attempted value is held in the modelstate dictionary:
ModelState["KeyToMyValue"].Value.Value.AttemptedValue
Accessing and changing these values can be tricky unless you want a load of magic strings in your code, and as validation happens on modelbinding your changed value won't be validated.
My recommendation in these circumstances is to call ModelState.Clear(), this will remove all validation and attempted values, then change your model directly. Finally you want to get your validation on the model by using TryValidateModel(yourModel).
Be aware that this method is probably the easiest non-hacky method of doing this but will remove attempted values that could not bind from the returned view.
You have to call 1 method from the 2 method, but you have to change it
public ActionResult Index(ToUppercaseModel model)
and send to 1 method your model.
public ActionResult Index(ToUppercaseModel? model)
{
if (model == null)
ToUppercaseModel model = new ToUppercaseModel { TheString = "testing" };
return View(model);
}
I think I got a solution, it works, but not sure if this is the way it should be?
Basically I just put my model into the TempData and call the normal Index method again.
public ActionResult Index()
{
ToUppercaseModel model = null;
if (TempData["FeaturedProduct"] == null)
{
model = new ToUppercaseModel { TheString = "testing" };
}
else
{
model = (ToUppercaseModel)TempData["FeaturedProduct"];
}
return View(model);
}
[HttpPost]
public ActionResult Index(ToUppercaseModel model)
{
model.TheString = model.TheString.ToUpper();
TempData["FeaturedProduct"] = model;
//return View(model);
return RedirectToAction("Index");
}

Accessing Route Value of Id inside a Partial View

I am trying to access the Route value of id from inside the PartialView. I have a Post, Comments scenario. And I want my Comments partialview to have access to the PostId.
The Url looks like the following:
Posts/Detail/2
Now, how can I access the value 2 inside the Partial View?
UPDATE:
I changed my CommentsController to the following:
public ActionResult Add(string id,string subject,string name,string body)
{
}
But now when I debug it does not even step into the Add method.
UPDATE 2:
My partial view looks like the following:
<div class="title">Add Comment</div>
#using (Html.BeginForm("Add","Comments",FormMethod.Post))
{
#Html.Label("Subject")
#Html.TextBox("subject")
<br />
#Html.Label("Name")
#Html.TextBox("name")
<br />
#Html.Label("Body")
#Html.TextBox("body")
<input type="submit" value="submit" />
}
And here is the Controller:
public ActionResult Add(string id, string subject, string name, string body)
If I remove id from the above controller action then it works.
You could fetch it from RouteData:
In WebForms:
<%
var id = ViewContext.RouteData.Values["id"];
%>
In Razor:
#{
var id = ViewContext.RouteData.Values["id"];
}
After showing your code it seems that in your form you are not passing the id. So modify it like this:
#using (Html.BeginForm("Add", "Comments", new { id = ViewContext.RouteData.Values["id"] }, FormMethod.Post))
{
...
}
or use a hidden field if you prefer:
#using (Html.BeginForm("Add", "Comments", FormMethod.Post))
{
#Html.Hidden("id", ViewContext.RouteData.Values["id"])
...
}
Now you should get the id in your Add controller action.
Inside of your controller, you could put the route data in the view model that gets sent to the view; the view can then pass along its view model (or portions of it) to the partial view. Your views, including partial views, should ideally only need to rely on data provided by their view models to render.
public ActionResult Add(string id, string subject, string name, string body)
{
return View(
new AddedViewModel {
Id = id,
// etc...
}
);
}

Resources