I cant successfully post the values from my partial view to my action - all the properties are null.
Partial View Model:
public class AddressViewModel
{
public string ClientNumber { get; set; }
public string Line1 { get; set; }
public string Line2 { get; set; }
public string Line3 { get; set; }
public string Suburb { get; set; }
public string PostalCode { get; set; }
}
Partial View:
#model Models.AddressViewModel
#{
Layout = null;
}
#using (Html.BeginForm("UseAddress", "Home"))
{
<div>
<table>
<tr>
<td>
<div class="display-label">
#Html.DisplayNameFor(model => model.Line1)
</div>
</td>
<td>
<div class="display-field">
#Html.DisplayFor(model => model.Line1)
</div>
</td>
........
</tr>
</table>
<input type="submit" name="UseAddress" id="submitbutton" value="Use Address" />
</div>
}
Action:
[HttpPost]
[Authorize]
public ActionResult UseAddress(AddressViewModel model)
{
return RedirectToAction("Index", "Home");
}
The partial view is rendered on the page by selecting a dropdown as follows:
<script type="text/javascript">
$(function () {
$('#AddressTypeDropdownList').change(function () {
var url = $(this).data('url');
$('#Address').load(url);
});
});
</script>
#Html.DropDownListFor(
x => x.SelectedAddressTypeId,
new SelectList(Model.AddressTypes, "Value", "Text"),
"-- Select Address Type --",
new
{
id = "AddressTypeDropdownList",
data_url = Url.Action("_Address", "Home")
}
)
<div id="Address"></div>
public ActionResult _Address()
{
AddressViewModel addressViewModel = new AddressViewModel {
ClientNumber = "test"
};
return PartialView(addressViewModel);
}
I would expect the UseAddress method to have the model.ClientNumber == "test" when I click the submit button but it is null...Is there anything obvious that I'm doing wrong?
DisplayFor doesn't create input for the field so it won't get posted. You'll need to add
#Html.HiddenFor(model => model.Line1)
....
To post the values.
Related
I am trying to generate a list of checkboxes and post the selected item to post method of my controller but my view model is null.
Please see below code and help.
The view get invoke with the following button on another page -
<button class="btn btn-primary" id="historicalrecords"
onclick="location.href='#Url.Action("HistoricalWorkSubmissions", "Main", new {id= #Model.MessageIdsCombined.FirstOrDefault()})'">View Historical Works</button>
Model WorkSubmissions.cs:
public class HistoricalWorkSubmission
{
public string Society { get; set; }
public string Rsa { get; set; }
public DateTime ProcessingTime { get; set; }
public bool isSelected { get; set; }
}
public class HistoricalWorkSubmisssionViewModel
{
public List<HistoricalWorkSubmission> Submissions { get; set; }
}
Get method in MainController:
[HttpGet]
public async Task<ActionResult> HistoricalWorkSubmissions(string id)
{
WorkSearchViewModel workSearchViewModel = new WorkSearchViewModel
{
MessageId = id
};
var workSubmissions = await _swvManager.SearchAllWorkSubmissionsAsync(workSearchViewModel).ConfigureAwait(true);
return View("~/Views/Main/HistoricalWorkSubmissions.cshtml", workSubmissions);
}
HistoricalWorkSubmissions.cshtml:
#model SWV.WorkPicture.UI.Models.HistoricalWorkSubmisssionViewModel
#{
ViewBag.Title = "HistoricalSubmissions";
}
<h2>HistoricalSubmissions</h2>
#using (Html.BeginForm("HistoricalWorkSubmissions", "Main", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
#Html.AntiForgeryToken();
<fieldset>
<div>
<table class="table-bordered">
#*#foreach (var submission in Model.Submissions)*#
#for (int i=0; i < Model.Submissions.Count(); i++)
{
var bmiWorks = Model.Submissions[i].Society + Model.Submissions[i].Rsa + " " + Model.Submissions[i].ProcessingTime;
<tr>
<td>
#Html.CheckBoxFor(m => Model.Submissions[i].isSelected)
#Html.Label(bmiWorks)
#Html.HiddenFor(m => Model.Submissions[i])
</td>
</tr>
}
</table>
<input class="button btn-primary" type="submit" value="Save"/>
</div>
</fieldset>
}
And finally post method:
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> HistoricalWorkSubmissions(HistoricalWorkSubmisssionViewModel workSubmissions)
{
WorkSearchViewModel workSearchViewModel = new WorkSearchViewModel();
workSearchViewModel.SwvId = "5124cfb4-afe8-4783-ab97-b9fbaaf6737d";
var workPicturesx = await _swvManager.SearchAllWorkSubmissionsAsync(workSearchViewModel).ConfigureAwait(true);
return View("~/Views/Main/HistoricalWorks.cshtml");
}
POST-
The value of abc is null in debugger. Please help.
Make sure the field names in the post match the model. The Label is not an issue, but CheckBoxFor and HiddenFor would generate Html input tags in the form posted to server.
#Html.CheckBoxFor(m => Model.Submissions[i].isSelected)
#Html.Label(bmiWorks)
#Html.HiddenFor(m => Model.Submissions[i].Society)
#Html.HiddenFor(m => Model.Submissions[i].Rsa)
#Html.HiddenFor(m => Model.Submissions[i].ProcessingTime)
Also I've removed the hidden field for submission because it's an object in your model, while we need to generate tags for each property instead.
I have the following models:
public class ColourCounts
{
[Key]
public int ColourGroup { get; set; }
public int ColourCount { get; set; }
public bool Selected { get; set; }
}
public class SizeCounts
{
[Key]
public int SizeGroup { get; set; }
public int SizeCount { get; set; }
public bool Selected { get; set; }
}
public class New
{
public New()
{
this.Newz = new New();
this.Colour = new ColourCounts();
this.ColourList = new List<ColourCounts>();
this.Size = new SizeCounts();
this.SizeList = new List<SizeCounts>();
}
public New Newz { get; set; }
public ColourCounts Colour { get; set; }
public List<ColourCounts> ColourList { get; set; }
public SizeCounts Size { get; set; }
public List<SizeCounts> SizeList { get; set; }
}
In my Index view I have
#model ...Models.New
...
<div id="colourTable">
#Html.Partial("_ColourCounts")
<div class="spinner">
</div>
<div>Loading...</div>
</div>
<div id="sizeTable">
#Html.Partial("_SizeCounts")
<div class="spinner">
</div>
<div>Loading...</div>
</div>
The partial view for
_ColourCounts
&
_SizeCounts
are
#model ...Models.New
<tbody>
<tr>
#Html.EditorFor(model => model.ColourList)
</tr>
</tbody>
&
#model ...Models.New
<tbody>
<tr>
#Html.EditorFor(model => model.SizeList)
</tr>
</tbody>
The editor templates are
#model ..Models.SizeCounts
<tr>
<td>
#Html.HiddenFor(m => m.SizeGroup)
#Html.DisplayFor(m => m.SizeGroup)
</td>
<td>
#Html.HiddenFor(m => m.SizeValue)
#Html.DisplayFor(m => m.SizeValue)
</td>
<td">
#Html.HiddenFor(m => m.SizeCount)
#Html.DisplayFor(m => m.SizeCount)
</td>
<td>#Html.CheckBoxFor(m => m.Selected)</td>
</tr>
&
#model ..Models.ColourCounts
<tr>
<td>
#Html.HiddenFor(m => m.ColourGroup)
#Html.DisplayFor(m => m.ColourGroup)
</td>
<td>
#Html.HiddenFor(m => m.ColourValue)
#Html.DisplayFor(m => m.ColourValue)
</td>
<td">
#Html.HiddenFor(m => m.ColourCount)
#Html.DisplayFor(m => m.ColourCount)
</td>
<td>#Html.CheckBoxFor(m => m.Selected)</td>
</tr>
I asynchronously load these by which is on the index view
<script type="text/javascript" name ="Size">
$(document).ready(function () {
//var val = $('#yearSelect3').val();
$.ajax({
url: "/News/_SizeCounts",
type: "GET",
})
.done(function (partialViewResult) {
$("#sizeTable").html(partialViewResult)
});
});
</script>
<script type="text/javascript" name="Colour">
$(document).ready(function () {
//var val = $('#yearSelect3').val();
$.ajax({
url: "/News/_ColourCounts",
type: "GET",
})
.done(function (partialViewResult) {
$("#colourTable").html(partialViewResult)
});
});
</script>
Whenever a user Checks any of the checkboxes, I want to capture ALL of the values (thee Newz can be ingored as that won't be re-rendered), the checkboxes and the hidden for values and post back to a controller
public ActionResult ReCalc(New model)
{
....
}
That then will get some new counts using a Linq query and I'll re-render the EditorTemplates in the Partial views as on the page load but with the new data.
I can't capture the form data in a way that I can pass it to the model in the ReCalc controller though.
I have tried in jquery using
var x = $('#myForm').serializeArray();
var y = JSON.stringify(x);
while the jason is "valid" according to http://jsonlint.com/ I get
{"name":"SizeList[0].Selected","value":"true"},{"name":"SizeList[0].Selected","value":"false"}
So clearly a problem there, and also the model is null in the controller when looking at in in debug.
Really appreciate any help to get the jquery right for this.
If you need any further information please let me know in layman's terms, I'm a T-SQL guy :) Thanks!
My GET method of controller construct and fill Model, which including Dictionary<int, MyClass>, and transmit that to View. But after, POST controller method get not null model with empty Dictionary.
Model:
public class CheckBoxItem
{
public string Name { get; set; }
public double Data { get; set; }
public bool Selected { get; set; }
}
public class CreateNewEventModel
{
[Required(ErrorMessage = "Error text")]
[Display(Name = "Header name")]
public string EventName { get; set; }
public Dictionary<int, CheckBoxItem> CheckBoxDataItems { get; set; }
public CreateNewEventModel()
{
CheckBoxDataItems = new Dictionary<int, CheckBoxItem>();
}
}
Controller:
public ActionResult CreateEvent()
{
CreateNewEventModel model = new CreateNewEventModel();
// FILL MODEL
foreach (var user in db.UsersInfo.ToList())
{
model.CheckBoxDataItems.Add(user.Id, new CheckBoxItem()
{
Name = user.Name,
Data = 0,
Selected = false
});
}
// THERE IS FULL MODEL
return View(model);
}
[HttpPost]
public ActionResult CreateEvent(CreateNewEventModel model)
{
// THERE IS model.Event name include text
// BUT model.CheckBoxDataItems is empty
if (ModelState.IsValid)
{
...
return View(model);
}
return View(model);
}
View:
#model HLyaa.Models.CreateNewEventModel
#{
ViewBag.Title = "Create Event";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>Создание события</h2>
#if (Model.CheckBoxDataItems.Count() != 0)
{
using (Html.BeginForm("CreateEvent", "Events", FormMethod.Post))
{
#Html.ValidationSummary()
<div>
#Html.LabelFor(model => model.EventName)
<div>
#Html.EditorFor(model => model.EventName)
</div>
</div>
<table>
#foreach (var kvpair in Model.CheckBoxDataItems)
{
<tr>
<td>
#Html.CheckBoxFor(model => model.CheckBoxDataItems[kvpair.Key].Selected)
</td>
<td>
#Html.DisplayFor(model => model.CheckBoxDataItems[kvpair.Key].Name)
#Html.HiddenFor(model => model.CheckBoxDataItems[kvpair.Key].Selected)
#Html.HiddenFor(model => model.CheckBoxDataItems[kvpair.Key].Name)
</td>
<td>
#Html.TextBoxFor(model => model.CheckBoxDataItems[kvpair.Key].Data, new { #type = "number" })
</td>
</tr>
}
</table>
<br />
<input type="submit" value="Next" />
}
}
How I can transmit data inside dictionary from View to Controller?
Dictionary no, List/Array yes, but you will have to make some modifications.
Modify models
public class CheckBoxItem {
public int UserId { get; set; }
public string Name { get; set; }
public double Data { get; set; }
public bool Selected { get; set; }
}
public class CreateNewEventModel {
[Required(ErrorMessage = "Error text")]
[Display(Name = "Header name")]
public string EventName { get; set; }
public List<CheckBoxItem> CheckBoxDataItems { get; set; }
public CreateNewEventModel() {
CheckBoxDataItems = new List<CheckBoxItem>();
}
}
Modify GET method CreateEvent
public ActionResult CreateEvent() {
var model = new CreateNewEventModel();
//...FILL MODEL
foreach (var user in db.UsersInfo.ToList()) {
model.CheckBoxDataItems.Add(new CheckBoxItem() {
UserId = user.Id,
Name = user.Name,
Data = 0,
Selected = false
});
}
// THERE IS FULL MODEL
return View(model);
}
Update View
<table>
#for (var i = 0; i < Model.CheckBoxDataItems.Count; i++) {
<tr>
<td>
#Html.CheckBoxFor(model => model.CheckBoxDataItems[i].Selected)
</td>
<td>
#Html.DisplayFor(model => model.CheckBoxDataItems[i].Name)
#Html.HiddenFor(model => model.CheckBoxDataItems[i].UserId)
#Html.HiddenFor(model => model.CheckBoxDataItems[i].Selected)
#Html.HiddenFor(model => model.CheckBoxDataItems[i].Name)
</td>
<td>
#Html.TextBoxFor(model => model.CheckBoxDataItems[i].Data, new { #type = "number" })
</td>
</tr>
}
</table>
CheckBoxDataItems should be populated now when you post it to controller
As I answered in this post, you need to call "ToArray()[*]" on your dictionary before accessing its key and value so you can specify an index that's used by the ASP model binder to send your data back to the controller. ;)
When I build my form, my list populates correctly. However when I post back to my controller, the list SynonymTerm is null.
Here is my view model:
public class SynonymEditViewModel
{
public string Term { get; set; }
public List<SynonymTermEditViewModel> SynonymTerm;
public SynonymEditViewModel()
{
SynonymTerm = new List<SynonymTermEditViewModel>();
}
}
public class SynonymTermEditViewModel
{
public string Term { get; set; }
public string ReplacementTerm { get; set; }
public SynonymDuplicateWarning Warning { get; set; }
public SynonymTermEditViewModel()
{
Warning = new SynonymDuplicateWarning();
}
}
public class SynonymDuplicateWarning
{
public List<string> Terms { get; set; }
public SynonymDuplicateWarning()
{
Terms = new List<string>();
}
}
A simplified version of the view:
Edit.cshtml
#model MyProject.ViewModels.Synonyms.SynonymEditViewModel
<div class="form-group">
<div class="col-md-2">
#Html.LabelFor(model => model.Term)
</div>
<div class="col-md-10">
#Html.HiddenFor(model => model.Term)
#Html.DisplayFor(model => model.Term)
</div>
</div>
<table id="terms-table">
<tbody>
#Html.EditorFor(model => model.SynonymTerm)
</tbody>
</table>
EditorTemplates/SynonymTermEditViewModel.cshtml
#model MyProject.ViewModels.Synonyms.SynonymTermEditViewModel
<tr>
<td>
#Html.TextBoxFor(model => model.Term)
#Html.ValidationMessageFor(model => model.Term)
#Html.EditorFor(model => model.Warning)
</td>
</tr>
Here is the form data pulled from the browser request.
Term:Cat
SynonymTerm[0].Term:Feline
This data is passed to this controller:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(SynonymEditViewModel synonym)
{
if (ModelState.IsValid)
{
//breakpoint here
var dbSynonym = db.Synonym_Replacement_Term.Find(synonym.Term);
Mapper.Map(synonym, dbSynonym);
db.SaveChanges(User.Identity.Name, Request.ServerVariables["REMOTE_ADDR"]);
return RedirectToAction("Index");
}
return View(synonym);
}
The expected behavior is SynonymTerm is a list containing 1 object with Term="Feline", and all other properties having default/constructed values. Is there any reason SynonymTerm would be null?
I found the answer. In my ViewModel, I needed to add a getter/setter for the list:
public List<SynonymTermEditViewModel> SynonymTerm { get; set; }
Apparently they're required for the reflection used by MVC, according to this similar question.
I have some problem posting a form with 'complex type' model:
I have a Model:
public class CircleEditViewModel
{
[Key]
public int CircleId { get; set; }
[Required]
[MaxLength(100)]
public string Name { get; set; }
public bool IsSystem { get; set; }
public class UserInCircle
{
public UserInCircle(User user)
{
this.UserId = user.UserId;
FullName = user.FullName;
}
public int UserId { get; set; }
public byte[] Picture { get; set; }
public string FullName { get; set; }
public bool isInCircle { get; set; }
}
public List<UserInCircle> Users { get; set; }
}
My first problem was that at post event, my Users where null.. so i followed a few posts on here (like MVC- Model Binding on a Complex Type) to use a for instead of a foreach,but since i did so, my form won't post anymore:
View:
#model Wims.Website.ViewModels.CircleEditViewModel
<script type="text/javascript">
$(document).ready(function () {
$.validator.unobtrusive.parse('form');
});
</script>
#using (Ajax.BeginForm(Html.ViewContext.RouteData.Values["Action"].ToString(), null, new AjaxOptions { HttpMethod = "POST", OnSuccess = "SaveDone(data)" }, new { id = "editform" }))
{
#Html.ValidationSummary(true)
<fieldset>
<legend>Circle</legend>
#Html.Label(DateTime.Now.ToString());
<div class="editor-label">
#Html.LabelFor(model => model.Name)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Name)
#Html.ValidationMessageFor(model => model.Name)
</div>
</fieldset>
if (Model.Users != null)
{
for (int i = 0; i < Model.Users.Count; i++)
{
<div class="userDetail">
<div>
<div>
#Html.DisplayFor(model => Model.Users[i].isInCircle);
</div>
<div class="iconDiv">
#Html.Image("~/Content/Images/defaultUser.jpg", Model.Users[i].FullName, null);
</div>
<div>
#Html.TextBoxFor(model => Model.Users[i].FullName)
#Html.HiddenFor(model => Model.Users[i].UserId)
</div>
</div>
</div>
<div style="clear: both"></div>
}
}
#Html.GenerateSecureDataControls(model => model.CircleId)
<input type="submit" value="Save" />
}
My view is rendered as a partial loaded thru ajax (not sure it makes any difference here).
Any idea why it won't post? If i remove all the '[]' like 'Users[0].FullName' to Users0.FullName i will post, but of course it won't be bound.
Thanks for your help
Edit just in case needed: Action:
[HttpPost]
public ActionResult Edit(CircleEditViewModel circleData, FormCollection collection)
{
if (ModelState.IsValid)
{
using (var logic = new CircleLogic())
{
Circle circle = logic.GetCircleById(circleData.CircleId, WebMatrix.WebData.WebSecurity.CurrentUserId);
if (circle == null)
{
return HttpNotFound();
}
else
{
circle.Name = circleData.Name;
logic.UpdateCircle(circle, GetSelectedUser(collection));
}
return PartialView("_CircleAndUsers", GetData(logic, circle.CircleId));
}
}
return this.Json(new { success = false, viewdata = RenderRazorViewToString("_CircleAndUsers", circleData) });
}
Pablo Romeo was right, i added a default ctor and it worked.