assigning button value value provided by model in asp.net MVC - asp.net-mvc

I am trying to create a button for each item that is returned from my model and assign the value of the button to the column "collegeOf". My buttons display the first part of it "College" but it should be something like "College of liberal arts and sciences". I can't figure out why the value is getting truncated after the first word. Below is my code for my view:
#model IEnumerable<KU_PLAN_DEV.Models.TRACK_INFO>
#{
ViewBag.Title = "All Degree Tracks";
}
<h2>Kutztown University Degree Tracks</h2>
<div class="jumbotronAll">
#foreach (var item in Model)
{
<br />
Html.BeginForm("Index", "AllTracks");
{
<input type="submit" value=#Html.DisplayFor(modelItem => item.collegeOf) class="AllTracksButtons" />
}
}
</div>

Related

ASP.NET MVC 5 always display the first row in the table [duplicate]

this is a tricky one to explain, so I'll try bullet pointing.
Issue:
Dynamic rows (collection) available to user on View (add/delete)
User deletes row and saves (POST)
Collection passed back to controller with non-sequential indices
Stepping through code, everything looks fine, collection items, indices etc.
Once the page is rendered, items are not displaying correctly - They are all out by 1 and therefore duplicating the top item at the new 0 location.
What I've found:
This happens ONLY when using the HTML Helpers in Razor code.
If I use the traditional <input> elements (not ideal), it works fine.
Question:
Has anyone ever run into this issue before? Or does anyone know why this is happening, or what I'm doing wrong?
Please check out my code below and thanks for checking my question!
Controller:
[HttpGet]
public ActionResult Index()
{
List<Car> cars = new List<Car>
{
new Car { ID = 1, Make = "BMW 1", Model = "325" },
new Car { ID = 2, Make = "Land Rover 2", Model = "Range Rover" },
new Car { ID = 3, Make = "Audi 3", Model = "A3" },
new Car { ID = 4, Make = "Honda 4", Model = "Civic" }
};
CarModel model = new CarModel();
model.Cars = cars;
return View(model);
}
[HttpPost]
public ActionResult Index(CarModel model)
{
// This is for debugging purposes only
List<Car> savedCars = model.Cars;
return View(model);
}
Index.cshtml:
As you can see, I have "Make" and "Actual Make" inputs. One being a HTML Helper and the other a traditional HTML Input, respectively.
#using (Html.BeginForm())
{
<div class="col-md-4">
#for (int i = 0; i < Model.Cars.Count; i++)
{
<div id="car-row-#i" class="form-group row">
<br />
<hr />
<label class="control-label">Make (#i)</label>
#Html.TextBoxFor(m => m.Cars[i].Make, new { #id = "car-make-" + i, #class = "form-control" })
<label class="control-label">Actual Make</label>
<input class="form-control" id="car-make-#i" name="Cars[#i].Make" type="text" value="#Model.Cars[i].Make" />
<div>
<input type="hidden" name="Cars.Index" value="#i" />
</div>
<br />
<button id="delete-btn-#i" type="button" class="btn btn-sm btn-danger" onclick="DeleteCarRow(#i)">Delete Entry</button>
</div>
}
<div class="form-group">
<input type="submit" class="btn btn-sm btn-success" value="Submit" />
</div>
</div>
}
Javascript Delete Function
function DeleteCarRow(id) {
$("#car-row-" + id).remove();
}
What's happening in the UI:
Step 1 (delete row)
Step 2 (Submit form)
Step 3 (results)
The reason for this behavior is that the HtmlHelper methods use the value from ModelState (if one exists) to set the value attribute rather that the actual model value. The reason for this behavior is explained in the answer to TextBoxFor displaying initial value, not the value updated from code.
In your case, when you submit, the following values are added to ModelState
Cars[1].Make: Land Rover 2
Cars[2].Make: Audi 3
Cars[3].Make: Honda 4
Note that there is no value for Cars[0].Make because you deleted the first item in the view.
When you return the view, the collection now contains
Cars[0].Make: Land Rover 2
Cars[1].Make: Audi 3
Cars[2].Make: Honda 4
So in the first iteration of the loop, the TextBoxFor() method checks ModelState for a match, does not find one, and generates value="Land Rover 2" (i.e. the model value) and your manual input also reads the model value and sets value="Land Rover 2"
In the second iteration, the TextBoxFor() does find a match for Cars[1]Make in ModelState so it sets value="Land Rover 2" and manual inputs reads the model value and sets value="Audi 3".
I'm assuming this question is just to explain the behavior (in reality, you would save the data and then redirect to the GET method to display the new list), but you can generate the correct output when you return the view by calling ModelState.Clear() which will clear all ModelState values so that the TextBoxFor() generates the value attribute based on the model value.
Side note:You view contains a lot of bad practice, including polluting your markup with behavior (use Unobtrusive JavaScript), creating label element that do not behave as labels (clicking on them will not set focus to the associated control), unnecessary use of <br/> elements (use css to style your elements with margins etc) and unnecessary use of new { #id = "car-make-" + i }. The code in your loop can be
#for (int i = 0; i < Model.Cars.Count; i++)
{
<div class="form-group row">
<hr />
#Html.LabelFor(m => m.Cars[i].Make, "Make (#i)")
#Html.TextBoxFor(m => m.Cars[i].Make, new { #class = "form-control" })
....
<input type="hidden" name="Cars.Index" value="#i" />
<button type="button" class="btn btn-sm btn-danger delete">Delete Entry</button>
</div>
}
$('.delete').click(function() {
$(this).closest('.form-group').remove();
}

TextBoxFor displaying the wrong name? [duplicate]

this is a tricky one to explain, so I'll try bullet pointing.
Issue:
Dynamic rows (collection) available to user on View (add/delete)
User deletes row and saves (POST)
Collection passed back to controller with non-sequential indices
Stepping through code, everything looks fine, collection items, indices etc.
Once the page is rendered, items are not displaying correctly - They are all out by 1 and therefore duplicating the top item at the new 0 location.
What I've found:
This happens ONLY when using the HTML Helpers in Razor code.
If I use the traditional <input> elements (not ideal), it works fine.
Question:
Has anyone ever run into this issue before? Or does anyone know why this is happening, or what I'm doing wrong?
Please check out my code below and thanks for checking my question!
Controller:
[HttpGet]
public ActionResult Index()
{
List<Car> cars = new List<Car>
{
new Car { ID = 1, Make = "BMW 1", Model = "325" },
new Car { ID = 2, Make = "Land Rover 2", Model = "Range Rover" },
new Car { ID = 3, Make = "Audi 3", Model = "A3" },
new Car { ID = 4, Make = "Honda 4", Model = "Civic" }
};
CarModel model = new CarModel();
model.Cars = cars;
return View(model);
}
[HttpPost]
public ActionResult Index(CarModel model)
{
// This is for debugging purposes only
List<Car> savedCars = model.Cars;
return View(model);
}
Index.cshtml:
As you can see, I have "Make" and "Actual Make" inputs. One being a HTML Helper and the other a traditional HTML Input, respectively.
#using (Html.BeginForm())
{
<div class="col-md-4">
#for (int i = 0; i < Model.Cars.Count; i++)
{
<div id="car-row-#i" class="form-group row">
<br />
<hr />
<label class="control-label">Make (#i)</label>
#Html.TextBoxFor(m => m.Cars[i].Make, new { #id = "car-make-" + i, #class = "form-control" })
<label class="control-label">Actual Make</label>
<input class="form-control" id="car-make-#i" name="Cars[#i].Make" type="text" value="#Model.Cars[i].Make" />
<div>
<input type="hidden" name="Cars.Index" value="#i" />
</div>
<br />
<button id="delete-btn-#i" type="button" class="btn btn-sm btn-danger" onclick="DeleteCarRow(#i)">Delete Entry</button>
</div>
}
<div class="form-group">
<input type="submit" class="btn btn-sm btn-success" value="Submit" />
</div>
</div>
}
Javascript Delete Function
function DeleteCarRow(id) {
$("#car-row-" + id).remove();
}
What's happening in the UI:
Step 1 (delete row)
Step 2 (Submit form)
Step 3 (results)
The reason for this behavior is that the HtmlHelper methods use the value from ModelState (if one exists) to set the value attribute rather that the actual model value. The reason for this behavior is explained in the answer to TextBoxFor displaying initial value, not the value updated from code.
In your case, when you submit, the following values are added to ModelState
Cars[1].Make: Land Rover 2
Cars[2].Make: Audi 3
Cars[3].Make: Honda 4
Note that there is no value for Cars[0].Make because you deleted the first item in the view.
When you return the view, the collection now contains
Cars[0].Make: Land Rover 2
Cars[1].Make: Audi 3
Cars[2].Make: Honda 4
So in the first iteration of the loop, the TextBoxFor() method checks ModelState for a match, does not find one, and generates value="Land Rover 2" (i.e. the model value) and your manual input also reads the model value and sets value="Land Rover 2"
In the second iteration, the TextBoxFor() does find a match for Cars[1]Make in ModelState so it sets value="Land Rover 2" and manual inputs reads the model value and sets value="Audi 3".
I'm assuming this question is just to explain the behavior (in reality, you would save the data and then redirect to the GET method to display the new list), but you can generate the correct output when you return the view by calling ModelState.Clear() which will clear all ModelState values so that the TextBoxFor() generates the value attribute based on the model value.
Side note:You view contains a lot of bad practice, including polluting your markup with behavior (use Unobtrusive JavaScript), creating label element that do not behave as labels (clicking on them will not set focus to the associated control), unnecessary use of <br/> elements (use css to style your elements with margins etc) and unnecessary use of new { #id = "car-make-" + i }. The code in your loop can be
#for (int i = 0; i < Model.Cars.Count; i++)
{
<div class="form-group row">
<hr />
#Html.LabelFor(m => m.Cars[i].Make, "Make (#i)")
#Html.TextBoxFor(m => m.Cars[i].Make, new { #class = "form-control" })
....
<input type="hidden" name="Cars.Index" value="#i" />
<button type="button" class="btn btn-sm btn-danger delete">Delete Entry</button>
</div>
}
$('.delete').click(function() {
$(this).closest('.form-group').remove();
}

MVC foreach set item.ID to model.ID

I have a form that shows all the available hotel rooms, each room has a button that does a HttpPost if clicked, I have made a property in the BookingViewModel called 'RoomID'. I would like to assign the item.RoomID to Model.RoomID so I can use it in my controller to get the id from the selected room but i'm not sure how to achieve this.
ChooseRoom View
#foreach (var item in Model.AvailableRooms)
{
<li class="room-item clearfix">
<h5>#item.Name</h5>
<div class="room-list-left">
<img src="#item.Image" alt="" />
</div>
<div class="room-list-right">
<div class="room-meta">
<ul>
<li><span>Occupancy:</span> #item.Adults Adults #item.Childs Children</li>
#if (item.SmokingRoom)
{
<li><span>Smoking Allowed:</span> Yes</li>
}
else
{
<li><span>Smoking Allowed:</span> No</li>
}
</ul>
</div>
<div class="room-price">
<p class="price">From: <span>$#item.Price</span> / Night</p>
</div>
<div class="clearboth"></div>
#using (Html.BeginForm("chooseroom", "booking", FormMethod.Post))
{
<input class="button2" type="submit" value="Select Room" />
}
BookingController
[HttpPost]
public ActionResult ChooseRoom(BookingViewModel vm)
{
BookingViewModel bookingObj = GetBooking();
bookingObj.SelectedRoom = Repository.GetRoomByID(vm.RoomID);
return View("reservation", bookingObj);
}
Thank you for your time!
update your begin form as below
#using (Html.BeginForm("chooseroom", "booking", FormMethod.Post))
{
<input type="hidden" name="RoomId" value="#item.RoomID" />
<input class="button2" type="submit" value="Select Room" />
}
Just need to provide input tags having the same name as your ViewModel property.
You could add inputs in foreach loop , it should be inside form. Something like this <input name="Model.AvailableRooms[index].RoomID" value="Id Here"/>
Or if you want to select one Room you should use ajax and post id.
If I'm not wrong you form is in loop,so you could add hidden input with id
#Html.HiddenFor(c => c.AvailableRooms[index].RoomID)

Server Side validation not working on MVC partial view

I have a partial view that's added to the main view via ajax call, triggered by the user clicking a button (the user clicks a button, and the partial view is added to the page).
The partial view is strongly typed to a different model than the main page. In my main page, I display the validation errors at the top, and then also have the message displayed below each field. The validation errors for my partial view are displaying at the top of the page, but they are not displaying with the field itself.
Field from partial view:
<td>
#Html.LabelFor(model => model.EmploymentCompany, "* Employer Name")
<input type="text" name="TempEmployments[#idx].EmploymentCompany" class="field panel-field" maxlength="40" style="width: 250px !important;" value="#EmploymentCompany"/>
#Html.ValidationMessage("EmploymentCompany")
</td>
Rendered as:
<td>
<label for="EmploymentCompany">* Employer Name</label>
<input type="text" name="TempEmployments[0].EmploymentCompany" class="field panel-field" maxlength="40" style="width: 250px !important;">
<span class="field-validation-valid" data-valmsg-for="EmploymentCompany" data-valmsg-replace="true"></span>
<td>
In the controller, where validation is called:
[HttpPost]
public ActionResult Create(Applicant application)
{
this.ModelState.AddModelErrors(application.Validate(update: false));
//if valid, other stuff is done. otherwise:
return View(application);
}
In the main view, where validation errors are displayed at the top:
if (!this.ViewData.ModelState.IsValid)
{
<div class="validation-summary-errors" data-valmsg-summary="true">
<ul>
#foreach (ModelState modelState in ViewData.ModelState.Values)
{
foreach (ModelError error in modelState.Errors)
{
<li>#Html.Raw(error.ErrorMessage)</li>
}
}
</ul>
</div>
}
And in the entity where validation errors are added:
foreach (var job in TempEmployments)
{
if (string.IsNullOrWhiteSpace(job.EmploymentCompany))
{
errorDictionary.Add("EmploymentCompany", "Company Name is required");
}
}
And here is what the errors look like at the top of the page:
And the partial view:

Form not submitting and no error is being produced with MVC

I have a form in my MVC application that in theory should submit data back to my database using a Repository class.
However, when I submit the form (http://localhost:1028/Admin/NewUser/), the URL changes to where the form should be submitting to, which is fine (http://localhost:1028/Admin/NewUser/Submit), but once it has been submitted, it should send the user to a confirmation page.
From what I can tell, I'm moving through all my pages correctly until it comes to the submit, where it displays the form again but under /Admin/NewUser/Submit and the data is not inserted into the database.
This is the ActionResult I'm using:
Public Function Submit() As ActionResult
Try
Dim user = New hdUser() With { _
.userLogon = Request.Form("UserLogin"), _
.userPass = Request.Form("UserPassword"), _
.userEmail = Request.Form("UserEmail"), _
.RealName = Request.Form("UserFullName"), _
.isLive = 1, _
.avatar = "noavatar.gif" _
}
userRepository.Add(user)
userRepository.Save()
Return Redirect("/Admin/NewUser/Confirm")
Catch ex As Exception
ModelState.AddModelError("Error", ex)
End Try
Return View()
End Function
I'm fairly new to MVC so I'm not entirely sure if the above is correct or not.
And in my data repository class UserRepository.vb, the two functions I'm using are:
Public Sub Add(ByVal user As hdUser) Implements IUserRepository.Add
db.hdUsers.InsertOnSubmit(user)
End Sub
and
Public Sub Save() Implements IUserRepository.Save
db.SubmitChanges()
End Sub
And the form I have created is:
<form action="/Admin/NewUser/Submit" method="post">
<table border="0" cellpadding="0" cellspacing="2">
<tr>
<td><strong>User's Full Name</strong> <br />
<%=Html.TextBox("UserFullName")%>
</td>
</tr>
<tr>
<td><strong>User Login</strong> <br />
<%=Html.TextBox("UserLogin")%>
</td>
</tr>
<tr>
<td><strong>Password</strong> <br />
<%=Html.Password("UserPassword")%>
</td>
</tr>
<tr>
<td><strong>Email Address</strong> <br />
<%=Html.TextBox("UserEmail")%>
</td>
</tr>
<tr>
<td align="right"><input type="submit" value="Create" /></td>
</tr>
</table>
</form>
The code doesn't produce any errors but also doesn't seem to be submitting to the database. So I'm not entirely sure where I've gone wrong.
It could be obvious to someone more experienced, but I really haven't a clue on this one.
Is this my code that's causing the issue or some other fault?
Thanks in advance for any help.
EDIT: Based on Zhaph - Ben Duguid comment, I have made the following edits:
AdminController.vb
<AcceptVerbs(HttpVerbs.Post)> _
Public Function NewUser(ByVal formValues As FormCollection) As ActionResult
Try
Dim user = New hdUser()
user.userLogon = Request.Form("UserLogin")
user.userPass = Request.Form("UserPassword")
user.userEmail = Request.Form("UserEmail")
user.RealName = Request.Form("UserFullName")
user.isLive = 1
user.avatar = "noavatar.gif"
UpdateModel(user)
userRepository.Add(user)
userRepository.Save()
Catch ex As Exception
ModelState.AddModelError("Error", ex)
End Try
Return View()
End Function
NewUser.aspx
<%Html.BeginForm()%>
<%=Html.ValidationMessage("Error")%>
<table border="0" cellpadding="0" cellspacing="2">
<tr>
<td><strong>User's Full Name</strong> <br />
<%=Html.TextBox("UserFullName")%>
<%=Html.ValidationMessage("Name", "*")%></td>
</tr>
<tr>
<td><strong>User Login</strong> <br />
<%=Html.TextBox("UserLogin")%>
<%=Html.ValidationMessage("Username", "*")%></td>
</tr>
<tr>
<td><strong>Password</strong> <br />
<%=Html.Password("UserPassword")%>
<%=Html.ValidationMessage("Password", "*")%></td>
</tr>
<tr>
<td><strong>Email Address</strong> <br />
<%=Html.TextBox("UserEmail")%>
<%=Html.ValidationMessage("Email", "*")%></td>
</tr>
<tr>
<td align="right"><input type="submit" value="Create" /></td>
</tr>
</table>
<% Html.EndForm() %>
Which now produces an error of The value '' is invalid. for me.
Does this mean that form values aren't being passed correctly to the controller?
EDIT: I've made those edits in response Zhaph - Ben Duguid's edit and I've changed the Form elements to the DB field names (for testing at least). And now, when the page is submitted Name, Login and Email are all filled, password is blank (which I'm assuming is expected behaviour as per password boxes) but I still receive the "The value '' is invalid" error.
Response.Write in your controller isn't going to do anything to the view.
You should be returning your model back to the edit page, with any errors in
ModelState.AddModelError();
There's a very good example of how you can implement a Repository pattern, and take advantage of the ASP.NET MVC model binding features, etc in the NerdDinner Chapter from the Professional ASP.NET MVC book.
An example controller I have (in c# I'm afraid) based on the Nerd Dinner samples:
//
// POST: /AdminAlbums/Create
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create(FormCollection collection)
{
var album = new Album();
// Method on System.Web.Mvc.Controller, that takes a form collection, and
// using reflection on the Model, assigns values to it from the form.
UpdateModel(album);
if (album.IsValid)
{
// These methods are the same as yours
m_PhotoRepository.Add(album);
m_PhotoRepository.Save();
// In this instance, I'm returning the user to a list view of Albums
// for editing, probably ought to send them to the page to start
// uploading photos.
return RedirectToAction("Index");
}
// Still here, so I'm going to set up some ViewData I need.
ViewData["Title"] = "Create a new album";
ViewData["Message"] = "Create Album";
// I'm picking up errors from the model here.
// RuleViolation is my own class, implemented in a partial on Album.
foreach (RuleViolation violation in album.GetRuleViolations())
{
ModelState.AddModelError(violation.PropertyName, violation.ErrorMessage);
}
return View(album);
}
So you can see I return the model back to the main view if there's an error, to populate the Validation summary.
The relevant part of the view is:
<%= Html.ValidationSummary("Edit was unsuccessful. Please correct the errors and try again.") %>
<% using (Html.BeginForm()) {%>
<fieldset>
<legend>Album details</legend>
<div class="form_row">
<label for="Caption" class="left_label">Album caption:</label>
<%= Html.TextBox("Caption", Model.Caption, new { #class = "textbox" })%>
<%= Html.ValidationMessage("Caption", "*") %>
<div class="cleaner"> </div>
</div>
<div class="form_row">
<label for="IsPublic" class="left_label">Is this album public:</label>
<%= Html.CheckBox("IsPublic", Model.IsPublic) %>
</div>
<div class="form_row">
<input type="submit" value="Save" />
</div>
</fieldset>
<% } %>
Edit in response to question edit
Sorry, I should have clarified:
A lot of this is based on using the Helper methods provided by the ASP.NET MVC framework - you'll notice that I'm using methods like Html.TextBox to generate my fields, with their name/id pulled from the model itself. This way, if I load the view with ModelErrors in the ModelState, the helper will add the relevant details to rendered HTML to include the following mark-up
<label for="Caption" class="left_label">Caption:</label>
<input class="input-validation-error textbox"
id="Caption" name="Caption" type="text" value="" />
<span class="field-validation-error">*</span>
The other option you could have would be to add a message to the ViewData collection, and if it has a value, display that on your view.
Edit in response to question edit
A couple of things to bear in mind:
1) The identifiers of the Form elements and the Validation controls should be the same:
<%= Html.TextBox("Caption", Model.Caption, new { #class = "textbox" })%>
<%= Html.ValidationMessage("Caption", "*") %>
(you have things like "UserEmail" and "Email")
2) You should be returning the hdUser to the view on error - so try something like this:
<AcceptVerbs(HttpVerbs.Post)> _
Public Function NewUser(ByVal formValues As FormCollection) As ActionResult
Dim user = New hdUser()
Try
UpdateModel(user)
user.isLive = 1
user.avatar = "noavatar.gif"
userRepository.Add(user)
userRepository.Save()
Catch ex As Exception
ModelState.AddModelError("Error", ex)
End Try
Return View(user)
End Function

Resources