Partial View not passing ViewModel correctly to controller - asp.net-mvc

I have a ViewModel that looks like this:
namespace PGO.ViewModels.Players
{
public class PlayerBuyinViewModel
{
public Player Player { get; set; }
public Buyin Buyin { get; set; }
}
}
I have a view that calls this partial view and also passing in a PlayerBuyinViewModel object with Player being populated already:
#model PGO.ViewModels.Players.PlayerBuyinViewModel
#using (Html.BeginForm("Buyin", "Players"))
{
<label>#Model.Player.GetFullName()</label>
<div class="form-group col-md-12">
<label class="col-md-1 control-label">Buyin</label>
<div class="col-md-2">
#Html.TextBoxFor(m => m.Buyin.Amount)
</div>
<button class="btn btn-primary col-md-2">CONFIRM BUYIN</button>
</div>
}
When I call GetFullName() it prints out the name correctly so the Player is in the view but when I submit my form to the Controller Player in PlayerBuyinViewModel is null.
My expectation is that the same PlayerBuyinViewModel should be used when passed on to the controller with the PlayerBuyinViewModel.Player being populated as well.
Buyin.Amount is being populated as expected from the textbox.
Do I need to create a new PlayerBuyinViewModel in this partial view and repopulate it again or what might be causing this?

Related

HttpException: A public action method 'ListCheckListType' was not found on controller

I checked all the solutions but still doesnt work.I got a partial view page in layout page and When ı run only partial page it works but when ı run another page with layout it doesnt work.
I hope you can help me
Here is my Model :
public CheckListType CheckListType { get; set; }
public IEnumerable<SelectListItem> CheckListTypeList1 { get; set; }
And my Controller :
public ActionResult ListCheckListType()
{
ControlListTypeModel listTypeModel = new ControlListTypeModel();
List<SelectListItem> CheckListTypeList = new List<SelectListItem();
foreach (CheckListType item in checklisttypeRepository.List().ProcessResult)
{
CheckListTypeList.Add(new SelectListItem { Value = item.CheckListTypeId.ToString(), Text = item.CheckListType1 });
}
listTypeModel.CheckListTypeList1 = CheckListTypeList;
return PartialView("~/Areas/User/Views/CheckList/ListCheckListType.cshtml", listTypeModel);
}
View :
#using TodoListApp.Areas.User.Models.ViewModel
#model ControlListTypeModel
<form action="/CheckList/ListCheckListType" method="get">
<div>
CheckListType :
</div>
<div>
#Html.DropDownListFor(modelitem=>modelitem.CheckListType.CheckListTypeId,Model.CheckListTypeList1)
<button type="button" class="butt button bg-info" style="height:40px; width:98px;">Choose CheckListType</button>
</div>
Layout :
<div class="container body-content">
#Html.Action("ListCheckListType");
#RenderBody(){
}
<hr />
<footer>
<p> #DateTime.Now.Year </p>
</footer>
</div>
HttpException: A public action method 'ListCheckListType' was not found on controller
The problem occurs because it searches ListCheckListType action in wrong controller while rendered in partial view. Specifying controller name as well should fix the exception
#Html.Action("ListCheckListType", "Home"); //if action is in HomeController

List of uploaded files with ASP.MVC 5 is empty

