ASP MVC: Submitting a form with nested user controls - asp.net

I'm fairly new to ASP MVC so go easy :).
I have a form that contains a number of user controls (partial views, as in System.Web.Mvc.ViewUserControl), each with their own view models, and some of those user controls have nested user controls within them. I intended to reuse these user controls so I built up the form using a hierarchy in this way and pass the form a parent view model that contains all the user controls' view models within it.
For example:
Parent Page (with form and ParentViewModel)
-->ChildControl1 (uses ViewModel1 which is passed from ParentViewModel.ViewModel1 property)
-->ChildControl2 (uses ViewModel2 which is passed from ParentViewModel.ViewModel2 property)
-->ChildControl3 (uses ViewModel3 which is passed from ViewModel2.ViewModel3 property)
I hope this makes sense...
My question is how do I retrieve the view data when the form is submitted? It seems the view data cannot bind to the ParentViewModel:
public string Save(ParentViewModel viewData)...
as viewData.ViewModel1 and viewData.ViewModel2 are always null. Is there a way I can perform a custom binding?
Ultimately I need the form to be able to cope with a dynamic number of user controls and perform an asynchronous submission without postback. I'll cross those bridges when I come to them but I mention it now so any answer won't preclude this functionality.
Many thanks.

For databinding to work you need to give special names to your text fields. For example in ChildControl1:
<%= Html.TextBox("ViewModel1.Property1", Model.Property1) %>
This will generate the following markup:
<input type="text" name="ViewModel1.Property1" id="ViewModel1_Property1" value="" />
And when you post to the following action:
public ActionResult Save(ParentViewModel viewData)
{
...
}
It will update the value of viewData.ViewData1.Property1. For binding collections you may take a look at this article.

Related

ASP.NET MVC3: Add element from list into a model's collection, from "Edit" view

I have a User model, which contains a collection of Devices. Users may have any number of Devices, but each Device may only have one User.
I am making a page for editing Users. When the User's Devices collection needs to be edited, the view presents a table of Devices associated with the User, with Remove links next to each, and a table of Devices not currently associated with a User, with Add links. The view is typed to a viewmodel that contains both lists.
This is all within a partial view, so that (ideally) it can update dynamically with Ajax.
Unfortunately I am quite new to ASP.NET and MVC3, and to web development in general. How should I go about writing the view to implement this?
Most of my assumptions about how to implement this are based on this tutorial. He's doing something similar, but he's just using #Html.EditorFor and adding whatever the user types in the text field into the collection, not selecting an existing object. Presumably I need to send the ID of the selected device back to the controller, so it can move the device between lists and return the updated partial view. But the view is typed to the viewmodel, and (unless I'm mistaken) I can't send arbitrary data back to the controller in a post; I can only send the viewmodel. So it would seem I need to put the selected device's ID into the viewmodel before posting it back, but if I do that then I'm violating the whole MVC pattern by manipulating data in the view.
This is a pretty rudimentary problem so I'm sure there are plenty of solutions, but unfortunately I lack enough domain knowledge to even know how to look for one.
You can send additional data to the controller (not only the viewmodel), for example, for the list of devices you can create checkboxes for each item, each checkbox must have the same name and a different value, something like this:
<input type="checkbox" name="devices" value="dev1" />
<input type="checkbox" name="devices" value="dev2" />
<input type="checkbox" name="devices" value="dev3" />
In your controller the method can receive a list of data...
public ActionResult UpdateUser(User usermodel,string[] devices){ ... }
Parameter devices contains a list of devices ids selected when the user submits the form (assuming that checkboxes are inside a form and the user submit it).
This is a way to do it, surely there are other ways.

Getting form field names on Submit when form is user control

