ASP.NET MVC One Form, Two Buttons - asp.net-mvc

I have a simple form based off a model called Visitor. I'd like to have a search button by one of the id text fields so the user can click the button and have the page populate with visitor information: first name, last name, etc. In Web Forms I would do something like this:
page_load(){
person = businessManager.FindPersonById(Convert.ToInt32(txtId.Text));
txtFirstName.Text = person.FirstName;
txtLastName.Text = person.LastName;
...
}
Prior to the search button, my view form called SignIn worked just fine; posted the data to the controller and did its thing:
[HttpPost]
public ActionResult SignIn(Visitor visitor) {
if (ModelState.IsValid) {
visitorRepoistory.Add(visitor);
visitorRepoistory.Save();
return RedirectToAction("/");
} else {
return View(new VisitorFormViewModel(visitor));
}
}
But now that I have a search button placed on my view form, I'm totally lost. I don't know how to wire the search button to the controller so I can: 1.) Look up the data and 2.) Return it back to the sign in form to populate the fields. What are the steps I need to take to accomplish this?
Thanks.

This question has been duplicated many times on SO
Multiple forms in ASP.NET MVC
But to answer your question you can have multiple forms on one page and have different Actions handle the submits. That's what the link above outlines.
Specific to your case:
View
<% Html.BeginForm("Search", "<ControllerName>"); %>
Your search controls here
<% Html.EndForm(); %>
<% Html.BeginForm("SignIn", "<ControllerName>"); %>
Your signin controls here
<% Html.EndForm(); %>
Controller
[HttpPost]
public ActionResult Search(FormCollection collection)
{
Do your search and return a view
}
[HttpPost]
public ActionResult SignIn(Visitor visitor)
{
if (ModelState.IsValid) {
visitorRepoistory.Add(visitor);
visitorRepoistory.Save();
return RedirectToAction("/");
} else {
return View(new VisitorFormViewModel(visitor));
}
}

Related

Using action link to do form action and add model errors

I have a form with many fields which I post back using the standard form tag:
#using (Html.BeginForm("SubmitForm", "ControllerName", FormMethod.Post))
{
//fields and submit button
}
Also on my form I have a grid which lists contractors, and I have a a button which adds contractors to the list. The way I do it is to create the button as a submit and use the form submit action to do the work. I'm pretty sure this isn't the best way to go about it. Here's is the action on the controller:
[HttpPost]
public ActionResult SubmitForm(string btnSubmit, SupplySelectionViewModel model)
{
switch (btnSubmit)
{
case "addContractor":
if (model.ContractorId > 0)
{
//add contractor to parent record and reload whole page
return RedirectToAction("Edit", new { id = model.ProposalId });
}
else
{
ModalState.AddModelError("Select a contractor");
//Return view with error
return View("Edit", model);
}
case "save":
if (ModelState.IsValid)
{
//Save whole model and redirect back to home page
}
//Return to view with validation erros
return View("Edit", model);
}
}
As you can see I use the name of the button to decide what to do (add contractor or submit the form). What I would like is to have the add contractor as an actionlink and post to a separate action. How would I do this without passing the whole model back as I add a model error if no contractor has been selected? I know I could do the validation using jquery but let's just say I want to keep all validation server side for now.

HTTP Post request from different controller actions and ModelState

