Custom EditorFor and ICollection issue - asp.net-mvc

I'm doing this
using (var db = new MyContext())
{
var g = new MyGroup
{
Caracteristicas = db.Caracteristicas.ToList()
};
return View(g);
}
and getting this exception when trying to display that data:
The ObjectContext instance has been disposed and can no longer be used
for operations that require a connection.
View:
#Html.EditorFor(m => m.Caracteristicas)
Custom editor:
#model Exemplo.Models.Caracteristica
<label class="checkbox">#Html.CheckBoxFor(m => m.Valor) #Model.Nome</label>
SOLVED (in part)
The problem was that I mismatched the name of the custom editor file.
But now that this little mistake is fixed, however, all Ids (in bind) are zero... how to solve that? :(
This is the output:
<label class="checkbox">
<input data-val="true" data-val-required="The Valor field is required." id="Caracteristicas_0__Valor" name="Caracteristicas[0].Valor" type="checkbox" value="true" />
<input name="Caracteristicas[0].Valor" type="hidden" value="false" />
OldItem</label>
"value" shouldn't be the Id? :(

Related

Boolean model binding issue in Mvc 4

I want to bind a boolean property to a hidden input controller, but the output html code was error
code as follows:
public class TestModel
{
public bool IsOk { get; set; }
public bool IsSuccess { get; set; }
}
public class HomeController : Controller
{
public ActionResult Index()
{
return View(new TestModel { IsOk = false, IsSuccess = true });
}
}
<h2>Index</h2>
<p>#Model.IsOk</p>
<p>
<input type="hidden" value="#Model.IsOk" />
</p>
<p>
<input type="hidden" value="#Model.IsSuccess" />
</p>
Html Output
<h2>Index</h2>
<p>False</p> //works
<p>
<input type="hidden" /> //where is value?
</p>
<p>
<input type="hidden" value="value" /> //wath's this?
</p>
But if i use ToString(), all above works well, so is it my mistake?
In HTML when you have an attribute which functions as an on/off or true/false switch you remove the attribute when the attribute is off/false and add the attribute with the same value as the attribute name when the attribute is on/true. Razor provides you with that functionality as you have already experienced.
Perhaps you intend to use Html.HiddenFor in the view?
<p>
#Html.HiddenFor(m => m.IsOk)
</p>
<p>
#Html.HiddenFor(m => m.IsSuccess)
</p>
This will produce this HTML where you have value="False" and value="True" as you expect:
<p>
<input data-val="true" data-val-required="The IsOk field is required."
id="IsOk" name="IsOk" type="hidden" value="False" />
</p>
<p>
<input data-val="true" data-val-required="The IsSuccess field is required."
id="IsSuccess" name="IsSuccess" type="hidden" value="True" />
</p>
Also, the model binder will be able to round-trip you view model properties.
Html attributes requires string objects
It's not automatically converted
So you have to use ToString()
Please try this.
$('#controlId').is(":checked");

asp.net mvc model binder with child collections

I'm learning ASP.NET MVC and having a problem with saving a model as below:
Orders -> OrderItems
In my Create view , I will add some items into Order and when I press submit, I want the items data bind to model.OrderItems. How can I achieve this ?
[HttpPost]
public ActionResult Create(OrderViewModel model)
{
...............
//iterate item list and add to main order
foreach (var item in model.OrderItems) <~ this is what I'm trying to do
{
}
...............
}
Thanks a lot.
If they're sequential, you can create an index on each of your inputs:
<input type="text" name="OrderItems[0].Name" value="Stuff" />
<input type="text" name="OrderItems[0].Price" value="5.00" />
...
<input type="text" name="OrderItems[1].Name" value="OtherStuff" />
<input type="text" name="OrderItems[1].Price" value="15.00" />
Or you'll need to provide an index:
<input type="hidden" name="OrderItems.Index" value="#item.ItemId" />
<input type="text" name="OrderItems[#item.ItemId].Name" value="Stuff" />
<input type="text" name="OrderItems[#item.ItemId].Price" value="5.00" />

Accepting params or raw data in controller?

I was wondering if it would be possible having a "params" argument in a controller function, or something similar which would allow me to process X amount of entries in my form.
For instance, I have a form which has X amount of "name" elements, which are auto-generated through jQuery. An example of these name elements could be the following:
<input type="text" name="studentName1"></input>
<input type="text" name="studentName2"></input>
<input type="text" name="studentName3"></input>
Now, there's a different amount of student names every time, so this makes it quite complex for me to handle the form data in my controller. I had something like the following 2 examples in mind, but of course they wouldn't work in reality.
[HttpPost]
public ActionResult PostStudentNames(params string[] studentNames)
Or:
[HttpPost]
public ActionResult PostStudentNames(string[] formValues)
Can I achieve something similar to that?
I just want to chime in with a different approach you can use for this. If it's more convenient, you can model bind directly to collections of primitive or complex types. Here's 2 examples:
index.cshtml:
#using (Html.BeginForm("ListStrings", "Home"))
{
<p>Bind a collection of strings:</p>
<input type="text" name="[0]" value="The quick" /><br />
<input type="text" name="[1]" value="brown fox" /><br />
<input type="text" name="[2]" value="jumped over" /><br />
<input type="text" name="[3]" value="the donkey" /><br />
<input type="submit" value="List" />
}
#using (Html.BeginForm("ListComplexModel", "Home"))
{
<p>Bind a collection of complex models:</p>
<input type="text" name="[0].Id" value="1" /><br />
<input type="text" name="[0].Name" value="Bob" /><br />
<input type="text" name="[1].Id" value="2" /><br />
<input type="text" name="[1].Name" value="Jane" /><br />
<input type="submit" value="List" />
}
Student.cs:
public class Student
{
public int Id { get; set; }
public string Name { get; set; }
}
HomeController.cs:
public class HomeController : Controller
{
public ActionResult Index()
{
return View();
}
public ActionResult ListStrings(List<string> items)
{
return View(items);
}
public ActionResult ListComplexModel(List<Student> items)
{
return View(items);
}
}
ListStrings.cshtml:
#foreach (var item in Model)
{
<p>#item</p>
}
ListComplexModel.cshtml:
#foreach (var item in Model)
{
<p>#item.Id. #item.Name</p>
}
The first form simply binds a list of strings. The second, binds the form data to a List<Student>. By using this approach, you can let the default model binder do some of the tedious work for you.
Updated for comment
Yes you can do that too:
Form:
#using (Html.BeginForm("ListComplexModel", "Home"))
{
<p>Bind a collection of complex models:</p>
<input type="text" name="[0].Id" value="1" /><br />
<input type="text" name="[0].Name" value="Bob" /><br />
<input type="text" name="[1].Id" value="2" /><br />
<input type="text" name="[1].Name" value="Jane" /><br />
<input type="text" name="ClassId" value="13" /><br />
<input type="submit" value="List" />
}
Controller action:
public ActionResult ListComplexModel(List<Student> items, int ClassId)
{
// do stuff
}
Mathias,
This works perfectly well without recourse to the params object. your form controls:
<input type="text" name="studentName" />
<input type="text" name="studentName" />
<input type="text" name="studentName" />
<input type="text" name="professorName" />
You would use the FormCollection object, which will contain all your form elements as either comma separated lists (if a control array) or as single properties. In the above example, this is what we'd get:
[HttpPost]
public ActionResult PostStudentNames(FormCollection formValues)
{
// basic check for rogue commas inside input controls
// would need far more sophistication in a #real# app :)
var valueStudents = formValues["studentName"].Split(',')
.Where(x => x.Length > 0).ToArray();
var valueProfessor = formValues["professorName"];
// other stuff
}
etc... At least, this is my recollection of this from a recent project. :)
<input type="text" name="studentName[0]"></input>
<input type="text" name="studentName[1]"></input>
<input type="text" name="studentName[2]"></input>
public ActionResult PostStudentNames(string[] studentName)
{
}