I am slowly, piece by piece learning what I am doing with ASP.NET
I've created the beginnings of my new web application, but I am often coming up against the issue that ASP.NET renames elements IDs or in the case of form fields, their names.
I have a form which is basically a sales system. It is essentially made up of two User Controls, one is a form for Customer Details (name, address etc) and the second is a form for the customer's purchases, it consists of a number lines dynamically created by Javascript created as you list the items the customer is purchasing.
Both these sections are User Controls because they are to be used for other areas of the system where this data will need to be recalled/re-entered.
When the USer Control is loaded, the field which contains the Customers' Name is renamed "m$mainContent$customerForm$name" I understand where this comes from, "m" is the ID of my Master Page, "mainContent" is the main Content Placeholder and "customerForm" is the name of the User Control.
In fact, in my case, this will always remain the same on all forms, so it is relative easy to overcome... but... suppose it wasn't
I can see there are ways I could deal with this with Javascript, but the form doesn't need an AJAX submit, a normal Post will do fine for this, so when I open up the recieving page I want to call Request.Form("name")% to save the customer's name into the database, but of course I really need Request.Form("m$mainContent$customerForm$name")%
How would I dynamically extract those prefixes from the posting form to ensure that if I rename anything or use it in a different scenario, the problem will not break it?
I am using .NET 2.0, so can't use Static Client.
This is really old but let's see if it's useful for you.
Getting this example I managed to get the value from a given field name without prefix.
I'm sure it is not the best solution, but it was useful for me. Let's say you want to recover the value of a field named "hfIdAviso", that comes in the POST as "ctl00$body$hfIdAviso":
var value = Request.Form.Cast<string>()
.Where(key => key.EndsWith("hfIdAviso"))
.ToDictionary(key => key, key => Request.Form[key])
.Values.FirstOrDefault();
That will return the value for that field.
Hope it helps.
If you include runat="server" in the declaration of your user control in the ASPX page, then you can access this control by just using the ID value of the control.
For example
In the ASPX:
<uc:MyUserControl ID="mycontrol1" runat="server" />
In the code behind (in C# syntax):
object foobar = mycontrol1.SelectedValue;
suppose you have a form inside user control that has a TextBox field with ID="txtfname".You have registered your user control in page1 and you want to POST form data to page2 :
in page1 you have user control:
<My:WebUserControl1 ID="myuc" runat="server" />
in page2 codebehind , you can get the value of user control form field (txtfname) this way :
string firstName = Request.Form["myuc$txtfname"];

how to display using asp.net mvc

I have a partial view called StudentInfo.ascx. In it I have something like this:
<fieldset>
<legend>Students Information</legend>
<div>
<label>First Name:</label>
<label>Last Name:</label>
</div>
</fieldset>
I have another partial view called Detail.ascx which will need to display this StudentInfo.ascx. I did something like this:
<%= Html.RenderAction("StudentInfo"); %>
Is that right?
It depends on what you intend to do and if you use strong type views etc... By the info you provided it would be better to use RenderPartial() but your partial view doesn't make any sense the way it is. It lacks significant details.
RenderPartial vs. RenderAction
If you use Html.RenderPartial(), it will work faster, but you will have to provide the data for your partial views in a single controller action that returns the parent (partial)view.
If you use Html.RenderAction() it will work slower, but it gives you more flexibility and decoupling from the parent (partial)view, because data for a particular child partial view will be provided by always the same controller action that's completely independent of parent (partial)view and controller action that returned it.
How to choose
Both are correct. It all depends on the problem at hand.
If your partial views are not strongly typed and/or they are meant for posting you will most probably display them using RenderPartial() extension method.
But if they do consume some data and they are strongly typed and they are used on multiple completely different views it's probably much better to use RenderAction().
Think of RenderPartial() as a placeholder that just renders some view and RenderAction() as a separate MVC request pipeline. So before partial view gets rendered the whole request pipline is executed:
request gets issued
controller action gets invoked, prepares data and
returns a partial view (that will be placed inside some other parent (partial)view)
So much more overhead.
Try
<% Html.RenderPartial("StudentInfo") %>

ASP MVC form fields wrongly getting filled in

I have an ASP MVC form which updates a business object and then its action goes to another page.
Both of these pages have a textarea with an id "Description", when it goes to the other page, for reasons unknown it fills in the values which were entered before the form submission.
Which method are you using to update your object? Have you tried adding the object type name in the ID?
Like, Product.Description instead of just Description.
Not knowing what your doing exactly, I'd say there is a 'Description' property on the ViewData's Model. ASP.NET MVC will try to match up your form values with values from the ViewData, including the Model. You can rename the control or reference the object directly as Chad said. eg Product.Description
With more details, someone may be able to help more.
Without seeing your controller actions this is a bit of guess work but how does it go to another page?
If in the post controller you return a different view (or return another action that returns a view etc) it may be that the ModelState is using the attempted values from the previous form submission, this is the expected behaviour (it's how the validation system and model binding works for a start). If you are sending them to a different form use a RedirectResult to your next action and use TempData if needed for any state you need to keep while transitioning.
If at all possible I'd also recommend using the strongly typed helpers such as Html.TextAreaFor(x=>x.Description)
<p><label>Description</label><br /><br /><%=Html.TextArea("Description", new { style = "width:470px;" })%></p>
For clarification, I'm going from an "Edit project" page, to a "Add issue" page

How do you use usercontrols in asp.net mvc that display an "island" of data?

I am trying to find out how to use usercontrols in asp.net mvc. I know how to add a usercontrol to a view and how to pass data to it. What I haven't been able to figure out is how do you do this without having to retrieve and pass the data in every single controller?
For example, if I have a user control that displays the most recent posts on several but not all the pages in the site, how do I write the Controllers so that I get data for that usercontrol and pass it to the user control from only one place in the web site instead of getting and passing data in each of the different controllers that the user control is used in?
I'm not sure if this makes sense or not. Is there a better or recommended way to handle an "island" of data that you want to display on several pages?
I'm coming from web forms where I could just write a user control that got its own data and displayed data independently from the rest of whatever page it is used on.
There are multiple ways to do it.
The basic approach is
Populate the data for the view in the BaseController (OnActionExecuting event)
Writing a custom action filter
Writing an Application Controller (the eg. is in the below links).
An example of OnActionExecuting will be
[HandleError]
public class BaseController : Controller
{
CourseService cs = new CourseService();
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
List<Tag> tags = cs.GetTags();
ViewData["Tags"] = tags;
}
}
You can use the "tags" view data on any view. This is just an example of usercontrol being rendered as side content.
<div id="sidebar_b">
<asp:ContentPlaceHolder ID="ContentReferenceB" runat="server" >
<% Html.RenderPartial("Tags"); %>
</asp:ContentPlaceHolder>
</div>
I found the following URL to be useful.
http://weblogs.asp.net/stephenwalther/archive/2008/08/12/asp-net-mvc-tip-31-passing-data-to-master-pages-and-user-controls.aspx
http://blog.matthidinger.com/2008/02/21/ASPNETMVCUserControlsStartToFinish.aspx
http://www.aaronlerch.com/blog/2008/01/26/displaying-foo-on-every-page-of-an-aspnet-mvc-application/
http://blog.wekeroad.com/2008/01/07/aspnet-mvc-using-usercontrols-usefully/
In the MVC Futures, available on codeplex , contains the RenderAction HtmlHelper extensions. This will allow you to create a controller for the ueser control and this controller will populate the ViewData used by the user control without having to resort to a base controller as was suggested.
In the View you would do
<% Html.RenderAction("Index", "UserControlController") %>
or one of the other overloads.
This will create an instance of the controller, execute the method and render the user control view into the main view. The main view controller does not need to know anything about the user control or its model/data.
Refactor the code that obtains the view data for this user control into it's own method, maybe even it's own model (class). Call this method from each controller that needs to populate the control and pass the results in the ViewData with a well-known key. You might even want to pass the type of the current controller to your method in case it needs to know what data to retrieve based on the base model for the controller.
ViewData["RecentPosts"] = RecentPosts.GetRecentPosts( this.GetType() );
In your control, retrieve the data using the well-known key.
How to Handle "Side Content" in ASP.NET MVC

Resources