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.
Related
I have created Comment box in Parent View as a partail view to add comment. below is my Comment model.
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int CMT_ID { get; set; }
private DateTime _date = DateTime.Now;
public DateTime cmd_ad
{
get { return _date; }
set { _date = value; }
}
public string cmd_content { get; set; }
public string t_email { get; set; }
public Nullable<int> SPID { get; set; }
public virtual service_provider service_provider { get; set; }
from partail View I have to submit cmd_content,t_email and SPID.Below is partail view.
#using (Html.BeginForm("AddComment", "Food")){
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
<table>
<tr><td></td> <td>#Html.TextAreaFor(model => model.cmd_content)</td></tr>
<tr><td>Email</td><td>#Html.EditorFor(model => model.t_email)</td></tr>
<tr><td></td> <td>#Html.ValidationMessageFor(model => model.t_email)</td</tr></table><p><input type="submit" value="Comment" class="btn btn-success" /></p>}
I have created action methods for submit data from partail View. Action method Details method for parent View.AddComment is Action method for _Comment partail View.Below is my Controller method.
public ActionResult Details(int id = 0)
{
ImageData details = new ImageData();
var sp_details = (from s in db.service_provider
join p in db.pictures on s.SPID equals p.SPID
join c in db.cities on s.City_ID equals c.City_ID
where s.SPID == id
select new ImageData()
{
Sp_name = s.Sp_name,
SPID = s.SPID,
pic = p.pic
});
return View(sp_details);
}
public ActionResult AddComment()
{
return PartialView("_Comment");
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult AddComment(comment cmt)
{
if (ModelState.IsValid)
{
db.comments.Add(cmt);
db.SaveChanges();
return RedirectToAction("Details", "Food");
}
return PartialView("_Comment", cmt);
}
When Someone add comment partail view should submit cmt_content,t_emil,SPID. My problem is How do I fetch SPID from parent View. It is same as parameter pass to Details Action method. Can Somebody help me to solve this problem.
Actually, the way you have this set up, there won't be anything other than the partial on post, anyways. Posting to an action that returns a partial view, should only be done via AJAX. If you're not using AJAX to post, then you should always return View, or you'll lose all your layout.
That said, you need to step back and understand the platform you're developing on: the Web. The web operates on the HTTP protocol and the TCP/IP protocols, on a lower level. Importantly, all of these are designed to be stateless. The whole idea was to create a mesh network where nodes could come online and drop off without bringing down the rest of the network. To achieve that, no individual server can have intimate knowledge of communication with a particular client, or if that server were to go down, then the client can no longer resume.
At a higher level, this translates into each request being a unique thing, uninformed by any request that proceeded it. When you post, the only thing that exists server-side is what you posted. That variable that existed before is long gone. If you need some value again in the post action, then you need to post it along with every thing else, or rerun whatever logic got you the value in the first place after the post. It's not just going to be there waiting for you.
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.
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.
I am using incremental sequencing for a collection of objects in a form. All works fine and dandy except when I need to use DropDownListFor. Lots of questions concerning binding a dropdown and selecting the correct value, which is working fine in my case. However I am unclear on what is supposed to have on the HttpPost action in my controller. Here is my code:
Model
public class WorkRequestList
{
public WorkRequest[] Requests { get; set; }
public Vehicle[] Vehicles { get; set; }
}
View
<% using (Html.BeginForm()) {%>
<% for (var i = 0; i < Model.Requests.Count(); i++) { %>
<%=Html.DropDownListFor(x => x.Requests[i].AssignedTo,new SelectList(Model.Vehicles,"Id","Name",Model.Requests[i].AssignedTo.Id)) %>
<%}%>
<%=Html.SubmitButton("TopSubmit","Submit") %>
<%}%>
Posted Action
[HttpPost]
public ActionResult Schedule(WorkRequestList form)
{
//what goes here?
}
The dropdown lists get populated just fine, they get pre-selected just fine. But on post back form.Requests.AssignedTo is null. I'm assuming the Vehicle Id is being posted back somewhere, but how do I get to that without resorting looping through the Request magic strings:
var id = Request["Requests[" + i + "].AssignedTo"];
Here is an alternate approach, as I could not get sub objects bound either without an explicit modelbinder:
define a new class for your response:
public class WorkRequestResponse
{
public int AssignedTo { get; set; }
}
On the page change it as follows: (I changed request to WorkRequest)
<% for (var i = 0; i < Model.WorkRequest.Count(); i++)
{ %>
<%=Html.DropDownListFor(x => x.WorkRequest[i].AssignedTo, new SelectList(Model.Vehicles, "Id", "Name", Model.WorkRequest[i].AssignedTo.Id))%>
<%}%>
On your controller bind as follows:
public ActionResult Index([Bind(Prefix = "WorkRequest")]List<WorkRequestResponse> AssignedTo)
{
// AssignedTo is now populated
WorkRequestList.WorkRequests = magic_assign_function(AssignedTo);
// manual model validation etc....
}
I would be keen to see if there is a more direct route, as this has plagued me too.
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.