I have a weird need in an ASP.NET MVC 3 application which blocks my current progress. Here is the case:
I have a little search engine for the products and I render this search engine on multiple pages. This SE makes a HTTP POST request to product controller's search action. It fine till here.
Let's assume that I am on home controller's index action (/home/index). I make a search and check if ModelState.IsValid. As a result, it is not valid. So, I should return this back with the entered model (so that user won't lose the values) and model state errors. But when I do that I ended up with different URL (/product/search) as expected.
If I do a redirect, I lose the ModelState and cannot display error messages.
I have different solutions so far and they all look dirty. Any idea?
Edit
Here is a little project which demonstrates this:
This is the ProductController:
public class ProductController : Controller {
[HttpPost]
public ActionResult Search(SearchModel searchModel) {
if (ModelState.IsValid) {
//Do some stuff...
return RedirectToAction("Index", "SearchResult");
}
return View(searchModel);
}
}
This is the SearchModel:
public class SearchModel {
[Required]
public string ProductCategory { get; set; }
[Required]
public string ProductName { get; set; }
}
This is the *_SearchPartial*:
#model MvcApplication20.SearchModel
#using (Html.BeginForm("search", "product"))
{
#Html.EditorForModel()
<input type="submit" value="Search" />
}
And finally this is the Home controller Index action view which renders the *_SearchPartial*:
#{
ViewBag.Title = "Home Page";
}
<h2>#ViewBag.Message</h2>
#Html.Partial("_SearchPartialView")
Here, when I submit the form and if the model state fails, how should I proceed at the Product controller Search action?
Here, when I submit the form and if the model state fails, how should
I proceed at the Product controller Search action?
Normally in this case you should render the _SearchPartialView but not as a partial but as a full view with layout so that the user can fix his errors. No need to stay at Home/Index in this case:
[HttpPost]
public ActionResult Search(SearchModel searchModel) {
if (ModelState.IsValid) {
//Do some stuff...
return RedirectToAction("Index", "SearchResult");
}
// since we are returning a view instead of a partial view,
// the _SearchPartialView template should be displayed with the layout
return View("_SearchPartialView", searchModel);
}
And if you wanted to stay on the same page upon error you could use an AJAX call to perform the search. So you would AJAXify this search form and then in the success callback test the result of the Search action and based on it decide whether to refresh the partial in order to show the error or redirect to the results action using window.location.href:
something along the lines of:
$(document).on('submit', '#searchForm', function() {
$.ajax({
url: this.action,
type: this.method,
data: $(this).serialize(),
success: function(result) {
if (result.redirectTo) {
// no validation errors we can redirect now:
window.location.href = result.redirectTo;
} else {
// there were validation errors, refresh the partial to show them
$('#searchContainer').html(result);
// if you want to enable client side validation
// with jquery unobtrusive validate for this search form
// don't forget to call the .parse method here
// since we are updating the DOM dynamically and we
// need to reattach client side validators to the new elements:
// $.validator.unobtrusive.parse(result);
}
}
});
return false;
});
This obviously assumes that you have now wrapped the partial call in a div with id="searchContainer" and that you provided an id="searchForm" when generating the search form:
<div id="searchContainer">
#Html.Partial("_SearchPartialView")
</div>
and now the search action:
[HttpPost]
public ActionResult Search(SearchModel searchModel) {
if (ModelState.IsValid) {
//Do some stuff...
return Json(new { redirectTo = Url.Action("Index", "SearchResult") });
}
return PartialView("_SearchPartialView", searchModel);
}
As far as I know the ModelState is lost when doing a RedirectToAction, the solution would be to save the modelstate in the TempData one example of this, that I'm using is this:
http://weblogs.asp.net/rashid/archive/2009/04/01/asp-net-mvc-best-practices-part-1.aspx#prg
This is also discussed in various posts for instance MVC Transfer Data Between Views

passing parameters to my partial view?

