ASP.NET MVC Data Validation - Highlight Table Row vs. TextBoxes - asp.net-mvc

On an ASP.NET MVC View, I have a couple of checkboxes, one for email address and one for phone. I want to make sure that at least one is checked (both can be checked, so a radio button is not ideal) and if neither are, highlight the row with a red border just like a textbox is with the validation functionality...
I have other fields that are getting validated correctly and the CSS is changing when there is an issue on the textboxes and textareas accordingly. The code below displays the message informing the user they must specify a contact preference, but does not highlight the row as having an issue...
SCREEN SHOT
VIEW
<table width="100%">
<tr>
<td>
<label>
How would you like us to contact you?
</label>
</td>
</tr>
<tr id="pref_row">
<td>
<span class="bold-text">Email: </span>
<%=Html.CheckBox("EmailDesired")%>
<span class="bold-text">Phone: </span>
<%=Html.CheckBox("PhoneDesired")%>
</td>
</tr>
</table>
CONTROLLER
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Index(ContactUs contactus)
{
ContactUsService svc = new ContactUsService();
// Validation
if (!contactus.EmailDesired && !contactus.PhoneDesired)
ViewData.ModelState.AddModelError("pref_row", "Please specify a contact preference (phone and/or email)!");
if (ViewData.ModelState.IsValid)
{
MessageModel msg = svc.SendRequest(contactus);
return RedirectToAction("Index", msg);
}
else
{
return View();
}
}

When the HtmlHelper render itself it checks if there is any item in the ModelState dictionary that has the same key as the helper itself. if so the control will be rendered with the attribute class equal to "input-validation-error" which is defined in the css file.
So, the style will be applied only on the rendered input controls.
This is my solution:
<table width="100%">
<tr>
<td>
<label>
How would you like us to contact you?
</label>
</td>
</tr>
<tr class="<%=ViewData.ModelState["pref_row"]!= null ? "input-validation-error":"" %>">
<td>
<span class="bold-text">Email: </span>
<%=Html.CheckBox("EmailDesired")%>
<span class="bold-text">Phone: </span>
<%=Html.CheckBox("PhoneDesired")%>
</td>
</tr>
</table>

Related

MVC: How to insert items in a grid without refreshing the whole page