object gets passed to action as string

I have a form that has a hidden field wich stores a object. This object is a RoutesValues (I want to store a reference because when I process the form I want to redirect to a route). The action that processes the form is:
public ActionResult Añadir(string userName, string codigoArticulo, string resultAction, string resultController, object resultRouteValues, int cantidad)
{
processForm(codigoArticulo, cantidad);
if (!ModelState.IsValid)
TempData["Error"] = #ErrorStrings.CantidadMayorQue0;
if (!string.IsNullOrWhiteSpace(resultAction) && !string.IsNullOrWhiteSpace(resultController))
return RedirectToAction(resultAction, resultController, resultRouteValues);
return RedirectToAction("Index", "Busqueda", new {Area = ""});
}
and my form is:
#using (Html.BeginForm("Añadir", "Carrito", FormMethod.Get, new { #class = "afegidorCarrito" }))
{
<fieldset>
<input type="hidden" name="codigoArticulo" value="#Model.CodiArticle" />
<input type="hidden" name="resultController" value="#Model.Controller" />
<input type="hidden" name="resultAction" value="#Model.Action" />
<input type="hidden" name="resultRouteValues" value="#Model.RouteValues" />
<input type="text" name="cantidad" value="1" class="anadirCantidad" />
<input type="submit" />
</fieldset>
}
the problem I have is that resultRouteValues gets passed as a string instead of an object. Is there any way to fix this?
Thanks.
No, there is no easy way if RouteValues is a complex object. You will have to serialize the object into some text representation into this hidden field and then deserialize it back in your controller action. You may take a look at MvcContrib's Html.Serialize helper.