I am calling my partial view like this:
<% Html.RenderPartial("~/controls/users.ascx"); %>
Can I pass parameters to partial view? How will I access them in the actual users.ascx page?
You could pass a model object to the partial (for example a list of strings):
<% Html.RenderPartial("~/controls/users.ascx", new string[] { "foo", "bar" }); %>
Then you strongly type the partial and the Model property will be of the appropriate type:
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<System.Collections.Generic.IEnumerable<string>>" %>
<% foreach (var item in Model) { %>
<div><%= Html.Encode(item) %></div>
<% } %>
There is another overload for RenderPartial that will pass your model through.
<% Html.RenderPartial("~/controls/users.ascx", modelGoesHere); %>
How to access? Just like you normally would with any view:
<%= Model.MagicSauce %>
It took a while to sink in, but MVC means you use a Model, a View, and a Controller one way or another for just about everything, including Partial Views. How all three elements fit together can be a little intimidating at first. I'd never done one until just now, and it works --Woohoo!
Hope this helps the next person.... Sorry, I'm using razor instead of .Net forms. I'm also pulling data from a SQL Server database into Entity Framework, which a developer is likely to use. I also probably went a little overboard with WebGrid, which is so much more elegant than a foreach statement. A basic #webgrid.GetHtml() will display every column and row.
Background
In this working example, users have uploaded pictures. Their pictures are displayed in their edit form using a partial view. The ImageID and FileName metadata is persisted in SQL Server while the file itself is persisted in the ~/Content/UserPictures directory.
I know it's kinda half vast, because all the details of uploading and editing personal data isn't shown. Just the germane parts of using a Partial View are focused on, albeit with some bonus EF thrown in. The namespace is MVCApp3 for S&G.
Partial View Model ViewModels.cs
The SQL Server Images table includes many more columns in addition to ImageID and FileName such as [Caption], [Description], a MD5 hash to prevent the same image being uploaded multiple times, and upload date. The ViewModel distills the Entity down to the bare minimum needed for a user to see their pictures.
public class Picts
{
public int ImageID { get; set; }
public string FileName { get; set; }
}
Main View View Edit.cshtml
Note the cast/convert to strongly type the ViewData[].
#Html.Partial(
partialViewName: "Picts",
model: (IEnumerable<MVCApp3.Models.Picts>)ViewData["Picts"]
)
If you don't set the strongly-typed model to use for the Partial View you'll get a "The model item passed into the dictionary is of type 'System.Data.Entity.DynamicProxies..." error because it assumes you're passing the parent/master model.
Partial View View Picts.cshtml (the whole file contents is shown)
#model IEnumerable<MVCApp3.Models.Picts>
#{
var pictsgrid = new WebGrid(Model);
}
#pictsgrid.GetHtml(
tableStyle: "grid",
displayHeader: false,
alternatingRowStyle: "alt",
columns: pictsgrid.Columns(
pictsgrid.Column(format:#<text><img src="#Url.Content("~/Content/Users/" + #item.FileName)" alt="#item.ImageID" width="200" />
#Html.ActionLink(linkText: "Delete", actionName: "DeletePicture", routeValues: new { id = #item.ImageID })
</text>)
))
Controller IdentityController.cs
Set the data content into the ViewData["MyPartialViewModelKeyName"] your partial view will consume. You can give the dictionary key any name you want, but I gave it ViewData["Picts"] to be consistent with the partial view file name and its view model class definition.
Because the pictures may be shared amongst multiple users there is a many-to-many table with a corresponding PITA query in Entity Framework using nested froms and inner joins to return just the pictures belonging to, or shared with, a user:
public class IdentityController : Controller
{
private EzPL8Entities db = new EzPL8Entities();
// GET: /Identity/Edit/5
[Authorize]
public ActionResult Edit(int? id)
{
if (id == null)
return new HttpNotFoundResult("This doesn't exist");
// get main form data
ezpl8_UsersPhysicalIdentity ezIDobj = db.ezpl8_UsersPhysicalIdentity.Find(id)
// http://learnentityframework.com/LearnEntityFramework/tutorials/many-to-many-relationships-in-the-entity-data-model/
// get partial form data for just this user's pictures
ViewData["Picts"] = (from user in db.ezpl8_Users
from ui in user.ezpl8_Images
join image in db.ezpl8_Images
on ui.ImageID equals image.ImageID
where user.ezpl8_UserID == id
select new Picts
{
FileName = image.FileName,
ImageID = image.ImageID
}
).ToList();
return View(ezIDobj);
}
// Here's the Partial View Controller --not much to it!
public ViewResult Picts(int id)
{
return View(ViewData["Picts"]);
}
[Authorize] //you have to at least be logged on
public ActionResult DeletePicture(int id)
{
//ToDo: better security so a user can't delete another user's picture
// TempData["ezpl8_UserID"]
ezpl8_Images i = db.ezpl8_Images.Find(id);
if (i != null)
{
var path = System.IO.Path.Combine(Server.MapPath("~/Content/Users"), i.FileName);
System.IO.File.Delete(path: path);
db.ezpl8_Images.Remove(i);
db.SaveChanges();
}
return Redirect(Request.UrlReferrer.ToString());
}
protected override void Dispose(bool disposing)
{
db.Dispose();
base.Dispose(disposing);
}
}
// get main form data
ezpl8_UsersPhysicalIdentity ezIDobj = db.ezpl8_UsersPhysicalIdentity.Find(id)
// http://learnentityframework.com/LearnEntityFramework/tutorials/many-to-many-relationships-in-the-entity-data-model/
// get partial form data for just this user's pictures
ViewData["Picts"] = (from user in db.ezpl8_Users
from ui in user.ezpl8_Images
join image in db.ezpl8_Images
on ui.ImageID equals image.ImageID
where user.ezpl8_UserID == id
select new Picts
{
FileName = image.FileName,
ImageID = image.ImageID
}
).ToList();
return View(ezIDobj);
}
// Here's the Partial View Controller --not much to it!
public ViewResult Picts(int id)
{
return View(ViewData["Picts"]);
}
[Authorize] //you have to at least be logged on
public ActionResult DeletePicture(int id)
{
//ToDo: better security so a user can't delete another user's picture
// TempData["ezpl8_UserID"]
ezpl8_Images i = db.ezpl8_Images.Find(id);
if (i != null)
{
var path = System.IO.Path.Combine(Server.MapPath("~/Content/Users"), i.FileName);
System.IO.File.Delete(path: path);
db.ezpl8_Images.Remove(i);
db.SaveChanges();
}
return Redirect(Request.UrlReferrer.ToString());
}
protected override void Dispose(bool disposing)
{
db.Dispose();
base.Dispose(disposing);
}
}

