Model values are null during [HttpPost] - asp.net

I'm having some problems with my code and was hoping someone could give me a hand. Here's the snippet I'm working with:
[Authorize]
public ActionResult EventResults(int id)
{
List<Event> CompetitionEvents = Event.getEventsByCompetitionId(id);
ViewBag.CompetitionEvents = CompetitionEvents;
List<Person> Competitors = Competition.getCompetitorsByCompetitionID(id);
ViewBag.Competitors = Competitors;
List<Results> Results = Competition.getCompetitorResultsPairings(CompetitionEvents, Competitors);
ViewBag.Results = Results;
ViewBag.OrganizerEmail = Competition.getCompetitionById(id).OrganizerEmail;
return View();
}
#model BINC.Models.Results
#using BINC.Models;
#{
var eventList = ViewBag.CompetitionEvents as List<Event>;
var competitorList = ViewBag.Competitors as List<Person>;
var resultList = ViewBag.Results as List<Results>;
}
<h2></h2>
<p>Results:</p>
#using (Html.BeginForm())
{
foreach (var evt in eventList)
{
<fieldset>
<legend>#evt.activity.Name</legend>
<p>Event Description: #evt.activity.Description</p>
#foreach (var competitor in competitorList)
{
foreach (var result in resultList)
{
if (result.EventID == evt.id && result.CompetitorEmail == competitor.Email)
{
<p>Competitor: #competitor.FirstName #competitor.LastName</p>
<p>Score: #result.Score</p>
if (ViewBag.OrganizerEmail.Equals(#User.Identity.Name))
{
#Html.LabelFor(model => model.Score, "New Score ");
#Html.TextBoxFor(model => model.Score, new { maxlength = 10, style = "width:125px" })
<input type="submit" name="submitButton" value="Update" />
}
}
}
}
</fieldset>
}
}
[HttpPost]
public ActionResult EventResults(Results res)
{
//stuff
}
My problem is nothing other than the score is set on my Results object.
For example, when I put the value '15' into the text box and click 'Update', I'm passing the Result model object to the httppost method, which has everything set to null other than the 'score' field that I just entered.
Am I over complicating this? Is there an easier way?
I tried adding
#Html.HiddenFor(model => model.EventID);
#Html.HiddenFor(model => model.CompetitorEmail);
but that didn't seem to help any.

You are having multiple Submit buttons and that could be the issue, also this is not considered as good practise
<input type="submit" name="submitButton" value="Update" />
keep just one submit button at the end of the form

Basically-- make sure you pass the model to view-- and use the Html Helpers (ie TextBoxFor() and HiddenFor)
I don't think it's an issue with the submit button-- but the one thing that would probably help is to actually pass the model to the view. You are using the ViewBag to pass your data. Pass the model to View and your Html Helpers should generate the correct form names in order for the model binding to work.

Related

Model is null in view on foreach

I have added a list to my view model but when I access it in a foreach loop in the view it throws:
NullReferenceException: Object reference not set to an instance of an object.
AspNetCore.Views_MyActivationCampaign_Campaign.ExecuteAsync() in Campaign.cshtml
+ foreach(var dp in Model.DpRestrictedList)
This is the list I have added:
public List<DpRestricted> DpRestrictedList { get; set; } = new List<DpRestricted>()
{
new DpRestricted(){DpId = 1, Name = "Post Restricted" },
new DpRestricted(){DpId = 2, Name = "Unrestricted" },
new DpRestricted(){DpId = 3, Name = "Customer Restricted" }
};
}
public class DpRestricted
{
public int DpId { get; set; }
public string Name { get; set; }
}
and I am trying to loop over it like this:
<div class="row">
<fieldset>
<legend>Delivery Methods</legend>
<div id="radio">
#*<input type="radio" id="new-method">
<label for="new-method">New Method</label>
<input type="radio" id="dm-101" checked="checked">
<label for="dm-101">DM_101</label>
<input type="radio" id="delivery-method-2">
<label for="delivery-method-2">Delivery Method 2</label>*#
#{
foreach(var dp in Model.DpRestrictedList)
{
#Html.RadioButtonFor(model => model.DeliveryPointRestrictionId, dp);
}
}
</div>
</fieldset>
</div>
Using statement and example:
#model WorkstreamX.Web.Core.ViewModels.ActivationCampaignViewModel
...
<div class="col-md-4">
<label for="headline">Campaign</label>
#Html.EditorFor(model => model.CampaignName, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.CampaignName)
</div>
The above is the using statement in the view and an example of how it is already being used elsewhere in it. When I check it is not just the list that is null but the Model in the loop statement is also null. Is it a case of me needing to new up the view model in the controller at this point? That is what I am about to try I just wanted to state this question and maybe find out why this is happening. Any advice here greatly appreciated.
[edit] How I fixed this issue:
I added an argument to my view:
before return View();
after return View(new ActivationCampaignViewModel());
I still don't quite understand the why of this as I appeared to have a model before. I am assuming that because I didn't call the constructor the list wasn't constructed and made it all fall over.
Your code should be like the below one.
Public ActionResult GetEmployee()
{
var employee = GetEmployee();
return View(employee);
}
#model IEnumerable<Appname.ViewModel.Employee>
#{
foreach(var data in Model) {}
}
How I fixed this issue:
I added an argument to my view:
before return View();
after return View(new ActivationCampaignViewModel());
I still don't quite understand the why of this as I appeared to have a model before. I am assuming that because I didn't call the constructor the list wasn't constructed and made it all fall over.

