I am trying to pass a different model to the partial view from a view. I have two separate controller actions for both of them and two different view models. But when I call the partial view from within the view it gives me the error
The model item passed into the dictionary is of type 'Application.ViewModels.Model1ViewModel', but this dictionary requires a model item of type 'Application.ViewModels.PartialViewModel'.
I am calling it like this:
#Html.Partial("_CreateUniFunctionPartial")
the model call in the view is
#model Application.ViewModels.Model1ViewModel
and model in partial view file is
#model Application.ViewModels.PartialViewModel
I am not sure how to pass the partial view so it doesnt give this error.
EDIT
Partial view
#model Application.ViewModels.PartialViewModel
#using (Html.BeginForm("partialview", "ApplicationDetail", FormMethod.Post))
{
<div class="form-horizontal">
<h4>UniFunctionViewModel</h4>
<hr />
#Html.ValidationSummary(true)
<div class="form-group">
#Html.LabelFor(model => model.detail, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.TextBoxFor(model => model.detail, new { #placeholder = "Further Information" })
#Html.ValidationMessageFor(model => model.detail)
</div>
</div>
</div>
}
you are using the right method but not passing in the right arguments
you might want to try it like this:
#Html.Partial("~/[path_to_root_only_if_exists]/_CreateUniFunctionPartial.cshtml", new Application.ViewModels.PartialViewModel())
if you do not pass in a model, it will automatically take the one from its parent, which in your case is
Application.ViewModels.Model1ViewModel
One thing you will need to do is regenerate a model or utilize a property in the model. For example:
public class OuterViewModel
{
public InnerViewModel InnerViewModel { get; set; }
}
public class InnerViewModel
{
public string SomeProperty { get; set; }
}
In the top page, you can accept the OuterViewModel, then pass the InnerViewModel to the Partial.
Outer.cshtml:
#model OuterViewModel
#Html.Partial("_InnerPartial", Model.InnerViewModel)
_InnerPartial.cshtml:
#model InnerViewModel
#using (Html.BeginForm("Inner", "Controller"))
{
<div>
#Html.AntiForgeryToken()
#Html.TextBoxFor(m => m.SomeProperty)
<input type="submit" value="Save" />
</div>
}
This is quite simple to do. There is a html directive which can render a partial view. Following is the code sample:
#Html.Partial("nameOfPartial", Model)
Now here Model could be from your main controller.
or you can define a new controller action with partialviewresult as return type and try to render it in the page like this:
#{Html.RenderAction("Someaction", "somecontroller");}
Related
I have a code where I try to bind data from a Model to DropDownListFor and it successfully displays the drop down in the View when executed but it doesn't return the selected value except for 0 always. I have checked and tried numerous solutions posted here but it doesn't seem to work out.
AdminController.cs (Create() action for the Create.cshtml View and ProductSave() Form action)
public ActionResult Create()
{
var categoryList = _context.Categories.ToList();
var viewModel = new CreateViewModel
{
Categories = categoryList,
};
return View(viewModel);
}
[HttpPost]
public ActionResult ProductSave(CreateViewModel viewModel)
{
return Content(viewModel.SelectedId.ToString());
}
CreateViewModel.cs
public class CreateViewModel
{
public IEnumerable<Category> Categories { get; set; }
public int SelectedId { get; set; }
public Product Product { get; set; }
}
Create.cshtml (I only included the necessary fields for the View here)
#using (Html.BeginForm("ProductSave", "Admin",null,FormMethod.Post))
{
#Html.AntiForgeryToken()
<div class="default-form-wrap style-2">
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.Product.Category_Id, new { #class = "control-label"})
<div class="nice-select single-select">
#Html.DropDownListFor(model => model.SelectedId,new SelectList(Model.Categories,"Id","CategoryName"),"Select Category", new { #class = "form-control" } )
#Html.ValidationMessageFor(model => model.Product.Category_Id, "", new { #class = "text-danger" })
</div>
</div>
<div class="row text-center">
<div class="col">
<input type="submit" value="Create" class="btn btn-base" />
</div>
</div>
</div>
When selected a Category the value is changed accordingly
Screenshot
I tried return Content() in the form action method to see the output from the bound viewmodel after I hit submit button
return Content(viewModel.SelectedId.ToString());
But here's what I get
Screenshot
I just want to get the output corresponding to the selected list item.
Looking at your HTML the culprit is probably:
<div class="nice-select single-select">
The DropdownListFor should be generating a <select> element with an ID value that MVC would resolve back to populate the model on the return trip. Your HTML is generating a <ul>.
You could remove that outer <div> or the class definitions and it should work. Getting that "nice-select" to work might take some reading around what it is and interacting with MVC tied controls to get it compatible with the form postback. You might need to use some Javascript and an #Html.HiddenFor so when a value is selected from the <ul> the hidden input that is tied to the model is updated and available for the post-back.
The problem I will be describing is very similar to ones I already found (e.g. this post with nearly identical name) but I hope that I can make it into something that is not a duplicate.
I have created a new ASP.NET MVC 5 application in Visual Studio. Then, I defined two model classes:
public class SearchCriterionModel
{
public string Keyword { get; set; }
}
public class SearchResultModel
{
public int Id { get; set; }
public string FirstName { get; set; }
public string Surname { get; set; }
}
Then I created the SearchController as follows:
public class SearchController : Controller
{
public ActionResult Index()
{
return View();
}
public ActionResult DisplaySearchResults()
{
var model = new List<SearchResultModel>
{
new SearchResultModel { Id=1, FirstName="Peter", Surname="Pan" },
new SearchResultModel { Id=2, FirstName="Jane", Surname="Doe" }
};
return PartialView("SearchResults", model);
}
}
as well as views Index.cshtml (strongly typed with SearchCriterionModel as model and template Edit) and SearchResults.cshtml as a partial view with model of type IEnumerable<SearchResultModel> (template List).
This is the Index view:
#model WebApplication1.Models.SearchCriterionModel
#{
ViewBag.Title = "Index";
}
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>SearchCriterionModel</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.Keyword, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Keyword, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Keyword, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="button" id="btnDisplaySearchResults" value="Search" onclick="location.href='#Url.Action("DisplaySearchResults", "SearchController")'" />
</div>
</div>
</div>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
<div id="searchResults">
</div>
As you can see, I added a div with id="searchResults" below the standard template and edited the button. What I want is to display the partial view SearchResults.cshtml in the div on the bottom, but only after the button is clicked. I have succeeded in showing a partial view there by using #Html.Partial("SearchResults", ViewBag.MyData), but it is rendered when the parent view is loaded for the first time and I set ViewBag.MyData in the Index() method already, which is not what I want.
Summary: On clicking the button, I will obtain some List of SearchResultModel instances (via database access) and then the partial view should be rendered, using this newly obtained data as model. How can I accomplish this? I already seem fail at the first step, that is reacting to the button click with the above code. Right now, I navigate to the URL ~/Search/DisplaySearchResults, but of course there's nothing there and no code-behind method is called.
In traditional ASP.NET I'd just have added a server-side OnClick handler, set the DataSource for a grid and show the grid. But in MVC I already fail with this simple task...
Update: Changing the button to #Html.ActionLink I can finally enter the controller method. But naturally since it returns the partial view, it's displayed as the whole page content. So the question is: How do I tell the partial view to be rendered inside a specific div on the client side?
Change the button to
<button id="search">Search</button>
and add the following script
var url = '#Url.Action("DisplaySearchResults", "Search")';
$('#search').click(function() {
var keyWord = $('#Keyword').val();
$('#searchResults').load(url, { searchText: keyWord });
})
and modify the controller method to accept the search text
public ActionResult DisplaySearchResults(string searchText)
{
var model = // build list based on parameter searchText
return PartialView("SearchResults", model);
}
The jQuery .load method calls your controller method, passing the value of the search text and updates the contents of the <div> with the partial view.
Side note: The use of a <form> tag and #Html.ValidationSummary() and #Html.ValidationMessageFor() are probably not necessary here. Your never returning the Index view so ValidationSummary makes no sense and I assume you want a null search text to return all results, and in any case you do not have any validation attributes for property Keyword so there is nothing to validate.
Edit
Based on OP's comments that SearchCriterionModel will contain multiple properties with validation attributes, then the approach would be to include a submit button and handle the forms .submit() event
<input type="submit" value="Search" />
var url = '#Url.Action("DisplaySearchResults", "Search")';
$('form').submit(function() {
if (!$(this).valid()) {
return false; // prevent the ajax call if validation errors
}
var form = $(this).serialize();
$('#searchResults').load(url, form);
return false; // prevent the default submit action
})
and the controller method would be
public ActionResult DisplaySearchResults(SearchCriterionModel criteria)
{
var model = // build list based on the properties of criteria
return PartialView("SearchResults", model);
}
So here is the controller code.
public IActionResult AddURLTest()
{
return ViewComponent("AddURL");
}
You can load it using JQuery load method.
$(document).ready (function(){
$("#LoadSignIn").click(function(){
$('#UserControl').load("/Home/AddURLTest");
});
});
source code link
While attempting access/use the default editor templates in MVC5.
I receive the error:
The model item passed into the dictionary is of type 'System.String',
but this dictionary requires a model item of type
'MvcContrib.UI.InputBuilder.Views.PropertyViewModel`1[System.Object]'.
Why is this happening?
Some background:
My model (TestModel.cs):
namespace MyTest.Web.Controllers.Models
{
public class TestModel
{
public TestModel()
{
Name = "Fred";
}
public string Name { get; set; }
}
}
My Controller (MyTestController.cs):
namespace MyTest.Web.Controllers
{
public class MyTestController : Controller
{
[HttpGet]
public ActionResult Index()
{
TestModel testData = new TestModel();
return View(testData);
}
...
}
}
My View (Index.cshtml):
#model MyTest.Web.Controllers.Models.TestModel
#{
ViewBag.Title = "MyTest";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<div class="mainDisplay">
<h2>Test</h2>
<div>
#using (Html.BeginForm("ProcessResults", "MyTest", FormMethod.Post))
{
<div style="display:block;">
<div style="display:inline;">Name:</div>
<div style="display:inline;">#Html.EditorFor(x => x.Name)</div>
</div>
<div style="display:block;">
<input type="submit" name="Submit" value="Submit" />
</div>
}
</div>
</div>
The error occurs on the following line
<div style="display:inline;">#Html.EditorFor(x => x.Name)</div>
and the view will not render
Things I have tried:
The sample from "ASP.NET MVC Basics Part 1: View Model binding" and this sample works without a problem.
If I add a custom editor template under ~/Views/Shared/EditorTemplates/Name.cshtml and I invoke #Html.EditorFor(x => x.Name, "Name") then the page renders
If I change the code to be #Html.TextBoxFor(x => x.Name, "Name") then the page renders
Finally, the solution has System.Web.Mvc assembly (which should contain the default editor templates).
What am I missing?
Hope someone can help me. I am new to MVC, coming from a winforms/console/vb6background.
Apologies if this has already been answered, I am stuggling to understand how I can resolve the below issue.
I have a view model :
public class testvm
{
public int id { get; set; }
public DateTime date { get; set; }
public student studentID { get; set; }
public testvm() { }
public testvm (student s)
{
studentID = s;
}
}
I am pre-populating the student child object of this ViewModel before it is passed to the view.
Student Model :
public class student
{
[Key]
public int ID { get; set; }
public string Name { get; set; }
}
The problem I have is when the model is returned to the create HTTP post method the student child object is blank.
The controller code :
// GET: testvms/Create
public ActionResult Create(int sId)
{
student a = db.students.Find(sId);
testvm b = new testvm(a);
return View(b);
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = "id,date,student")] testvm testvm)
{
if (ModelState.IsValid)
{
db.testvws.Add(testvm);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(testvm);
}
View code:
#model WebApplication2.Models.testvm
#{
ViewBag.Title = "Create";
}
<h2>Create</h2>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>testvm</h4>
<hr />
#Html.HiddenFor(model => model.studentID.ID)
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.date, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.date, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.date, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</div>
</div>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
The model object on the view is populated with the student information. When this is passed back to Create POST controller the student child object is null!
Can somebody please advise where I am going wrong or of the correct way to achieve this?
My application will have many forms that will all need to be pre-populated with student information. Each student will have many forms that will need to be filled out for them.
Many thanks in advance,
Rob
For every property in domain model (in your case testvm) you must have an EditorFor or Input element (like TextBoxFor or so) on your view(or HiddenFor for ID or other non user ui data).It may be a pain binding nested models in MVC as the DefaultModelBinder may not be able to bind whole object.However it would be safer approach to expose only the required properties on view like
#Html.HiddenFor(model => model.studentID.ID)
#Html.HiddenFor(model => model.studentID.Name)
and later on Controller Side
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(testvm testvm)
{
var originalobj=db.get //get fresh copy from data store
originalobj.Name=testvm.Name;
// ...other properties
//perform required operations on originalobj
}
you may use AutoMapper for this Purpose as
Mapper.CreateMap<testvm,testvm>();
originalobj=Mapper.Map<testvm,testvm>(testvm,originalobj);
you may find more information about Automapper on :
https://github.com/AutoMapper/AutoMapper/wiki/Getting-started
Your property name is called studentId (even though standard C# property naming convention dictates that it should have been called StudentId):
public student studentID { get; set; }
But in your Bind attribute you seem to have specified some student property which doesn't really exist on your view model:
[Bind(Include = "id,date,student")]
So you probably want to get rid of this Bind attribute from your controller action:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(testvm testvm)
{
...
}
Also note that you only have a hidden field for the student id inside your form:
#Html.HiddenFor(model => model.studentID.ID)
You don't have a corresponding hidden field for the student name property, so it will never be posted back to your controller action.
Your attribute [Bind(Include = "id,date,student")] should include the names of the properties that you want to be set, student isn't in your model, but studentID is, they have to match.
You don't have to explicitly specify all of the field names that you want to be bound to your model, by default they will be bound anyway unless you tell the binder NOT to bind it by using [Bind(Exclude = "id,date,student")]. Therefore as it currently stands, I'd recommend removing your Include attribute to ease maintenance unless there is an important reason for using it and simply ensure that the models that you bind to only include the values you need.
Secondly, you have to make sure that any values that you are posting back from a form in your view have the same parameter names and are structured the same as the ones that you want to be bound to the request model.
This:
#Html.HiddenFor(model => model.studentID.ID)
Is not the same as:
#Html.HiddenFor(model => model.studentID)
I have 2 models in my sample MVC 3 application, SimpleModel and ComplexModel, shown below:
public class SimpleModel
{
public string Status { get; set; }
}
public class ComplexModel
{
public ComplexModel()
{
Simple = new SimpleModel();
}
public SimpleModel Simple{ get; set; }
}
I have defined views for this models:
_SimplePartial.cshtml:
#model SimpleModel
#Html.LabelFor(model => model.Status)
#Html.EditorFor(model => model.Status)
and Complex.cshtml:
#model ComplexModel
#using (Html.BeginForm()) {
#Html.Partial("_SimplePartial", Model.Simple)
<input type="submit" value="Save" />
}
After submitting form, with random value entered in Status field, the value is not binded to my model. The Status field is NULL when I'm checking the model in my controller action:
[HttpPost]
public ActionResult Complex(ComplexModel model)
{
// model.Simple.Status is NULL, why ?
}
Why is it not binded ? I don't want to inherit models. Do I have to write my custom model binders for such simple case ?
Regards.
Instead of:
#Html.Partial("_SimplePartial", Model.Simple)
I would recommend you using Editor templates:
#model ComplexModel
#using (Html.BeginForm())
{
#Html.EditorFor(x => x.Simple)
<input type="submit" value="Save" />
}
and then put the simple partial inside ~/Views/Shared/EditorTemplates/SimpleModel.cshtml or inside ~/Views/Home/EditorTemplates/SimpleModel.cshtml where Home is the name of your controller:
#model SimpleModel
#Html.LabelFor(model => model.Status)
#Html.EditorFor(model => model.Status)
Of course if you prefer to have the partial in some special location and not follow the conventions (why would you?) you could specify the location:
#Html.EditorFor(x => x.Simple, "~/Views/SomeUnexpectedLocation/_SimplePartial.cshtml")
then everything will come into place as expected.
As Daniel Hall suggests in his blog, pass a ViewDataDictionary with a TemplateInfo where HtmlFieldPrefix is set to the name of the SimpleModel-property:
#Html.Partial("_SimplePartial", Model.Simple, new ViewDataDictionary(ViewData)
{
TemplateInfo = new System.Web.Mvc.TemplateInfo
{
HtmlFieldPrefix = "Simple"
}
})