MVC Html.CheckBox and form submit issue

Crazy issue with submitting of values in Html.Checkbox in ASP.NET MVC RC
Some of the values are just not come to Request.Params
At my form I have this line inside the cycle:
<%=Html.CheckBox("cb" + p.Option.Id, p.Option.IsAllowed, new { value = 6 })%>
and it renders to next:
<input checked="checked" id="cb17" name="cb17" type="checkbox" value="6" />
<input name="cb17" type="hidden" value="false" />
<input checked="checked" id="cb18" name="cb18" type="checkbox" value="6" />
<input name="cb18" type="hidden" value="false" />
<input id="cb19" name="cb19" type="checkbox" value="6" />
<input name="cb19" type="hidden" value="false" />
<input id="cb20" name="cb20" type="checkbox" value="6" />
<input name="cb20" type="hidden" value="false" />
<input checked="checked" id="cb21" name="cb21" type="checkbox" value="6" />
<input name="cb21" type="hidden" value="false" />
After submitting the Form I'm get something like:
Form.Params["cb17"] = {6, "false"}
Form.Params["cb18"] = {6, "false"}
Form.Params["cb19"] = {"false"}
Form.Params["cb20"] = {"6,false"}
Form.Params["cb21"] = {"false"}
In the request string Some of the params are displayed twice (normal situation) and some only ONE TIME (only value of hidden field).
It seems that it doesn't rely on whether checkbox was checked or not, whether value has changed or so...
Does anybody faced with such a situation? How can I work around?
<% using(Html.BeginForm("Retrieve", "Home")) %>//Retrieve is the name of the action while Home is the name of the controller
<% { %>
<%foreach (var app in newApps) { %>
<tr>
<td><%=Html.CheckBox(""+app.ApplicationId )%></td>
</tr>
<%} %>
<input type"submit"/>
<% } %>
and in your controller
List<app>=newApps; //Database bind
for(int i=0; i<app.Count;i++)
{
var checkbox=Request.Form[""+app[i].ApplicationId];
if(checkbox!="false")// if not false then true,false is returned
}
the reason you check for false because the Html Checkbox helper does some kind of freaky thing for value true
True returns as:
it makes the string read "true, false"
so you may have thought it was two values but its just one and means true
False returns as:
it makes the string read "false"
This is actually the way it should work according to specifications.
It has nothing to do with ASP.NET MVC, but when a check box is left unchecked it is not included in the POST collection.
You get two values because you have both a checkbox and an input with the same name (and the ones you have two values for are most likely the ones with checkboxes checked).
Edit: specifications from W3C
Without the need to ask database about the ids after form submitting/before saving (Stateless mode) I've produced such code:
foreach (string key in Request.Form)
{
var checkbox = String.Empty;
if (key.StartsWith("cb"))
{
checkbox = Request.Form["" + key];
if (checkbox != "false")
{
int id = Convert.ToInt32(key.Remove(0, 2));
}
}
}
Thanks you guys to help me work around this issue!
I use this:
public struct EditedCheckboxValue
{
public bool Current { get; private set; }
public bool Previous { get; private set; }
public bool Changed { get; private set; }
public EditedCheckboxValue(System.Web.Mvc.FormCollection collection, string checkboxID) : this()
{
string[] values = collection[checkboxID].Split(new char[] { ',' });
if (values.Length == 2)
{ // checkbox value changed, Format: current,old
Current = bool.Parse(values[0]);
Previous = bool.Parse(values[1]);
Changed = (Current != Previous);
}
else if (values.Length == 1)
{
Current = bool.Parse(values[0]);
Previous = Current;
Changed = false;
}
else
throw new FormatException("invalid format for edited checkbox value in FormCollection");
}
}
and then call it like this:
EditedCheckboxValue issomething = new EditedCheckboxValue(collection, "FieldName");
instance.IsSomething = issomething.Current;

Resources