Filter a View with a dropdown list in ASP.NET MVC

I am trying to filter a list view using a dropdown as a filter.
My controller:
public async Task<ActionResult> Index(int? TradeExerciseNumber)
{
var TradeExerciseEntries = new TradeExerciseController().GetAll();
ViewBag.TradeExerciseEntries = new SelectList(TradeExerciseEntries, "TradeExerciseID", "TradeExerciseNumber");
if (TradeExerciseNumber != null)
{
return View(await db.TradesModels.Where(x => x.TradeExerciseId == TradeExerciseNumber).ToListAsync());
}
return View(await db.TradesModels.ToListAsync());
}
And my view:
#using (Html.BeginForm())
{
<p>
Find by Exercise Number: #Html.DropDownList("TradeExerciseEntries", -how do I pass value to TradeExerciseNumber in my controller to let it render pls- )
<input type="submit" value="Search" />
</p>
}
Now, how do I pass the dropdownlist value to TradeExerciseNumber in my controller to let it render please? Thank you very much.
Best regards
So this is what I did in my view and it worked:
#using (Html.BeginForm("Index", "Trades")){
<p>
Find by Exercise Number: #Html.DropDownList("TradeExerciseNumber", ViewBag.TradeExerciseEntries as SelectList, null, new { onchange = "submit();" })
</p>
}
I hope it helps. Thanks

how to get Partial Views in mvc 5 posted data?

Iam Asp.net Webform Developer and are trying to learn .net MVC 5.
I know how to make partial view with only static html tags in MVC 5.
But can i in a MVC partial View also have a form with textboxes and a submit buttion ?
If yes, so where do i write Postback function for this partial View for get its posted values ,in which controller ?
Yes, You can have text boxes and a submit button in partial view as well.
Lets say for example you have a product application, and your main page (View) is displaying all the product available.
so in order to show the product you need to pass a model
#model IList<xyzRetailer.ViewModels.ProductViewModel>
#{
ViewData["Title"] = "Home Page";
}
and you can read the value like:
<div class="col-md-3">
<h2>Product Categories</h2>
<ul>
#foreach (var item in Model.Select(a=>a.Category).Distinct())
{
<li>#item</li>
}
</ul>
</div>
Now to your question if you want to have a a partial view with different view model you can go with tuple or you can have a new object created just for partial view while calling it like below
#Html.Partial("_AddProduct", new yzRetailer.ViewModels.ProductViewModel())
and your partial view will be something like below:
#model xyzRetailer.ViewModels.ProductViewModel
#using (Html.BeginForm("Create", "Home", FormMethod.Post))
{
<div class="input-group">
#Html.TextBoxFor(model => model.Id, new { #class = "hidden" })
#Html.TextBoxFor(model => model.Category, new { #class = "form-control" })
</div><input type="submit" value="Save" class="btn btn-primary btn-block" />
}
And the controller code should go Home-> Create that you have mentioned in #Html.BeginForm.
public async Task<IActionResult> Create(ProductViewModel product)
{
if (!ModelState.IsValid)
{
return this.BadRequest(ModelState);
}
if (string.IsNullOrEmpty(product.Id))
{
var result = await _catelogueManager.AddAsync(product);
}
else
{
var result = await _catelogueManager.UpdateAsync(product.Id, product);
}
var products = await _catelogueManager.GetAllAsync();
return View("Index", products);
}

