ASP.NET MVC 2.0 - IList<T> CheckBoxes - c#

What is the best way to handle this:
class Option {
int id;
string name;
}
class QuoteItem
{
IList<Option> options;
}
class QuoteViewModel {
IList<Option> allOptions;
QuoteItem quoteItem;
}
Basically, I have all the available options in allOptions. I want to have a checkbox that puts another Option (even if its just its id) into the QuoteItem.options list when it is checked. How would I accomplish this? Would it best be an IList<bool> and bind it after the fact?

I suggest you take look at this blog entry from Phil Haack about model binding to a list
For your situation you can use simple model binding to a IEnumerable<int> options, where the values will be the id of your selected options.
your input view will then look something like this:
<form method="post" action="/QuoteItems/SetOptions">
<input type="hidden" name="options" value="1" />
<input type="hidden" name="options" value="4" />
<input type="hidden" name="options" value="2" />
<input type="hidden" name="options" value="8" />
<input type="submit" />
</form>
The hidden inputs contain your selected optionId's, note name attribute which is the same for each hidden input. The default model binder can bind this to a list of integers.
The thing you need to do next is adding / removing a hidden options input at client side depending on whether an item is selected in your "all-options" select control.

Related

List property ignored by controller if 0 index is missing

I'm building a webform in c# .net mvc using mongodb to store information. The form works with a company object that has a property that is a List of Addresses, called addressdata. When the form is submitted, the company object is sent to the controller and then upserted into MongoDB. The input names take the form
<input type="text" name="Company.addressdata[a].city" />
Where "a" is the index in the list. This all works great! The list of address objects is created upon submission and inserts into mongoDB.
However, I just added the ability to delete addresses, and now I'm running into trouble. I have noticed that when a user deletes the first row, all the rows after are lost. So, if they delete the 0 index, the Company object will not populate the list of Addresses and thus they will not go into MongoDB.
Is there a way to work around this? Is this how it's designed to work? It seems like too much to renumber all of the following rows with the new index, but is that what it takes? Or is there another way?
In my experience, that's by design. The indexes must start from 0, or you have to define your own indexes for each of them with a special element.
This article shows an example of that: http://haacked.com/archive/2008/10/23/model-binding-to-a-list.aspx/
<form method="post" action="/Home/Create">
<input type="hidden" name="products.Index" value="cold" />
<input type="text" name="products[cold].Name" value="Beer" />
<input type="text" name="products[cold].Price" value="7.32" />
<input type="hidden" name="products.Index" value="123" />
<input type="text" name="products[123].Name" value="Chips" />
<input type="text" name="products[123].Price" value="2.23" />
<input type="hidden" name="products.Index" value="caliente" />
<input type="text" name="products[caliente].Name" value="Salsa" />
<input type="text" name="products[caliente].Price" value="1.23" />
<input type="submit" />
</form>
So you have options, either:
Update indices when deleting, or
Define arbitrary indices

C# MVC send and retrieve a list<> to/from a view using one model