I have a webform where I want to select a list of files, enter some additional informations and submit the whole bundle. The controller should than process the list of files according to the additionally entered datas. Therefore, I want to bundle all in one Model:
public class MyModel
{
public string AdditionalInformations { get; set; }
public int AdditionalInformationsId { get; set; }
public IEnumerable<HttpPostedFile> Files { get; set; }
}
public class MyController: Controller
{
[HttpPost]
public ActionResult UploadFile(MyModel Model)
{
switch(Model.AdditionalInformationsId)
{
case 1:
// do something with Model.Files
case 2:
// do something else with Model.Files
...
}
RedirectToAction("Index");
}
}
My view looks like
#model MyProgram.Models.MyModel
#using MyProgram.Models
#using (Html.BeginForm("UploadFile", "MyController", FormMethod.Post,
new { enctype = "multipart/form-data" }))
{
<div class="row">
<div class="col-sm-6">
<input type="file" name="Files" multiple />
</div>
<div class="col-sm-6"></div>
</div>
<div class="row">
<div class="col-sm-3">
#Html.DropDownListFor(model => model.AdditionalInformationsId,
((MyDropDownList)Session["DDLists"]).companies,
Model.AdditionalInformation,
new { #class = "form-control", id = "ReceivingCompany" })
</div>
<div class="col-sm-3">
<br />
<button class="btn btn-default" type="submit">Upload</button>
</div>
<div class="col-sm-6"></div>
</div>
}
If I now use the submit-Button, the model is sent to the controller. E.g. AdditionalInformationsId has the value entered in the form. But IEnumerable Files is allways empty, except if I submit the form without choosing any files for upload, in that case the list contains a null-object. Also adding the attribute id="Files" to the multiple-file input-tag does not work.
I can't find the error. Is there maybe a setting I have to set in the web.config?
Change your property type to IEnumerable of HttpPostedFileBase
public IEnumerable<HttpPostedFileBase> Files { get; set; }
Now the posted files will be mapped to the Files property in your HttpPost action.

MVC default model binder returns Null object

I am having an issue where the default model binder is refusing to bind to my object, which is a List<> of simple objects.
The class:
public class ReferralHistoryDetail {
[Key]
public long Referral_Number { get; set; }
public Guid QuoteGuid { get; set; }
public byte ReferralTypeID { get; set; }
public string Referral_Type { get; set; }
public DateTime ReferralDateTime { get; set; }
public string ReferralComments { get; set; }
}
The controller definition:
[HttpPost]
public ActionResult Save(List<ReferralHistoryDetail> details) {
The view from Fiddler:
What am I doing wrong here?
This is shown via an EditorTemplate in the following manner:
#using (Html.BeginForm("Save", "Home")) {
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
<div class="tab-content">
<div class="tab-pane fade in active form-horizontal" id="basic">
#Html.EditorFor(m => m.ReferralHistoryDetail)
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Save" class="btn btn-default" />
</div>
</div>
}
The value that is coming across as NULL is the one being passed to the POST method of the controller. The default model binder is not able to bind this.
I resolved this.
The problem is the Html.BeginForm() is in the main view, but it is only posting back stuff from the EditorFor()'s.
I had to change my controller to take in the main view's object and not the object that the EditorFor() is dealing with, because the Html.BeginForm() lives on the main view.

Model binding not working inside a partial view

The problem I currently have is that in my partial view, the checkboxes are not being binded correctly by the MVC framework. The CaseViewModel.IsCaseSelected property will always be false regardless of whether or not the checkbox is selected. However, if I hardcode the html in the parent view instead of rendering a partial, then the CaseViewModel.IsCaseSelected property will be properly set corresponding to the checkboxes.
My code is shown below.
The form in my view looks something like this:
<div class="form-group">
<div>
<label for="ProfileName">Profile Name:</label>
<input type="text" name="ProfileName"><br>
</div>
#Html.Partial("~/Views/Shared/_CasesSelection.cshtml", Model.Cases_Category1)
<div id="category2-cases">
<p>category-2</p>
<label for="select-all">Select all</label>
<input id="select-all" type="checkbox" onclick="select_all_toggle(this)" />
#for (int i = 0; i < Model.Cases_Category2.Count; i++)
{
#Html.Label(Model.Cases_Category2[i].CasesNumber.ToString())
#Html.CheckBoxFor(model => model.Cases_Category2[i].IsCaseSelected)
}
</div>
<div>
<input type="submit" value="Create" class="btn btn-default" />
</div>
My partial view looks like this:
#model List<Models.CaseViewModel>
<div id="some-case">
<p>some-case</p>
<label for="select-all">Select all</label>
<input id="select-all" type="checkbox" onclick="select_all_toggle(this)" />
#for (int i = 0; i < Model.Count; i++)
{
#Html.Label(Model[i].CaseNumber.ToString())
#Html.CheckBoxFor(model => model[i].IsCaseSelected)
}
</div>
The model it is binded to looks like this:
public class TestProfileVM
{
[Required]
[RegularExpression(#"^[a-zA-Z0-9-_]+$")]
public string ProfileName { get; set; }
public List<CaseViewModel> Cases_Category1 { get; set; }
public List<CaseViewModel> Cases_Category2 { get; set; }
}
And finally, CaseViewModel looks like this:
public class CaseVM
{
public string CaseType { get; set; }
public int CaseNumber { get; set; }
public bool IsCaseSelected { get; set; }
}
Additional information:
Additionally, in my parent view, when I replace Model.Cases_Category2[i] with Model.Cases_Category2.ElementAt(i), the binding does not work correctly. What is the reason for this, and could it be related to the original problem? Thanks.
I guess problem is the name of checkbox inside partial view. Hence you pass part of view model to partial page, name of checkbox will generated upon that model which you pass to partial page.
So compare generated name for checkbox inside parent view with the name of checkbox inside partial view and if they are not same, change name of checkbox inside partial page accroding to the name checkbox inside parent page.

How can i maintain objects between postbacks?

I'm not so experienced using MVC. I'm dealing with this situation. Everything works well until call the HttpPost method where has all its members null. I don't know why is not persisting all the data on it.
And everything works well, because I can see the data in my Html page, only when the user submit the information is when happens this.
[HttpGet]
public ActionResult DoTest()
{
Worksheet w = new Worksheet(..);
return View(w);
}
[HttpPost]
public ActionResult DoTest(Worksheet worksheet)
{
return PartialView("_Problems", worksheet);
}
This is class which I'm using.
public class Worksheet
{
public Worksheet() { }
public Worksheet(string title, List<Problem> problems)
{
this.Title = title;
this.Problems = problems;
}
public Worksheet(IEnumerable<Problem> problems, WorksheetMetadata metadata, ProblemRepositoryHistory history)
{
this.Metadata = metadata;
this.Problems = problems.ToList();
this.History = history;
}
public string Title { get; set; }
public List<Problem> Problems { get; set; } // Problem is an abstract class
public WorksheetMetadata Metadata { get; set; }
public ProblemRepositoryHistory History { get; set; }
}
And my razor view.... the razor view shows successfully my view. I realized something rare, please note in my 5 and 6 lines that I have HiddenFor method, well if I used that, when calls HTTPPOST persists the data, I don't know why.
#model Contoso.ExercisesLibrary.Core.Worksheet
<div id="problemList">
<h2>#Html.DisplayFor(model => model.Metadata.ExerciseName)</h2>
#Html.HiddenFor(model => model.Metadata.ExerciseName)
#Html.HiddenFor(model => model.Metadata.ObjectiveFullName)
#for (int i = 0; i < Model.Problems.Count; i++)
{
<div>
#Html.Partial(Contoso.ExercisesLibrary.ExerciseMap.GetProblemView(Model.Problems[i]), Model.Problems[i])
</div>
}
</div>
UPDATE
I'm using a static class to get the view name, but as I'm testing I'm just using this Partial view
#model Contoso.ExercisesLibrary.AbsoluteArithmetic.Problem1
<div>
<span style="padding:3px; font-size:18px;">#Model.Number1</span>
<span style="padding:5px; font-size:18px;">+</span>
<span style="padding:5px; font-size:18px;">#Model.Number2</span>
<span style="padding:5px; font-size:18px;">=</span>
<span style="font-size:18px">
#Html.EditorFor(model => model.Result, new { style = "width:60px; font-size:18px;" })
#Html.ValidationMessageFor(model => model.Result)
</span>
</div>
#section Scripts {
}
And here the user do the post
#model Contoso.ExercisesLibrary.Core.Worksheet
<form method="post">
#Html.Partial("_Problems", Model)
<input type="submit" value="Continue" />
</form>
The Model Binder will 'bind' or link input fields on your view to the model. It will not bind display fields (like label), that is why you need the HiddenFor it will add an <input type="hidden" which will then be bound to the Model when you Post.
You can use 'TempData'. It is used to pass data from current request to subsequent request means incase of redirection.
This link also helps you.
TempData
SO Tempdata
Make sure your form tag looks like the following, for instance the controller name, action method, the form method and an id for the form. I am referring to the #using statement. In my case the controller name is RunLogEntry, the action method is Create and the id is form.
Normal Post from View to Controller
#using (Html.BeginForm("Create", "RunLogEntry", FormMethod.Post, new { id = "form", enctype = "multipart/form-data" }))
{
<div id="main">
#Html.Partial("_RunLogEntryPartialView", Model)
</div>
}
If you want to post via Jquery, could do the following:
$.post("/RunLogEntry/LogFileConfirmation",
$("#form").serialize(),
function (data) {
//this is the success event
//do anything here you like
}, "html");
You must specify a form with correct attribute in your view to perform post action
<form action="Test/DoTest" method="post">
...
</form>
or
#using(Html.BeginForm("DoTest", "Test", FormMethod.Post)) {
...
}
The second is recommended.
Put your entire HTML code under:
#using(Html.BeginForm())
tag.

Resources