MVC 5 - two separate models on a page, how do I attach one to a form so I can submit it?

TL;DR: How do I handle form data that is being submitted with nonstandard names for the data?
The stats:
MVC 5
ASP.NET 4.5.2
I am bringing in two different models:
public async Task<ActionResult> Index() {
var prospectingId = new Guid(User.GetClaimValue("CWD-Prospect"));
var cycleId = new Guid(User.GetClaimValue("CWD-Cycle"));
var viewModel = new OnboardingViewModel();
viewModel.Prospecting = await db.Prospecting.FindAsync(prospectingId);
viewModel.Cycle = await db.Cycle.FindAsync(cycleId);
return View(viewModel);
}
One called Prospecting, the other called Cycle. The Prospecting one is working just fine, as nothing else on the page needs it except one small item.
The Cycle has a mess of separate forms on the page, each needing to be separately submittable, and editing just one small part of the Cycle table. My problem is, I don't know how to submit the correct data to the backend. I am also not entirely sure how to "catch" that data.
The bright spot is that apparently the front end is properly reflective of what is in the db. As in, if I manually change the db field to a true value, the checkbox ends up being selected on refresh.
My current form is such:
#using(Html.BeginForm("UpdatePDFResourceRequest", "Onboarding", FormMethod.Post, new { enctype = "multipart/form-data" })) {
#Html.AntiForgeryToken()
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<fieldset>
#Html.LabelFor(Model => Model.Cycle.PDFResourceLibrary, htmlAttributes: new { #class = "control-label" })
#Html.CheckBoxFor(Model => Model.Cycle.PDFResourceLibrary, new { #class = "form-control" })
#Html.ValidationMessageFor(Model => Model.Cycle.PdfResourceLibrary, "", new { #class = "text-danger" })
<label class="control-label"> </label><button type="submit" value="Save" title="Save" class="btn btn-primary glyphicon glyphicon-floppy-disk"></button>
</fieldset>
}
But the resulting HTML is such:
<input id="Cycle_PDFResourceLibrary" class="form-control" type="checkbox" value="true" name="Cycle.PDFResourceLibrary" data-val-required="'P D F Resource Library' must not be empty." data-val="true">
As you can see, the name= is Cycle.PDFResourceLibrary and I don't know how to catch this on the backend.
My model for that specific form is:
public class PDFResourceRequestViewModel {
[DisplayName("PDF Resource Library Request")]
public bool PDFResourceLibrary { get; set; }
[DisplayName("Date Requested")]
[DataType(DataType.Date)]
public DateTime PDFResourceLibraryDate { get; set; }
[DisplayName("Notes")]
public string PDFResourceLibraryNotes { get; set; }
}
(not the overall model for that table, though)
And the method used to handle the form submission is:
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> UpdatePDFResourceRequest(PDFResourceRequestViewModel model) {
var id = new Guid(User.GetClaimValue("CWD-Cycle"));
Cycle cycle = await db.Cycle.FindAsync(id);
if(cycle == null) {
return HttpNotFound();
}
try {
cycle.CycleId = id;
cycle.PDFResourceLibrary = model.PDFResourceLibrary;
cycle.PDFResourceLibraryDate = DateTime.Now;
cycle.PDFResourceLibraryNotes = model.PDFResourceLibraryNotes;
db.Cycle.Add(cycle);
await db.SaveChangesAsync();
return RedirectToAction("Index");
} catch { }
return View(model);
}
Now, I know that the method is wrong, for one I am editing just three values out of dozens in that table, so I need to be using something like this method. Problem is, the form is getting submitted with the name= of Cycle.PDFResourceLibrary and it is not being matched up on the back end.
Help?
You can use the [Bind(Prefix="Cycle")] attribute to 'strip' the prefix so that name="Cycle.PDFResourceLibrary" effectively becomes name="PDFResourceLibrary" and will bind to your PDFResourceRequestViewModel
public async Task<ActionResult> UpdatePDFResourceRequest([Bind(Prefix="Cycle")]PDFResourceRequestViewModel model)