I have this view where I create a new company that gets added to the database.
The view is in 2 sections, leftside and rightside.
In the rightside I want to input the trades for that company.
A company can have 1 or more trade and this includes 1 primary trade, and 0 to many other trades.
When the user adds in the other trades, each trade will be added to a list underneath. I have NOT put in the code for this yet. For each trade in the list, he can have the option of removing it.
When he has entered all the details, he clicks submit and all the data is saved in the database.
Now I am thinking of putting in a partial view for the other trades, but I am wondering how I can do this, and every time a trade is selected from autocomplete, the data is posted to a controller method and the partial view is return.
But this will clear the data in the leftside section.
So how should I do this?
My view looks like
#model SCD.ViewModels.SubcontractorViewModel
#{
ViewBag.Title = "Create";
}
#using (Html.BeginForm())
{
#Html.ValidationSummary(true)
<fieldset>
<legend>Create Subcontractor</legend>
<section class="wrapper">
<section class="leftside">
<table class="formTable">
<tr>
<td class="leftCell">#Html.LabelFor(model => model.Subcontractor.CompanyName)</td>
<td class="rightCell">#Html.TextBoxFor(model => model.Subcontractor.CompanyName, new { #style = "width: 300px;" })</td>
</tr>
<tr>
<td class="leftCell">#Html.LabelFor(model => model.AddressViewModel.Address1)</td>
<td class="rightCell">#Html.TextBoxFor(model => model.AddressViewModel.Address1, new { #style = "width: 300px;" })</td>
</tr>
<tr>
<td colspan="2" style="text-align: center;" class="rightCell"><input type="submit" value="Save"/></td>
</tr>
</table>
<div style="float: left">#Html.ActionLink(" ", "List", null, new { #class = "buttonBackToList" })</div>
</section>
<section class="rightside">
<table class="formTable">
<tr>
<td class="leftCell">#Html.LabelFor(model => model.PrimaryTrade)</td>
<td class="rightCell"><input type="search" name="searchPrimaryTrade" id="searchPrimaryTrade" data-scd-autocomplete="#Url.Action("AutocompletePrimaryTrade", "DataService")" style = "width: 300px;"/>
<input type="button" id="ResetPrimaryTrade" value="Reset"/>
</td>
</tr>
<tr>
<td class="leftCell">#Html.LabelFor(model => model.OtherTrades)</td>
<td class="rightCell"><input type="search" name="searchOtherTrade" id="searchOtherTrade" data-scd-autocomplete="#Url.Action("AutocompleteOtherTrade", "DataService")" style = "width: 300px;"/>
<input type="button" id="ResetOtherTrade" value="Reset"/>
</td>
</tr>
</table>
</section>
</section>
</fieldset>
}
Ajax is your answer, Whenever you do not want to reload the page then using client side ajax to communicate with the server is the only option.
I would use jQuery or to add the rows via ajax which will insert them into your database and return the populated model again and return this as a PartialView(). Your ajax would then onSuccess: replace your tableID with the returned results.
So your jQuery would be something like:
$('.rightside').children('.formTable').replaceWith(theReturnedPartialFromAjaxCall);
If you are adding a dynamic row there are two options:
On adding a row you can call an ajax request which will also add a blank row to the database and then repopulate your model and return the Partial View. This will now have the model binding in place on the new blank row, deleting the blank row or any row can also be done by ajax as the row now has an ID. Make sure however you put the ID as a hidden field in when you loop through each row.
OR (not the preferred way but probably what you will need to do as you have to perform the save)
You can capture the form collection in the save, if there are multiple rows then store this in an array
public ActionResult Save(MyModel model, FormCollection frm) {
String[] name = frm["name"].Split(',');
}
I don't like this way as it is prone to error and the first method will allow you to bind MVC's model validation

Asp.Net Mvc Form does not post?

