submit model with RenderPartial on View - asp.net-mvc-4

So imagine this:
There is a View.
There is a model with this view in the following form:
Id
List<Food>
within each Food, there is:
Id
Name
List<Ingredients>
each ingredient contains:
Id
Name
Qty
This is in 1 model.
I have a view which takes in this Model.
I then have a partial view which takes in the List and renders it on the screen.
That works however when the form is submitted (the button is on the main view), the data fails to bind/is not shown in the Model.
what is the correct way to be able to bind the data back?
it does work when I take the whole thing and put it in the main view itself but due to reusability reasons, it makes sense having it in a partial view so I can just "drop" it on any page and pass the data.
The partial view takes in this:
#model List<FoodProject.Web.Models.FoodViewModel>
Thanks
UPDATE
I tried using the EditorTemplate and it seems to almost respect the model binding conventions as before using the Html.Partial was not doing so. That was producing things like:
[0].PropertyName
instead of:
ModelName[0].PropertyName
The template editor is almost there but gives me:
ModelName.[0].Id
I believe this is why when posting, I get null back in the model collection
how can I make it respect the model binding? where am I going wrong?

You should be close with the Editor Template because that worked for me. I'll show my example and maybe it will help you see what you have wrong.
The Models:
public class TestModelA
{
public List<TestModelB> PropA { get; set; }
}
public class TestModelB
{
public string PropB { get; set; }
}
The Editor Template (TestModelB.cshtml) placed in Views/Shared/EditorTemplates:
#model MvcTest.Models.TestModelB
#Html.EditorFor(m => m.PropB)
The main view:
#model MvcTest.Models.TestModelA
#using (Html.BeginForm())
{
#Html.EditorFor(m => m.PropA)
<input type="submit" value="Submit" />
}

Related

ASP.NET MVC post still goes through even though validation should block it

If I do the following
ViewModel:
public class ToleranceInput{
public decimal Tolerance{get;set;}
}
And in my views
AssignTolerances.cshtml
#model ToleranceInput
#using (#Html.BeginForm("AssignTolerances", Tolerances", FormMethod.Post, new {#class="form-horizontal"}))
{
#Html.ValidationSummary()
#Html.Partial("_AssignOrEditTolerances", Model)
<div class="form-actions">
<button type="submit">
<i class="icon-ok icon-white"></i> Save
</button>
</div>
}
_AssignOrEditTolerances.cshtml:
#model ToleranceInput
#Html.TextBoxFor(a=>a.Tolerance)
#Html.ValidationMessageFor(a=>a.Tolerance)
The standard MVC validation works correctly. When I type in letters (instead of numbers) it shows a red error message as it should. However, in the viewmodel if I change Tolerance to a nullable decimal? it does not work correctly. if I type a non-decimal such as 'asdf' in the textbox I see a quick flash of the red validation messsage when I hit the save button, but then the POST goes through and the [HttpPost] AssignTolerances method in my controller gets executed. Why is this happening? Any ideas?
Edit: I have just found the following link. Looks like the strings get converted to nulls and the workaround looks horrible: Integer validation against non-required attributes in MVC
Have you also considered adding the check in the controller action?
[HttpPost]
public ActionResult TestPost(ToleranceInput model)
{
if(!ModelState.IsValid)
{
return View(model);
}
}
And then simply add validation into your Model, such as the [Required] attribute

Strange behaviour in MVC 4 partial view

I got this simple partial view
#ModelType someModel
<div>
#Code
Dim list = //some Ienumerable of SelectListItem
Html.ListBox("MappingFields", list)
End Code
</div>
The same code works in a main view nicely. But when I shift it to a partial view and have an action in the main controller as:
Function RetrurnMyPartialView(id As String) As PartialViewResult
//get model for id
Return PartialView("_partialView", model)
End Function
It is not working!!
I got another partial view with similar implementation and is working fine. Can a ListBox be not used in partial views?
This wasted almost a day. The resulting html for the partial view is an empty div like:
<div>
</div>
I tried to debug, and the model, list items and everything are binding properly.
Please help..Thank you.

MVC Sharing strongly typed Partial View

I am trying to render a strongly typed partial view and share it between different parent views.
so I would have a parent view A that is an edit form which has element as well as partial view for Location.
#model Model.Contact
....
#Html.HiddenFor(model => model.LocationID)
#{Html.RenderPartial("../Shared/_Location", Model.Location);}
partial view is an edit form update/insert form
#model Model.Location
....with form elements bound to model passed
<div class="row-fluid">
<div class="span6">
Address 1<br />
#Html.EditorFor(model => model.Address1)<br />
</div>
<div class="span6">
Address 2<br />
#Html.EditorFor(model => model.Address2)<br />
</div>
</div>......
and view B is bounded to different model which also has a need to update Location.
#model Model.JobAssignment
....
#Html.HiddenFor(model => model.OriginLocationID)
#{Html.RenderPartial("../Shared/_Location", Model.OriginLocation);}
It's OriginLocation because this entity has multiple FKs to Location thus I named them differently in CodeFirstMapping
[ForeignKey("OriginLocationID")]
public virtual Location OriginLocation { get; set; }
[ForeignKey("DestinationLocationID")]
public virtual Location DestinationLocation { get; set; }
Database and and Entities are all fine with Fk properly setup to Location by Job and Contact.
What i had before is Location partial view would not get a location entity/model but instead be bound to whatever parent model is like #model Model.Contact just like parent view, that would work fine, as the controls in partial view is prefixed with Location.* and Location_* for ID and it works its magic, having controls as: #Html.EditorFor(model => model.Location.Address1)
but im trying to share the location partial view just like I used to do with UserControls in asp.net, so want to bind it to
I cannot get it to work, the closest I get is to bound the data, but on edit/save I get strange error.
"A referential integrity constraint violation occurred: A primary key property that is a part of referential integrity constraint cannot be changed when the dependent object is Unchanged unless it is being set to the association's principal object. The principal object must be tracked and not marked for deletion."
Please Please somebody point me to the right direction!

