I'm wondering why my controller is not recognizing an array of ints but recognizes an object literal of ints.
c#:
Controller:
[HttpGet]
public ActionResult Index(Model search)
Model:
public class Model {
public int[] MyList {get; set;}
}
javascript:
model.myList = [0, 1]; //null at controller
model.myList = {0, 1}; //controller recognizes this
What's going on here?
This may be of help for information with regards to how MVC translates and treats arrays, dictionary objects etc http://www.hanselman.com/blog/ASPNETWireFormatForModelBindingToArraysListsCollectionsDictionaries.aspx
Resolved by changing [HttpGet] to [HttPost]
Related
I have this controller
public class DownloadController : Controller
{
[HttpPost]
public FileContentResult GetFile(MyModel model)
{
// Action code
}
}
And this model
public class MyModel
{
public string Test { get; set; }
}
Passing model from the View to the controller works fine like this
#using (Html.BeginForm("GetFile", "Download", FormMethod.Post))
{
#Html.HiddenFor(m => m.Test)
<button type="submit" name="submit" class="submit">Download</button>
}
The model is correctly passed to the controller and I can do what I need to do with it.
Now, what I'm trying to achieve is to make this GetFile() controller action to be generic, so I can pass it any model, without strongly typing the model class in method signature, like I did in the example above.
I know I can achieve this by overriding GetFile() method once for each model that I have, but I'm wondering is there a better way to do this, to stay DRY as much as possible?
Thank you.
I'd suggest using a base class:
public class BaseGetFileModel {}
which various models will derive from.
[HttpPost]
public FileContentResult GetFile(BaseGetFileModel model)
EDIT:
OK, if you want a generic way of doing this, then you could do this:
[HttpPost]
public FileContentResult GetFile()
{
var someValue = Request["SomeValue"];
}
You don't accept any model parameter, you simply pick up POST'd values from the request. Or you could iterate through the request values collection, if you want to avoid hard-coding key names.
I have a model with an property of type icollection.
public class myClass{
public string param1{get; set;}
public string param2{get; set;}
public virtual ICollection<myClass2> param3{get; set;}
public myClass()
{
param3 = new hashSet<myClass2>();
}
}
public class myClass2{
public string param4{get; set;}
public string param5{get; set;}
public virtual myClass param6{get; set;}
}
I pass the model containing these two class to my view and am able to see the items in my icollection by using foreach(var item in Model.myClass.param3)
And I store the items in a hidden field to retrieve it in my controller
foreach(var item in Model.myClass.param3){
#Html.HiddenFor(model => item.parm4);
#Html.HiddenFor(model => item.parm5);
}
But when I submit the form and pass the model to the controller, I get a count = 0 when calling model.myClass.param3.
How can I pass an ICollection to my view?
I tried this link but do not know why it is not working.
EDIT
The link uses the class Book as a list in order to index (suggesting I should cast the ICollection to a list). How do I do that? Also, if I cast it to a list how do i pass that to the controller as the controller expects to receive an ICollectiion?
You can't use a foreach loop for that, you have to use a for loop.
for (int i=0; i<Model.MyClass.param3.Count; i++)
{
#Html.HiddenFor( model => model.MyClass.param3[i])
}
The reason for this is the HiddenFor helper needs some way of assigning unique names to each field for the model binding to work. The i variable accomplishes this.
In your case you;ll need to do some refactoring to implement this. I don't think ICollection or HashSet supports indexing, so you'll need to cast it to a List or some collection that does support indexing.
See this excellent blog post on the subject.
I'm pulling my hair out on this one.
I am trying to implement a multi-step wizard, and i'm using the Html.Serialize html helper in MVC3 Futures. This works well, except one of the properties in my model is a SelectList. I don't want this property serialized (and it blows up when it tries anyways).
I can't use [NonSerialized] because that only works on fields, not properties. I've even tried some of the other normal ways such as [XmlIgnore] (which I didn't think would work anyways).
Can anyone suggest an attribute that will ignore a property in a model when using Html.Serialize?
EDIT:
The error I get when I try to serialize is a InvalidDataContractException. There is this message:
Type 'System.Web.Mvc.SelectList' cannot be serialized. Consider marking it with the DataContractAttribute attribute, and marking all of its members you want serialized with the DataMemberAttribute attribute. If the type is a collection, consider marking it with the CollectionDataContractAttribute. See the Microsoft .NET Framework documentation for other supported types.
However, if I do this then I have to mark all the members with [DataMember] just to exclude 1 property, which seems kind of stupid.
UPDATE:
A quick example of this is this bit of code (make sure to add reference to System.Runtime.Serialization.dll):
Test.cs
[Serializable]
public class Test
{
public int ID { get; set; }
[IgnoreDataMember]
public SelectList TestList { get; set; }
}
HomeController.cs
public ActionResult About()
{
return View(new Test() { ID = 0, TestList = new SelectList(new [] {""})});
}
Home/About.cshtml
#using Microsoft.Web.Mvc
#model MvcApplication3.Models.Test
#Html.Serialize("Test", Model)
This generates the InvalidDataContractException
public class MyViewModel
{
[IgnoreDataMember]
public SelectList Items { get; set; }
...
}
or simply:
public class MyViewModel
{
public IEnumerable<SelectListItem> Items { get; set; }
...
}
I've successfully removed references to Request.Form in my code, but I haven't seen any ASP.NET MVC 3 support for binding Request.Files to my view model. Would best practice be to just pass the Request.Files object (HttpFileCollectionBase) to whatever method processes my attachments? E.g.,
[HttpPost]
public ActionResult UpdateStatus(StatusViewModel vm)
{
bool updated = HandleUpdate(Request.Files, vm);
...
return View("Updated");
}
You can simply declare Action parameters of type HttpPostedFileBase, like so:
public ActionResult Method(HttpPostedFileBase fileUploaded)
You can bind to arrays of like-named instances, as well.
public ActionResult Method(HttpPostedFileBase[] filesUploaded)
and, HttpPostedFileBase can be a member of your view model.
public class MyModel{
public string Name{get; set;}
public HttpPostedFileBase FileUploaded{get; set;}
}
The question is very simple:
Say you have a model called Person
public class Person
{
public int PersonID {get; set;}
public string Name {get; set;}
[AllowHtml] // Allow html in Intro property
public string Intro {get; set;}
[ScaffoldColumn(false)]
public string ComplicatedValue {get; set;}
}
In controller's Create action
[HttpPost]
public ActionResult Create(Person o, FormCollection collection)
{
// whatever code here;
}
If you run it,
input plain text for Intro, no
problem happens.
input html content for Intro, no matter how you set
your configuration file, it will
tells "A potential dangerous ..."
I DO find the reason of this problem.
If you change the function to
public ActionResult Create(Person o) // Get rid of the *FormCollection collection*
{
// whatever code here;
}
This will eliminate the "potential dangerous" error.
But my problem is that for my application, I have to use the secondary parameter FormCollection collection in the Create Action method, because I need to use some other control values and server variable to assign a calculated value to the ComplicatedValue property.
If any expert of ASP.NET MVC3 have met the same problem as me, and found a solution, please kindly let me know.
This forum at this link discusses this issue at length and gives some workarounds.
http://forums.asp.net/p/1621677/4161637.aspx
Here is one solution from that thread that may or may not work for you:
public ActionResult Create(Person o) // Get rid of the *FormCollection collection*
{
FormCollection form = new FormCollection(Request.Unvalidated().Form);
// whatever code here;
}
or my own recommendation:
public ActionResult Create(Person o, int otherControlValue1, int otherControlValue2, ...)
{
o.ComplicatedValue = CalculateComplicatedValue(otherControlValue1, otherControlValue2, ...);
// whatever code here.
}
In my case, I am not using the FormCollection, but it was there so that I had a different footprint on my [HttpPost] method. I did this hack and put in a bogus parameter:
public virtual ActionResult Edit(int id)
{
return View(this.repository.GetById(id));
}
[HttpPost]
public virtual ActionResult Edit(int id, int? bogusID)
{
var d = repository.GetById(id);
if (TryUpdateModel(d))
{
repository.Save();
return RedirectToAction("Index");
}
return View();
}
Might I suggest using a custom model binder instead of pulling the complex data from a FormCollection. Scott Hanselman has a blog post on creating a custom model binder that would serve as a good template. In his post he puts together a DateTimeModelBinder that allows a DateTime property to be set either by a single input containing the date or a pair of inputs containing a date and a time.
have you tried
[Bind(Exclude="ComplicatedValue")]
:
[HttpPost]
public ActionResult Create([Bind(Exclude="ComplicatedValue")]Person o)
{
}
?
with that it allows you to exclude setting ComplicatedValue property on the form and still submit the object as a Person class.
hope that helps