view
#model Osos12Mvc3.Models.TblSayaclar
<script src="#Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>
<div id="div_dialog_container" class="dialog_container">
#using (Html.BeginForm((string)ViewBag.FormAction, "Sayac"))
{
<div id="div_iu_form_container" class="ui_form_container">
<div>#Html.ValidationSummary(true, "Sayaç Kaydı Başarısız! Lütfen Bilgileri Kontrol Ediniz.")
</div>
<table>
<thead>
<tr>
<th colspan="2">
Genel Bilgiler
</th>
</tr>
</thead>
<tbody>
<tr>
<td>#Html.LabelFor(x => x.TblMusteriler.musteri_adi):
</td>
<td>#Html.DropDownList("sno", (SelectList)ViewBag.musteri_id, "--Müşteri Seçiniz--")
</td>
</tr>
</tbody>
<tfoot>
</tfoot>
</table>
<table class="table_ui_buttons">
<thead>
<tr>
<th colspan="2">
</th>
</tr>
</thead>
<tbody>
<tr>
<td>
<input type="submit" value="Kaydet" />
</td>
<td>
</td>
</tr>
</tbody>
</table>
</div>
}
</div>
controller
public ActionResult SayacEkle()
{
var musteriler = entity.TblMusteriler.Select(x => new { x.sno, x.musteri_adi });
ViewBag.musteri_id = new SelectList(musteriler.AsEnumerable(), "sno", "musteri_adi");
return ContextDependentView(new TblSayaclar());
}
Im using validationsummary in my view. When I clicked submit dropdownlist color change to red. (like required field). I dont understand that state. I debugged post event, It did not posted. Post action was not firing.
This is the Html Source:
<select data-val="true" data-val-number="The field sno must be a number." data-val-required="The sno field is required." id="sno" name="sno" class="input-validation-error"><option value="">--Müşteri Seçiniz--</option>
AliRıza Adıyahşi
What is the wrong?
Thanks
When I clicked submit dropdownlist color change to red. (
If your view model property is marked with the Required attribute then it is normal that it gets validated:
[Required]
public string Sno { get; set; }
There are 2 levels of validation which run in that order:
Client side validation (not enabled by default, implement with jquery ubnobtrusive => prevent the form from being submitted)
Server side validation
If you are using client side validation (unobtrusive javascript) and you have a model error then the form will not even be posted to the server. The user needs to first fix his errors before being able to submit the form.

MVC list of checkboxes check and select to Action then to csv file

I have a view like:
#model IEnumerable<VectorCheck.Models.Invoice>
#{
ViewBag.Title = "Exportable Invoices";
}
<script src="../../Scripts/jquery-1.7.1.min.js" type="text/javascript"></script>
<script src="../../Scripts/jquery-ui-1.8.16.min.js" type="text/javascript"></script>
<script src="../../Scripts/Views/Export/index.js" type="text/javascript"></script
<header class="header">
<div class="headerText">
<h1>Exportable Invoices</h1>
</div>
</header>
#using (Html.BeginForm("Export", "Export")) {
<table>
<tr class="mainheader">
<th>Invoice Number</th>
<th>Date</th>
<th>Organisation</th>
<th>Total (Excl GST)</th>
<th>Status</th>
<th>Exported Date</th>
<th>
<select id="expenseSelect"></select>
<input type="submit" id="btnexport" value="Export" />
</th>
</tr>
#foreach (var item in Model) {
<tr>
<td>
#Html.DisplayFor(modelItem => item.InvoiceNumber)
</td>
<td>
#Html.DisplayFor(modelItem => item.InvoiceDate, "{0:D}")
</td>
<td>
#Html.DisplayFor(modelItem => item.Organisation.Name)
</td>
<td>
#Html.DisplayFor(modelItem => item.TotalExcludingGst)
</td>
<td>
#Html.DisplayFor(modelItem => item.Status)
</td>
<td>
#Html.DisplayFor(modelItem => item.ExportedDateTime)
</td>
<td class="centered">
<input type="checkbox" class="exportcheckbox" data-invoiceid=#item.InvoiceId />
</td>
</tr>
}
</table>
}
<div>
#Html.ActionLink("Back to Summary", "Index", "Invoice")
</div>
Ok, so see how each checkbox has an attribrute data-invoiceid=#item.InvoiceId. Well I'm trying to get to an action method the Ids of all the invoices that have had their checkboxes checked. Also I'm trying to get the id of the selectlist expenseSelect which has options added to it on page load via jquery. I managed to achieve this with jquery and then sending the data with a $.post. The problem is in the file I'm sending the info to:
public ActionResult Export()
{
...
var csvData = _utility.GetCsvData(data);
return File(Encoding.UTF8.GetBytes(csvData), "text.csv", "invoices.csv");
}
brings up a save/open file dialog. I'm been informed this won't work for the jquery ajax call and I need to post the info back using a submit.
That's fine but now I have no idea how to send the select id and a list of the ids of the checked checkboxes to the method. Can anybody show me how to go about this?
You don't need any HTML5 data-* attributes since they are not sent to the server when you submit the form. In order to send their values you will have to use AJAX but this won't work with file downloads. So simply give your checkboxes a name:
<td class="centered">
<input type="checkbox" class="exportcheckbox" name="ids" value="#item.InvoiceId" />
</td>
and then on the server the default model binder will automatically construct an array of the ids of the checked items:
[HttpPost]
public ActionResult Export(int[] ids)
{
byte[] data = ...
return File(data, "text/csv", "invoices.csv");
}
Depending on the type of InvoiceId you might need to adjust the type of the action argument.
Radically changing my answer...
You could dynamically add a hidden IFRAME to your page. The IFRAME src can take your selected "ids" as a querystring parameter. This should get your your download dialog.
Got some help with the jquery from here: JQuery: Turn array input values into a string optimization
var selectedIdsArray = $(":checked").map(function(){return $(this).attr('data-invoiceid');});
var url = '#Url.Action("Export", "Export")?csv=' selectedIdsArray.get().join(',');
$('body').append("<iframe style='visibility:hidden' src='"+url +"'/>");