MVC view displaying time when date only required

I have the following model property
[DataType(DataType.Date)]
[DisplayFormat(DataFormatString = "{0:d/M/yyyy}", ApplyFormatInEditMode = true)]
[Display(Name = "Date of Screening")]
public DateTime? ScreeningDate { get; set; }
and the following view:
#Html.TextBoxFor(model => model.ScreeningDate, new { #class="date maintainState" })
and yet I get this markup for the textbox:
<input value="18/06/2013 12:00:00 AM" class="date maintainState" ... type="text" />
I want the value to be 18/06/2013
It works when I apply #Html.EditorFor, but I need control over the class attribute. What am I doing wrong? Thank you very much.
It works when I apply #Html.EditorFor, but I need control over the
class attribute. What am I doing wrong?
Nothing, it's how things work. Only the EditorFor and DisplayFor helpers respect the [DisplayFormat] attribute.
I understand that you are using TextBoxFor because you want to apply a custom class to the field which is what the EditorFor doesn't allow you to. But that's not true. All that the EditorFor helper does is to render the corresponding editor template. And if you do not have a custom template it renders the default one.
So what?
Well, you write a custom editor template for the DateTime type that will behave as you want. Just like that in ~/Views/Shared/EditorTemplates/DateTime.cshtml:
#Html.TextBox("", ViewData.TemplateInfo.FormattedModelValue, ViewData)
And then simply:
#Html.EditorFor(model => model.ScreeningDate, new { #class="date maintainState" })
Now killed 2 rabbits with one bullet:
We applied the desired format
We applied the desired class to the input field
And if you wanna kill a third rabbit and be able to validate this custom format when the form is submitted back (because remember that the default model binder doesn't care much about this [DisplayFormat] attribute) you could write a custom model binder that will respect the [DisplayFormat] attribute, just as I explained here: https://stackoverflow.com/a/7836093/29407

MVC Partial view with controller, ajax - how do I ge the partial controller to get data?

I'm learning MVC and am stumped by this. I'm trying to factor some common code that gets data and displays it in a WebGrid into a partial view that i can use on multiple pages.
My home controller Index method just does a return View(). The Home view looks like this:
#using (Ajax.BeginForm("SearchAction", "Search",
new AjaxOptions { UpdateTargetId = "data-grid", HttpMethod = "Post" }))
{
#Html.TextBoxFor(model => model.name)
<input type="submit" value="Search" />
}
#{
<div id="data-grid">
#Html.Partial("SearchResults", Model)
</div>
}
I'm trying to use Ajax to avoid losing my search form data when clicking a WebGrid pager link, which are rendered as normal links.
My SearchController looks like this:
public ActionResult SearchAction(string name)
{
return RedirectToAction("SearchResults", new { name = name });
}
public ActionResult SearchResults(string name)
{
//does database query and sticks results in the viewbag
//filter on optional name parameter
VieweBag.Members = MyQueryResults;
return PartialView();
}
My SearchResults shared view, data is passed in via ViewBag.Members:
#{
var grid = new WebGrid(null, rowsPerPage: ViewBag.Pagesize);
grid.Bind(ViewBag.Members);
#grid.GetHtml(// etc. etc.)
}
The results I'm getting is that the ViewBag.Pagesize and ViewBag.Members binding fails since there is no data in the viewbag. Obviously, my partial controller is not being called to do the initial query and put stuff in the ViewBag when the home page is first loaded. How do I make that happen?
The other weird thing is that if I just copy the database query code into my home controller (where it originally was) to force the original query, then if I put some text into the search field and do a search, the partial view renders by itself on a new page. Why is that happening, I thought it would only render as part of my home page.
I've cobbled this partial view together from various answers/places and have no doubt gotten something horribly wrong :\
The partial page won't pass through a controller, but simply render the view directly. If you want to pass view data to the partial view, there is an overloaded function that takes a viewdata dictionary. I'm sorry I can't be more detailed, but I'm on my mobile (waiting for my son to fall asleep in the other room) :)
Update:
If you want to trigger a GET action for your partial view, you can use Html.Action. Here are some useful links:
MSDN RenderAction
Difference between RenderPartial and RenderAction
Further, it would probably make sense for you to move your form tags into your partial view, but those are details for when you clean up the code.
Jonass is right, the ViewBag only propagates between the Controller and the View.
One thing you can do is make the model of the partial view be the same as the type of data you're putting into the ViewBag.
So if for example your MyQueryResults is of type:
IEnumerable<Result>
In your partial view you'd add
#Model IEnumerable<Result>
And then in the main view, you'd pass it through the Render method:
#Html.Partial("SearchResults", ViewBag.Members);
You'll need to tweak this a bit to make sure it's the right type, but this should do the trick.
Good luck!