asp.net-mvc / linq to sql - do i always need an HTML.TextBox to do a Edit Save?

I have a UserController and an Edit.aspx. There is one field that is my primary key so i dont want to allow users to edit this field.
The issue is that if i remove the
<%= Html.TextBox("Email", Model.Email) %>
then when the asp.net-mvc magic calls my Controller code:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Edit(int id, tblMailingList user_)
{
try
{
repo.UpdateUser(user_);
return RedirectToAction("Index");
}
catch
{
return View();
the email field of the tblMailingList is null. The issue is that i need this as the lookup in the table to retrieve the current record and obviously if its null i get an exception.
When i put the textbox back for this field, it works fine. It seems crazy that i would have to have a textbox and allow editing to pass this field over to the controller. i tried putting it in a label and it still shows up as null in the controller.
any suggestions?
My first question would be why are you doing the lookup on the Email field and not the Id field?
You can pass parameters in your Form declaration to be passed through to your Controller.
<% using (Html.BeginForm(
"MethodName", "Controller", FormMethod.Post,
new { id = Model.Id, email = Model.Email)) { %>
I'm not sure if I got the method declaration correct so please check.
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Edit(int id, string email, tblMailingList user_)
{
try
{
repo.UpdateUser(user_);
return RedirectToAction("Index");
}
catch
{
return View();
I would recommend updating slightly differently as your tblMailingList user will not be valid to be updated in your Repository.
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Edit(int id, FormCollection form)
{
tblMailingList user = repo.GetUser(id); // get the user using the id
// so we can update in the same
// context
UpdateModel(user); // this will automatically update
// your user model with values
// from the form
try
{
repo.UpdateUser(user);
return RedirectToAction("Index");
}
catch
{
return View();
If you just want a field that could passed to controller which needs to be invisible in the form, Html.HiddenField could work for your case.
Do I get wrong?

ASP.Net MVC have An Action render another Action

I have two pages I need, and want to show for the url /index and /review. The only difference between the two pages is on the review I will have a review comment section to show and Submit button. Otherwise the two pages are identical. I thought I could create a user control for the main content.
However, if I could say under the Review action, flag to show review stuff and them return the rest of the index action.
How would you (generic you) do this?
Model example
public class MyModel
{
public bool ShowCommentsSection { get; set; }
}
Controller actions
public ActionResult Index()
{
var myModel = new MyModel();
//Note: ShowCommentsSection (and the bool type) is false by default.
return View(myModel);
}
public ActionResult Review()
{
var myModel = new MyModel
{
ShowCommentsSection = true
};
//Note that we are telling the view engine to return the Index view
return View("Index", myModel);
}
View (somewhere inside your index.aspx probably)
<% if(Model.ShowCommentsSection) { %>
<% Html.RenderPartial("Reviews/ReviewPartial", Model); %>
<% } %>
Or, if Razor is your cup of tea:
#if(Model.ShowCommentsSection) {
Html.RenderPartial("Reviews/ReviewPartial", Model);
}
// ... Make comment section visible
return Index();
Why don't you just always include the review comment form and use client side code to show or hide it. Considering no additional data is required for the review page beyond that of which already needed on the index a round trip to a controller is not necessary. This would allow you to delete the review action and view.

Resources