I've merged the create account view and the log in view in the same view. So it's a view with two forms, but they get mixed when I submit. If I try to log in and there's an error that is displayed with:
Html.ValidationSummary()
both forms get the error. And I started to rename fields to loginPassword, createPassword, because otherwise when I submit and the password is missing, it's marked as missing on both side.
What would be the way to separate these two forms so they can work indenpendently on the same view/page?
Ah yes, I've had to do exactly this before. The way I found was to set a flag in the ViewData detailing which form was posted and then I created my own extension method for ValidationSummary.
The code isn't with me right now so I'll try my best to do some air code for it now, it's obviously just an concept of how to do it so take it at face value.
To start with I would use the same setup as tvanfosson suggested with his 'EntryPageModel'.
View - note Html.MyValidationSummary
<% using(Html.BeginForm("NewAccount", "Account")) %>
<% { %>
<%= Html.MyValidationSummary("NewAccountForm") %>
<%= Html.TextBox("NewAccount.FirstName") %>
<%= Html.TextBox("NewAccount.LastName") %>
<%= Html.TextBox("NewAccount.Email") %>
<%= Html.Password("NewAccount.Password") %>
<%= Html.Password("NewAccount.ConfirmPassword") %>
<% } %>
<% using(Html.BeginForm("Login", "Account")) %>
<% { %>
<%= Html.MyValidationSummary("LoginForm") %>
<%= Html.TextBox("Login.Email") %>
<%= Html.Password("Login.Password") %>
<% } %>
Controller - note ViewData["PostedForm"]
public class Account : Controller
{
private EntryPageModel _viewModel;
public ActionResult NewAccount(FormCollection formValues)
{
try
{
//binding and validation for _viewModel.NewAccount
}
catch
{
ViewData["PostedForm"] = "NewAccountForm";
return View("RegisterAndLogin", _viewModel);
}
}
public ActionResult Login(FormCollection formValues)
{
try
{
//binding and validation for _viewModel.Login
}
catch
{
ViewData["PostedForm"] = "LoginForm";
return View("RegisterAndLogin", _viewModel); //You'll want to pass in a model
}
}
}
Custom html extension
namespace System.Web.Mvc
{
public static class HtmlExtensions
{
public static string MyValidationSummary(this HtmlHelper html, string formName)
{
if (!string.IsNullOrEmpty(html.ViewData["PostedForm"])
&& (html.ViewData["PostedForm"] == formName))
{
return html.ValidationSummary();
}
return "";
}
}
}
HTHs,
Charles
I had to deal with the same problem. I found that there is no way to separate the validation messages using the built in ValidationSummary(). Here are two suggestions:
Position the validation summary in an area where it could apply to both forms. For example, if the login and sign up forms are side by side, position the validation summary in a div centered above both forms. I found an example of this style on the Mahalo login page.
In the appropriate controller action methods, add something to the ViewData indicating which action was called. In the view there will be a ValidationSummary for each form, but each will be conditionally rendered based on what you added to the ViewData.
Either way the form fields should be uniquely named.
I went with solution #1 because I was satisfied with the way I was able to get it to look. But if you need the validation summary to appear in two different locations depending on which form was submitted, go with #2.
The input elements do need different names/ids even if they are in different forms. Unless they have different names, it will trigger the validation logic for each control since it matches based on the name of the control. I think you are on the right track by changing the names to differentiate them.
I'd set it up with a compound model, perhaps so that you could do something like (note this is incomplete):
<%= Html.TextBox( "Login.Name" ) %>
<%= Html.TextBox( "Login.Password" ) %>
<%= Html.TextBox( "NewAccount.Name" ) %>
<%= Html.TextBox( "NewAccount.Password" ) %>
<%= Html.TextBox( "NewAccount.ConfirmPassword" ) %>
On the server side, use the prefix option for the binder
public ActionResult Login( [Bind(Prefix="Login")]AccountModel model )
{
...
}
And your model would look like:
public class AccountModel
{
public string Name { get; set; }
public string Password { get; set; }
public string ConfirmPassword { get; set; }
}
public class EntryPageModel
{
public AccountModel Login { get; set; }
public AccountModel NewAccount { get; set; }
}
If the forms are posting to completely different actions, then your ModelStateDictionary should only contain the errors that were provided by the action that was invoked.
Can you post the relevant code?
I'm not sure if there is a way to split the ValidationSummary().
For your forms, you could create model classes that you would bind against, with the various fields. It wouldn't gain you much over what you've already got, though.
Related
When I display a session value in a master page (<%: Session["companyname"].ToString() %>) the following information is displayed on the page { CompanyName = TestCompany}. How do I get just the value?
Thanks for your help!
If you can show the code where the value is stored in the session, it's more likely that I could help. I would suggest, though, that you might want to reconsider using the value from the session directly in your view. It would be better, in my opinion, to have a base view model that all of your view models derive from that has the CompanyName property, and any other common properties required by your master page, on it. Your master page, then, could be strongly-typed to the base view model and you could use the values from the model. I've used this pattern with good success on a couple of projects. Couple it with a base controller where the common properties are populated for view results in OnActionExecuted(), it can be very effective in both reducing code duplication and the use of magic strings in your views.
Model:
public abstract class CommonViewModel
{
public string CompanyName { get; set; }
public string UserDisplayName { get; set; }
...
}
Controller:
public abstract class BaseController
{
public override void OnActionExecuted( ActionExecutedContext filterContext )
{
if (filterContext.Result is ViewResult)
{
var model = filterContext.ViewData.Model as CommonViewModel;
if (model != null)
{
model.CompanyName = Session["CompanyName"] as string;
model.UserDisplayName = Session["UserDisplayName"] as string;
}
}
}
}
Master Page:
<%# Master Language="C#" Inherits="System.Web.Mvc.ViewMasterPage<Foo.CommonViewModel>" %>
<!-- ... -->
<div>
<%: Model.CompanyName %>
</div>
<!-- ... -->
<div>
<%: Model.UserDisplayName %>
</div>
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.
Consider the following scenario:
Action Edit() is forwarded to Edit.aspx view to render the view.
Edit.aspx consists of textbox1 and two partial views (aka view user controls):
part1.ascx (which has textbox2, textbox3)
and part2.ascx (which has checkbox1 and checkbox2)
You want to have a strongly typed view for Edit.aspx, say, you use EditViewData class.
You also need Edit.aspx, part1.ascx and part2.ascx have access to some global information such as currentUserID, currentUserLanguage, currentUserTimezone.
Questions:
How do you go about structuring the EditViewData class?
How do you pass the view data to the view and partial views so that the object gets populated automatically when you submit the form and return to the Edit() http.post action?
What do you pass to the Edit() http.post action?
Your viewdata should look like this:
public class EditViewData
{
public int currentUserID { get; set; }
public string currentUserLanguage { get; set; }
public string currentUserTimezone { get; set; }
// ... other stuff
}
After you strongly type your aspx, you also need to strongly type your ascxs. Then in your aspx, when you call RenderPartial, just call like usual:
<% using (Html.BeginForm()) %>
<% Html.RenderPartial("part1.ascx" ); %>
<% Html.RenderPartial("part2.ascx" ); %>
<%}%>
It should automatically inherit the Model in the control. Just remember that your BeginForm should be surrounding both of your controls (ascxs).