Model mismatch error when posting form

I am working on a simple image upload site in which users will have the ability to post comments on the images uploaded to the site, whenever posting a comment I am given this error :
The model item passed into the dictionary is of type '<>f__AnonymousType1`1[System.Int32]', but this dictionary requires a model item of type 'SilkMeme.Models.Meme'.
I know it has something to do with the model being defined at the top of my view being different to the one I am sending the post request to but I'm not entirely sure how to fix it
View
#model SilkMeme.Models.Meme
....
#using (Html.BeginForm("Comment", "Memes", new { id = Model.SilkId }))
{
<label for="thought">Thoughts?</label>
<input type="text" name="thought"/>
<label for="rating">Rating?</label>
<input name="rating" type="range" min="0" max="10" step="1" />
<input type="submit" value="Post Thoughts" />
}
<div class="thoughts">
#foreach (var c in ViewBag.thoughts)
{
<p>- #c.ThoughtWords , #c.ThoughtRating / 10 meme</p>
}
</div>
Controller
public ActionResult Details(int? id)
{
var thoughts = from comment in db.Thoughts where comment.SilkId == id select comment;
ViewBag.thoughts = thoughts;
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
Meme meme = db.Memes.Find(id);
if (meme == null)
{
return HttpNotFound();
}
return View(meme);
}
[HttpPost]
public ActionResult Comment(int id)
{
int thoughtid = (from m in db.Thoughts select m).OrderByDescending(e => e.ThoughtId).FirstOrDefault().ThoughtId + 1;
if (Request["thought"].ToString() != "")
{
Thought thought = new Thought()
{
ThoughtId = thoughtid,
SilkId = id,
Meme = db.Memes.Find(id),
ThoughtWords = Request["thought"],
ThoughtRating = Int32.Parse(Request["rating"])
};
db.Thoughts.Add(thought);
}
return View("Details", new { id = id });
}
This line.
return View("Details", new { id = id });
It is basically passing an anonymous object with Id property to your view which is strongly typed to Meme type and expects an object of Meme class.
If you save your data successfully, Ideally,you should do a redirect to the GET action (following PRG pattern)
[HttpPost]
public ActionResult Comment(int id)
{
int thoughtid = (from m in db.Thoughts select m)
.OrderByDescending(e => e.ThoughtId).FirstOrDefault().ThoughtId + 1;
if (Request["thought"].ToString() != "")
{
Thought thought = new Thought()
{
ThoughtId = thoughtid,
SilkId = id,
Meme = db.Memes.Find(id),
ThoughtWords = Request["thought"],
ThoughtRating = Int32.Parse(Request["rating"])
};
db.Thoughts.Add(thought);
db.SaveChanges();
}
return RedirectToAction("Details", new { Id=id });
}
Also, I recommend using MVC Modelbinding to read the submitted form data. You will find a ton of examples on stackoverflow to do that. When using ModelBinding, you can return the posted view model back to the view (with an error message if needed) and the ValidationSummary /ValidationMessgeFor helper methods can show an error message to user as needed.

Resources