I am attempting to write a page that retrieves a List<> from a SQL table and display it so users can make changes. I'm already able to display the information. I'm running into problems with submitting it. I am unable to figure out a way to take the data entered and send the changes to a List<> so I can manipulate it in the controller/model.
Currently I'm placing the new data into arrays upon submit. Since I'm using #model List<modelname> in the view. Is it possible to replace the values or possibly put the data into a new list from the View?
You can absolutely do this, but people do initially tend to run into problems, so I'll provide a full example.
To start with, let's define a HomeController which has an Index action that returns a List<EmployeeViewModel>:
public class HomeController : Controller
{
public ActionResult Index()
{
// Assume this would really come from your database.
var employees = new List<EmployeeViewModel>()
{
new EmployeeViewModel { Id = 1, Name = "Employee 1" },
new EmployeeViewModel { Id = 2, Name = "Employee 2" },
new EmployeeViewModel { Id = 3, Name = "Employee 3" },
};
return View(employees);
}
[HttpPost]
public ActionResult Index(List<EmployeeViewModel> employees)
{
// Rest of action
}
}
The typical way people first approach this problem is to do something like the following in index.cshtml:
#model List<EmployeeViewModel>
#using (Html.BeginForm())
{
foreach (var item in Model)
{
<div class="row">
#Html.EditorFor(x => item.Id)
</div>
<div class="row">
#Html.EditorFor(x => item.Name)
</div>
}
<input type="submit" />
}
At first glance, this would look like it would work. However, if you put a breakpoint in the POST action and click the submit button, you'll notice that employees is null. The reason for that is because the foreach loop is generating HTML like the following:
<input name="item.Id" type="number" value="1" />
<input name="item.Name" type="text" value="Employee 1" />
<input name="item.Id" type="number" value="2" />
<input name="item.Name" type="text" value="Employee 2" />
<input name="item.Id" type="number" value="3" />
<input name="item.Name" type="text" value="Employee 3" />
I've stripped out some irrelevant parts there, but notice how the name attributes have the same values for each employee. When the default model binder tries to construct the list of data on the server, it has to be able to distinguish between the different employees, and, because it can't, it results in the list being null.
So why is it generating the same values? It's because of this:
#Html.EditorFor(x => item.Id)
#Html.EditorFor(x => item.Name)
We're not passing an index value to the HtmlHelper method calls, so that information doesn't make it to the generated HTML. We can fix this simply by making use of a for loop instead:
for (int i = 0; i < Model.Count; i++)
{
#Html.EditorFor(x => Model[i].Id)
#Html.EditorFor(x => Model[i].Name)
}
As we're now supplying an index with each method call, the generated HTML does now contain an index for each employee:
<input name="[0].Id" type="number" value="1" />
<input name="[0].Name" type="text" value="Employee 1" />
<input name="[1].Id" type="number" value="2" />
<input name="[1].Name" type="text" value="Employee 2" />
<input name="[2].Id" type="number" value="3" />
<input name="[2].Name" type="text" value="Employee 3" />
This allows the default model binder to associate an Id and a Name with each EmployeeViewModel, allowing it to correctly construct the type on the server.
At this point, your problem is solved, however, it's not recommended to use a for loop if you can avoid it, which brings us to editor templates. Editor templates are HtmlHelper methods that allow us to render a custom template (i.e. a view) for a given type. So let me show you an example of how to do that with the example code above.
To start with, you'll need to do the following:
Create an EditorTemplates folder inside of your ~/Views/Home/ folder (the EditorTemplates name has special meaning in MVC, so it's important to spell it correctly).
Create an EmployeeViewModel.cshtml view inside of that folder (again, the name is important here: it should match the name of the type you wish to create a custom template for).
Once you've done that, it should look like this:
Now, open up EmployeeViewModel.cshtml, and put your rendering code from Index.cs inside of it:
#model EmployeeViewModel
<div class="row">
#Html.EditorFor(x => x.Id)
</div>
<div class="row">
#Html.EditorFor(x => x.Name)
</div>
Finally, open up index.cshtml and change it to the following:
#model List<EmployeeViewModel>
#using (Html.BeginForm())
{
#Html.EditorFor(x => x)
<input type="submit" />
}
Html.EditorFor, and Html.DisplayFor, are both smart enough to recognise when they're being called for a collection, so they look for a custom template to render the collection's type (in this case, EditorFor will look for an editor template for EmployeeViewModel).
As we've provided an editor template, not only will it render it for each item in the collection, it will also generate the correct indices for each of those items, giving the model binder all the information it needs to reconstruct that collection on the server.
The end result is that model binding becomes simpler, and not only is the code for your views simpler, it is also split up based on the types involved, which makes your views easier to work with, as opposed to having a giant view that does everything.
I should also mention that as my example is directly using a collection, and nothing more, you can actually replace #Html.EditorFor(x => x) with #Html.EditorForModel(). I didn't do that initially as I didn't want to give the impression that templates are called just by using the latter.

Jquery validation for a name along with class [duplicate]

I have an HTML form that I'm processing with Perl CGI, like so:
<form action="process.cgi" id="main">
<input type="text" name="foo" class="required" />
<input type="text" name="foo" class="required" />
<input type="text" name="foo" class="required" />
</form>
And I'm validating it with the jQuery Validation plugin. Trouble is, when I call $('#main').valid(), it only checks that the first field is non-empty. How to I get it to check all the fields?
OFFICIAL DOCUMENTATION:
jqueryvalidation.org/reference/#link-markup-recommendations
"Mandated: A 'name' attribute is required for all input elements needing validation, and the plugin will not work without this. A 'name' attribute must also be unique to the form, as this is how the plugin keeps track of all input elements. However, each group of radio or checkbox elements will share the same 'name' since the value of this grouping represents a single piece of the form data."
<input type="text" name="foo" class="required" />
<input type="text" name="foo" class="required" />
<input type="text" name="foo" class="required" />
Quote OP:
Trouble is, when I call $('#main').valid(), it only checks that the
first field is non-empty. How to I get it to check all the fields?
It has nothing specifically to do with .valid() and it will fail the same using other jQuery Validate methods. That's because all three fields have the same name of foo. For this plugin each input must have a unique name.
DEMO: jsfiddle.net/xaFZj/
EDIT:
You cannot have duplicate names while using this plugin. This demo clearly shows that the plugin will fail when the name attribute is duplicated. There is no workaround since the name attribute is how the plugin keeps track of the form elements.
jsfiddle.net/ed3vxgmy/
The only exception is a radio or checkbox group where the elements in each grouping will share the same name.

Send checkboxes values from a view to a controller in two steps?

Let's say I've a two vanilla* html checkboxes in a Razor cshtml view.
<input type="checkbox" name="tags[]" id="categorieOne" value="1">
<input type="checkbox" name="tags[]" id="categorieTwo" value="2">
The first step would be to send this tags[] array to a controller.
The second step would be to get values 1 & 2 in seperated variables (example: in order to show "You've selected the following categories 1 ... 2" )
*By vanilla I mean they are not written with razor.
If you rename your checkboxes from tags[] to tags, your controller action could take a string array as parameter which will hold the selected values:
<input type="checkbox" name="tags" id="categorieOne" value="1" />
<input type="checkbox" name="tags" id="categorieTwo" value="2" />
and then:
[HttpPost]
public ActionResult SomeAction(string[] tags)
{
... the tags array will contain the selected values (1, 2, ...)
}

ASP.net MVC3 getting checkbox value from HTML form

I have a simple form in my MVC3 site that allows users to create a contest entry. This has been implemented and works fine currently, but a request has been made to now allow users to make their entries private.
In my Entry model I added a boolean isPrivate. Then I figured I would change the HTML forms for create and edit to include a checkbox to specify whether the entry should be private.
I'm new to MVC3, but I figured I could simply change the action that the form posts to by including a new boolean parameter.
This unfortunately doesn't seem to work. Can anyone tell me how checkbox values are passed from an HTML form to a post action? This is probably fairly common, but I can't seem to find an example for this on the web. Almost all the examples out there simple show text inputs, I can't find anything with checkboxes.
Form:
<form method="post" action="../Entry/Create" enctype="multipart/form-data" onsubmit="return isValidInput()">
<input type="text" id="EntryTitle" name="EntryTitle" />
<div id="invalidTitle" class="invalidData"></div>
<p id="char-remaining">(100 characters remaining)</p>
<input type="text" id="EntryVideo" name="EntryVideo" />
<div id="invalidVideo" class="invalidData"></div>
<p id="vid-desc">(URL of the Video to Embed)</p>
<input type="file" id="ImageFile" name="ImageFile" />
<div id="invalidImage" class="invalidData"></div>
<p id="file-desc">(200x200px, jpeg, png, or gif)</p>
<textarea id="EntryDesc" name="EntryDesc"></textarea>
<div id="invalidDesc" class="invalidData"></div>
<br />
<input type="checkbox" id="isPrivate" name="isPrivate" />
Make my entry private.
<br />
(private entries will only be viewable by you and site administrators)
<br />
<button id="new-entry-save">save</button>
</form>
Action:
public ActionResult Create(string EntryTitle, string EntryVideo, HttpPostedFileBase ImageFile, string EntryDesc, Boolean isPrivate)
{
...
}
add value="true" to checkbox, also add hidden input after it with same name and value=false, i.e.:
<input type="checkbox" id="isPrivate" name="isPrivate" value="true" />
<input type="hidden" name="isPrivate" value="false" />
If you don't want to use hidden, use bool? instead of bool (e.g. nullable)
The other option is to have hidden text field with the same name to force data in unchecked field to be part of the post. See Post the checkboxes that are unchecked.
<form>
<input type='hidden' value='0' name='selfdestruct'>
<input type='checkbox' value='1' name='selfdestruct'>
</form>

Categories

Resources