I have an generic enumerable of type BookCover that I wan't to display to the user. They can only choose one book cover from the selection available.
public class BookCover {
public bool IsSelected { get; set; }
public string CoverPathThumb { get; set; }
public string SpinePathThumb { get; set; }
public string BackPathThumb { get; set; }
}
My action method is similar to
public ActionResult
SelectCover(IEnumerable<BookCover>
covers);
In my template I just enumerate and write out the desired HTML but the problem is I don't know how to get the data from the post back.
How do I name my <input> id's? Is there another reason IEnumerabme isn't populating when the post back occurs?
#Vince: You can customize the ModelBinder. And in the ModelBinder, you can get data from HttpContext.Request.Form, after that you will build your new BookCover collection. Finallly you call
public ActionResult SelectCover(IEnumerable<BookCover> covers);
And remember registering it in Global.asax as:
ModelBinders.Binders[typeof(IEnumerable<BookCover>)] = new YourModelBinderName();
You can get references at here and here is discussion about it. Hope this help you!
You should add an ID you BookCover type, and then use this ID to identify the cover that the user selected. If you retrieve your covers from a database, just use this ID in your class.
I think you can do something like this:
<% foreach(var item in covers) { %>
<%: Html.EditorFor(x => item.IsSelected) %>
<% } %>
The name of your inputs should be in the form:
covers[0].IsSelected
covers[0].CoverPathThumb
covers[0].SpinePathThumb
covers[0].BackPathThumb
E.g.
<input type="text" name="covers[0].CoverPathThumb" />
Increase 0 for each cover entry.
Related
I've checked MVC post a list of complex objects as well as https://mhwelander.net/2014/03/26/asp-net-mvc-model-binding-not-occurring-when-posting-list-of-complex-types/ and a few other posts on the subject.
I would still rather ask the question, in order to understand and not simply copy paste an answer.
I have a complex type as below :
public interface ICaracteristic
{
int value { get; set; }
int max { get; set; }
string name { get; set; }
}
public class BaseAttributes : ICaracteristic
{
public int max { get; set; }
public int value { get; set; }
public string name { get; set; }
Random r = new Random();
}
And an object using multiples list of those types :
public class Character
{
// Infos
public string characterName { get; set; }
public string playerName { get; set; }
public IGame game { get; set; }
// Caracteristics
public List<ICaracteristic> baseAttr { get; set; }
public List<ICaracteristic> skills { get; set; }
public List<ICaracteristic> stats { get; set; }
public List<ICaracteristic> spendPoints { get; set; }
}
I spare you the constructors and a few other methods.
Now, for the creation, I simply ask my user to enter the names of character and player, no problem, it works wonderfully as these are simple strings.
For the edit, I get the character in "my db" (based on xml sheets but that doesn't matter here), and display it this way :
#model RPGmvc.Models.Character
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<h4>Character #Html.TextBoxFor(model => model.characterName)</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="row">
<!--SECTION BASE ATTRIBUTES-->
<div class="col-md-3">
<!-- #foreach (var item in Model.baseAttr)
{
<div>
<label>#Html.DisplayFor(model => item.name)</label>
<p>#Html.EditorFor(model => item.value, new { #class = "form-control" })</p>
</div>
}
For the display, this works perfectly fine : it shows the correct names and values of each base attribute of my character (as well as the values of the other lists).
However when I click the "save" button below the form, the model that is posted reset the values of that list... but not the characterName.
What is weird is that it seems to mix that model's different constructors : the playerName, which I don't use in my edit template, is set to "toby determined" (as in my empty basic constructor), but the characterName is the one of the current character being edited, instead of "new character" (as in said constructor)
I've tried to use a for instead of a foreach, thinking that maybe the index in the list would help to pass the correct values; but as I use an Interface that gave an error "Could not create an instance of interface".
I've tried with a custom editor, but it created the same problem as the "foreach".
(Here's the custom editor, just in case :
#model RPGmvc.Models.Caracteristic.ICaracteristic
<p>
#Html.DisplayFor(m => m.name)
#Html.DisplayFor(m => m.value)
</p>
Note that using an interface as model didn't cause any problem. )
I understand that I could apparently add an awful lot of annotations through my page to sort this, but since the custom editor didn't work and neither did the for with index, I first would like an explanation of what is happening :
What happens with the model binder here?
Could I "force" him to create an instance of an implementation of my interface, instead of the interface itself?
How come the HttpPost creates a custom object mixing my constructors ?
Thanks for your help.
Edit : Stephen Muelcke helped me by advising to remove the Interfaces from my model and using the real implementations. That almost worked :
Now my Post takes the correct values of the BaseAttributes, but the names of those are "null".
This is problematic since my datas comes from XML sheets, in which I search this way :
foreach (ICaracteristic battr in myCharac.baseAttr)
{
var currentNode = myDoc.SelectSingleNode("/character_sheet/base_attributes/" + battr.name.Replace(" ", "_").ToLower());
currentNode.InnerText = battr.value.ToString();
}
Which obviously fails, as name is "null".
I only did the change from "foreach" to "for" in the "Base Attributes" section. In that section, all names are set to null. In the other sections, no values are taken.
Any idea?
So, since Stephen Muecke doesn't care for the reputation :
--You can't model bind to an interface.
--You can't do a foreach on a list because modelbinder needs the index.
--You need a "control" sort on every value you want to use, so for complex types you need one even on the display name.
A good solution is to do a for loop, in which you include an #Html.Hiddenfor in addition to a #Html.DisplayFor, so that the display name is not editable but still has a control.
For the Interface and modelbinder lack of interaction, the only solution is to use the implementation of the interface. Sadly.
I'm building an internal page that allows trusted users to change a parameter setup manually through a form. The inputs to this setup are a list of setupparameters (of unknown size), each with a specific list of values. The user can then select a value for all or a subset of the parameters.
I have attempted to illustrate this with my current model for the view
public class SetupModel
{
public List<SetupParameter> Parameters { get; set; }
}
public class SetupParameter
{
public string ParameterName { get; set; }
// list with text=paramvalue, value=paramvalueid
public SelectList ParameterValueList { get; set; }
// id of the selected parametervalue if any
public int? SelectedParameterValueID { get; set; }
}
My current attempt at rendering a view for this:
<% using (Html.BeginForm("Update", "Parameters") {%>
...
<% foreach( var parameter in Model.Parameters ) { %>
<div><%: parameter.ParameterName %></div>
<div><%: Html.DropDownListFor(x => parameter.SelectedParameterValueID, parameter.ParameterValueList, "Please select") %></div>
<% } %>
...
My question is how can I render a view that allows me to submit the form and get a reasonably understandable model back to my form action that will allow me to obtain the list of selected parameter values. I'm not aware of the best practices or tricks here, so I will appreciate any feedback I get :)
You could try using a FormCollection:
public ActionResult Submit(FormCollection formCollection)
{
//Iterate form collection to get fields
return View();
}
You might find this post by Phil Haack useful: Model Binding To A List.
Also note that you'll need to post back an identifier (ParameterName, for example) for each parameter too, so you can indentify which value corresponds to a parameter back in the controller.
I’m using the Membership Provider and would like to display a list of all the users and their First Name, Last Name etc using the GetAllUsers function.
I'm having trouble understanding how to implement this function in MVC.
Has anyone implemented this in MVC or is there an easier way to list all the users in my application?
Any help or advise would be really helpful.
Controller
public ActionResult GetUsers()
{
var users = Membership.GetAllUsers();
return View(users);
}
View Model
public class GetUsers
{
[Required]
[DisplayName("User name")]
public string UserName { get; set; }
[Required]
[DisplayName("User name")]
public string FirstName { get; set; }
}
View
<%= Html.Encode(item.UserName) %>
Error
The model item passed into the dictionary is of type 'System.Web.Security.MembershipUserCollection', but this dictionary requires a model item of type 'System.Collections.Generic.IEnumerable`1[Account.Models.GetUsers]'.
View
Inherits="System.Web.Mvc.ViewPage<MembershipUserCollection>"
<ul>
<%foreach (MembershipUser user in Model){ %>
<li><%=user.UserName %></li>
<% }%>
</ul>
Controller
public ActionResult Admin()
{
var users = Membership.GetAllUsers();
return View(users);
}
What's the difficulty you have with it? the GetAllUsers method simply returns a collection of users that you can then display ... either manually, or using a grid component from a vendor like Telerik.
something like:
<% foreach(var user in Membership.GetAllUsers()) { %>
<p>Name: <%= user.UserName %></p>
<% } %>
Obviously, heed the warning in the documentation:
Be careful when using the GetAllUsers
method with very large user databases,
as the resulting
MembershipUserCollection in your
ASP.NET page may degrade the
performance of your application.
There is an overload which lets you do paging to get around this :-)
#Jemes, the problem you're having is that you're passing a System.Web.Security.MembershipUserCollection as the model to your view and you specified that the model of your view was of type Account.Models.GetUsers. Change the type to System.Web.Security.MembershipUserCollection. However, if you're using the default Membership provider in your solution, you will not have the First Name available as the MembershipUser class doesn't have a FirstName property.
Am using strongly typed view to show a complex object in a data entry/edit form. for eg: Model.UserInformation.Name, Model.LivingPlace.FacilitiesSelectList, Model.Education.DegreesList... etc. These information are shown in multiselect listbox, grids.. etc. User can change the information in the edit screen. Is there any way to post the Model object with user changes to controller on sumbit button click. Please suggest.
Regards,
SHAN
The same object instance that has been passed to the view: No. ASP.NET MVC uses a default Model binder to instantiate new action parameters from request values. So for example if you had the following action method:
public ActionMethod DoWork(User model)
{
return View();
}
public class Address
{
public string Street { get; set; }
}
public class User
{
public string FirstName { get; set; }
public string LastName { get; set; }
public Address[] Addresses { get; set; }
}
the binder will look in the request and try to bind model values. You could to the following in your View:
<%= Html.TextBox("FirstName") %>
<%= Html.TextBox("LastName") %>
<%= Html.TextBox("Addresses[0].Street") %>
<%= Html.TextBox("Addresses[1].Street") %>
This will automatically populate the values of your model in the controller action.
To avoid mass assignment of properties that shouldn't be bound from request values it is always a good idea to use the BindAttribute and set Exclude or Include properties.
Use <input type="text" name="UserInformation.Name"><input> to bind to subobjects.
I'm new to ASP.NET MVC so this could have an obvious answer. Right now I have a form in my view with a lot of input controls, so I have an action that looks like this:
public ActionResult MyAction(string formItemOne, int? formItemTwo, etc...)
It has like a dozen parameters, which is pretty ugly. I'm trying to change it to this:
public ActionResult MyAction(FormCollection formItems)
and then parse the items dynamically. But when I change to a FormCollection, the form items no longer "automagically" remember their values through postbacks. Why would changing to a FormCollection change this behavior? Anything simple I can do to get it working automagically again?
Thanks for the help,
~ Justin
Another solution is to use models instead of manipulating the raw values. Like this:
class MyModel
{
public string ItemOne { get; set; }
public int? ItemTwo { get; set; }
}
Then use this code:
public ActionResult MyAction(MyModel model)
{
// Do things with model.
return this.View(model);
}
In your view:
<%# Page Inherits="System.Web.Mvc.ViewPage<MyModel>" %>
<%= Html.TextBox("ItemOne", Model.ItemOne) %>
<%= Html.TextBox("ItemTwo", Model.ItemTwo) %>
To replace your big list of parameters with a single one, use a view model. If after the POST you return this model to your view, then your view will remember the values posted.
A view model is simply an class with your action parameters as public properties. For example, you could do something like this, replacing:
public ActionResult MyAction(string formItemOne, int? formItemTwo, etc...)
with
public ActionResult MyAction(FormItems formItems)
{
//your code...
return View(formItems);
}
where FormItems is
public class FormItems
{
public property string formItemOne {get; set;}
public property int? formItemTwo {get; set;}
}
You may see a complete example in Stephen Walter's post ASP.NET MVC Tip #50 – Create View Models.
Maybe because they aren't magically inserted into the ModelState dictionary anymore. Try inserting them there.
If you use UpdateModel() or TryUpdateModel() I think the values are gonna be persisted.