ASP.Net MVC returning values from List of Checkboxes

I have a model with a property that is a List. MyObjects simply has an id, a description and a selected boolean property.
I have managed to display the items as checkboxes on my view. I did this via:
<%foreach (var cat in Model.DefaultCategories)
{%>
<tr>
<td>
<%=cat.Category %>
</td>
<td>
<%=Html.CheckBoxFor(x=>cat.Selected) %>
</td>
</tr>
<%
}%>
</table>
However, there is a problem. They all end up, when rendered, with the same names. Here's a portion of my list:
<tr>
<td>
Medical
</td>
<td>
<input id="cat_Selected" name="cat.Selected" type="checkbox" value="true" /><input name="cat.Selected" type="hidden" value="false" />
</td>
</tr>
<tr>
<td>
Salary
</td>
<td>
<input checked="checked" id="cat_Selected" name="cat.Selected" type="checkbox" value="true" /><input name="cat.Selected" type="hidden" value="false" />
</td>
</tr>
They have all been named "cat.Selected".
How can I resolve this?
And then, when I submit, I need to iterate through them. With different names, I assume I can get them in my HttpPost method:
[HttpPost]
public ActionResult Modify(int id, FormCollection formValues)
{
PayeeDto p = new PayeeDto { Name = Request.Form["name"], PayeeId = id };
Services.PayeeServices.Save(p);
return RedirectToAction("Index");
}
The FormCollection will have the different names? At the moment, it just has the single 'cat.selected' item.
There is a way you can submit collections to your action by using names with []. As described here http://haacked.com/archive/2008/10/23/model-binding-to-a-list.aspx
<% for (int i = 0; i < Model.DefaultCategories.Count; i++) { %>
<td>
<input type="checkbox" name="[<%= i %>].Selected" <% Model.DefaultCategories[i].Selected ? "checked=\"checked\"" : string.Empty %>/>
</td>
<% }%>
Then your action can take a collection of models like so
public ActionResult Modify(int id, ICollection<UpdateModel> updates)
{}
I would recommend you using Editor Templates and stop writing loops in your views. They will take care of generating the proper names so that binding works. Example:
In your main view:
<table>
<thead>
<tr>
<th>Name</th>
<th>Selected</th>
</tr>
</thead>
<tbody>
<%: Html.EditorFor(x => x.DefaultCategories) %>
</tbody>
</table>
and then inside an editor template strongly typed to a Category (~/Views/Home/EditorTemplates/Category.ascx). Also if you want to get the corresponding Name back in your controller action you need to include it (probably as hidden field). Another technique involves adding only the id and then fetching back the relevant information from the database in your controller action:
<%# Control
Language="C#"
Inherits="System.Web.Mvc.ViewUserControl<AppName.Models.Category>" %>
<tr>
<td><%: Model.Name %></td>
<td>
<!-- include the category name as hidden field so that
we can fetch it back in the controller action
-->
<%: Html.HiddenFor(x => x.Name) %>
<%: Html.CheckBoxListFor(x => x.Selected) %>
</td>
</tr>
Now the naming convention is important here. If the DefaultCategories property on your view model is an IEnumerable<Category>, then the editor template needs to be called Category.ascx and placed in ~/Views/Home/EditorTemplates/Category.ascx or if it will be reused between multiple controllers in ~/Views/Shared/EditorTemplates/Category.ascx.
Also your controller action you are submitting to should use a view model as parameter:
[HttpPost]
public ActionResult Modify(MyViewModel model)
{
PayeeDto = Mapper.Map<MyViewModel, PayeeDto>(model);
Services.PayeeServices.Save(p);
return RedirectToAction("Index");
}
This may not be the best answer but I try not to use generated checkboxes in MVC.
I would change
<td>
<%=Html.CheckBoxFor(x=>cat.Selected) %>
</td>
To
<td>
<input type="checkbox" name="<%: cat.value %>" id="<%: cat.value %>" <% cat.Selected ? " checked=\"checked\" " : ""; %> />
</td>

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