ASP.NET MVC2: Getting textbox data from a view to a controller - asp.net-mvc-2

I'm having difficulty getting data from a textbox into a Controller. I've read about a few ways to accomplish this in Sanderson's book, Pro ASP.NET MVC Framework, but haven't had any success.
Also, I've ran across a few similiar questions online, but haven't had any success there either. Seems like I'm missing something rather fundamental.
Currently, I'm trying to use the action method parameters approach. Can someone point out where I'm going wrong or provide a simple example? Thanks in advance!
Using Visual Studio 2008, ASP.NET MVC2 and C#:
What I would like to do is take the data entered in the "Investigator" textbox and use it to filter investigators in the controller. I plan on doing this in the List method (which is already functional), however, I'm using the SearchResults method for debugging.
Here's the textbox code from my view, SearchDetails:
<h2>Search Details</h2>
<% using (Html.BeginForm()) { %>
<fieldset>
<%= Html.ValidationSummary() %>
<h4>Investigator</h4>
<p>
<%=Html.TextBox("Investigator")%>
<%= Html.ActionLink("Search", "SearchResults")%>
</p>
</fieldset>
<% } %>
Here is the code from my controller, InvestigatorsController:
private IInvestigatorsRepository investigatorsRepository;
public InvestigatorsController(IInvestigatorsRepository investigatorsRepository)
{
//IoC:
this.investigatorsRepository = investigatorsRepository;
}
public ActionResult List()
{
return View(investigatorsRepository.Investigators.ToList());
}
public ActionResult SearchDetails()
{
return View();
}
public ActionResult SearchResults(SearchCriteria search)
{
string test = search.Investigator;
return View();
}
I have an Investigator class:
[Table(Name = "INVESTIGATOR")]
public class Investigator
{
[Column(IsPrimaryKey = true, IsDbGenerated = false, AutoSync=AutoSync.OnInsert)]
public string INVESTID { get; set; }
[Column] public string INVEST_FNAME { get; set; }
[Column] public string INVEST_MNAME { get; set; }
[Column] public string INVEST_LNAME { get; set; }
}
and created a SearchCriteria class to see if I could get MVC to push the search criteria data to it and grab it in the controller:
public class SearchCriteria
{
public string Investigator { get; set; }
}
}
I'm not sure if project layout has anything to do with this either, but I'm using the 3 project approach suggested by Sanderson: DomainModel, Tests, and WebUI. The Investigator and SearcCriteria classes are in the DomainModel project and the other items mentioned here are in the WebUI project.
Thanks again for any hints, tips, or simple examples!
Mike

try strongly typing the page to use SearchCriteria to autopost the data like that ex:
public partial class Search: ViewPage<SearchDetails>

This should do it for you (unable to verify this is perfect - typed this from memory):
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult SearchDetails(FormCollection formValues)
{
var txtContents = formValues["Investigator"];
// do stuff with txtContents
return View();
}

1.) Have you looked into ViewModels for your View? In essence that is what your SearchCriteria class is. Make sure you strongly type your view with that model:
<%# Page Title="" Language="C#" MasterPageFile="~/Views/Shared/MyMaster.Master" Inherits="System.Web.Mvc.ViewPage<SearchCritieria>"
Also make sure that you use the HtmlHelper.TextBoxFor method to map this Investigator property to the SearchCritiera model. On Post back your text box value should be there:
'<%=Html.TextBoxFor(model => model.Invesigator)%>'
Good luck!
Also here is a great reference on using ViewModels that I have looked at a lot recently:
http://geekswithblogs.net/michelotti/archive/2009/10/25/asp.net-mvc-view-model-patterns.aspx

Thanks for the tips everyone. For learning purposes, I need to go back and follow the strongly typed route. I'm curious if I would have run into this problem if I would have done that from the beginning.
Until then, the following worked:
Use a submit button
Use this code for the form:
<% using(Html.BeginForm(new { Action = "SearchResults"})) { %> <% } >
Thanks again for you help!
Mike

Related

How to remote validation with Asp.Net MVC 2

I Create this class.
public class UniqueFileNumber : ValidationAttribute
{
private string _LocationFile;
public override string FormatErrorMessage(string str)
{
return ViewRes.ValidationString.Loc_FileNumberExist;
}
public override bool IsValid(object value)
{
DBEntities _db = EntityFactory.GetEntity();
string strName = Convert.ToString(value);
return !_db.Locations.Any(p => p.LocationFile == strName);
}
}
and add this attribute to my entity Like that.
[UniqueFileNumber]
public object FileNumber{ get; set; }
The validation work only on the PostBack (Refresh).
It would be fine if it's work on client side too. In my client side , I add that line
<% Html.EnableClientValidation(); %>
What's the problem here.
thanks.
You have to give it the javascript fonction to use for validation...
check out this post from Phil Haacked ASP.NET MVC 2 Custom Validation
A better example for what you have to do since you still need to go on the server, check
this post from Brad Wilson Remote Validation with ASP.NET MVC 2
One more Remote Validation ASP.NET MVC 2 based on Brad Wilson idea, but used jQuery ajax calls.

Does renderaction calls its corresponding httpPost Action on submit

I'm a little new to asp.net mvc and I have a question (very basic). I have hacked around but I am not totally sure about this and I could'nt find anything particularly helpful.
Assume that I have 2 controllers A and B and 2 views FullView and PartView
public class AController:...
{
//Renders FullView
public ActionResult Create
{
....
}
[HttpPost]
public ActionResult Create
{
....
}
}
public class BController:...
{
//Renders an Arbitrary partial View (PartView)
public ActionResult Create
{
....
}
//Saves the data of the partial View
[HttpPost]
public ActionResult Create
{
....
}
}
the 1st view (FullView) has the code
<%Html.RenderAction("Create", "B"); %>
my question is on submit will BController's action ([HttpPost] Create) run?
Thank you
That depends on what action you specify in your <form /> tag. This doesn't have anything to do with asp.net mvc. If you use Html.BeginForm() without parameters it will post to the current url (not the create action on BController).
Well 1st thing you could do is toggle some breakpoints in your actions and hit f5.
Second - what action is called purely depends on what url you hit with what http method.
But for your case, when you post form A and controller A processes post you might get into validation problems and that's when you return View() on a post action and that's why form B is rendered via its post method.

MVC2 Checkboxes: Still having problems; Or: What does isSelected got to do with it?

In the ever increasingly epic failure that are my Create and Edit forms, I'm still having problems generating checkboxes via Html.Checkbox(). I'm not sure if I should just manually write the HTML at this point.
I have a view model:
public class AdminGameReviewViewModel
{
public Game GameData { get; set; }
public List<Genre> AllGenres { get; set; }
public List<PlatformListing> AllPlatforms { get; set; }
}
And a helper model:
public class PlatformListing
{
public Platform Platform { get; set; }
public bool IsSelected { get; set; }
}
And, I'm trying to generate the checkboxes like so:
<%: Html.Label("Platforms") %><br />
<% Model.AllPlatforms.ForEach(p => Html.Encode(Html.CheckBox("PlatformIDs", p.IsSelected, new { value = p.Platform.PlatformID }))); %>
But, they're not displaying at all.
I'm curious as to why I need to supply a boolean for the checkboxes themselves. I'd think it would simply send back checked values, as an array or list similar to PHP's $_POST. So, I'm not sure if I'm implementing my boolean property correctly, in addition to whatever other brain dead errors I'm committing. The boolean is forcing me to bury the data I want to display/bind further than I'd like, and it's giving me problems 'seeing' what I'm doing wrong.
I keep getting the feeling I'm looking too deeply at my problem, and trying to make the solution more complicated than it should be. Given my inexperience with MVC2/C#, I'm just not sure where to go from here. It doesn't help that this seems to be the one part of form handling that's either glossed over or completely ignored by most demos and tutorials.
So, again, any help would be greatly appreciated.
The problem is you're not writing anything to the Response stream.
Html.Checkbox() returns a string, it doesn't write directly to the response object (nor does Html.Encode() - that just escapes reserved characters and returns the result string). On top of all that, the ForEach extension method only executes an action, it doesn't return a value.
So you have to ditch the ForEach extension, and use the <%= %> or <%: %> code nuggets:
<% foreach (var p in Model.AllPlatforms) { %>
<%= Html.CheckBox("PlatformIDs", p.IsSelected, new { value = p.Platform.PlatformID }) %>
<% } %>

Where and how to load dropdownlists used in masterpage

I'm new to MVC!
I am trying to use two DropDownLists (Cities, Categories) in a PartialView that will be used in MasterPage, meaning they will be visble all the time.
I tried to load them in HomeCOntroller, but that didn't work. I got an Exception.
I read something about making a baseController that the other controllers will inherit from, I have tried that, kind of, but I guess i'm doing something wrong.
This is the only code I got today:
Masterpage
<% Html.RenderPartial("SearchForm"); %>
PartialView (SearchForm.ascx)
<% using (Html.BeginForm("Search", "Search")) { %>
<% } %> // dont know why I need two BeginForms, if I dont have this the other form won't trigger at all! Weird!
<% using (Html.BeginForm("Search", "Search", FormMethod.Get)) { %>
<%= Html.DropDownList("SearchForm.Category", new SelectList(ViewData["Categories"] as IEnumerable, "ID", "Name", "--All categories--")) %>
<%= Html.DropDownList("Search.City", Model.Cities, "--All cities--") %>
<input name="search" type="text" size="16" id="search" />
<input type="submit" id="test" title="Search" />
<% } %>
Two question:
Where and how to load the DropDownLists is the problem. I have tried to load it in the HomeController, but when go to another page then it says that the DDLs is empty and I get a Excecption.
Why do I have to use two forms for the ActionMethod to trigger ?
Hope anyone can help me out!
It sounds like you're only setting the property for a single action result. The Model.Cities data will have to be populated for every single view that needs to use it.
One solution would be to move the population of it to an ActionFilter
public class CityListAttribute : ActionFilterAttribute
{
public override void OnActionExecuted(ActionExecutedContext filterContext) {
var result = filterContext.Result as ViewResult;
result.ViewData.Model = //populate model
base.OnActionExecuted(filterContext);
}
}
and then add the filter to your controller
[CityList]
public class HomeController : Controller {
public ActionResult Index() {
return View();
}
}
As for the two forms issue, there should be no reason that i can think of that you need an empty form.
Take a look at the html that's being output and make sure it's ok. Also check the action is being generated correcly
Better way to do this, is to create something like MasterController and have action method on it like this:
[ChildActionOnly]
public ActionResult SearchForm()
{
//Get city data, category data etc., create SearchFormModel
return PartialView(model);
}
I recommend you create strongly typed view (SearchForms.ascx of type ViewUserControl<SearchFormModel>). Also it may be a good idea to have a model like this:
public class SearchViewModel
{
public IList<SelectListItem> Cities { get; set; }
public IList<SelectListItem> Categories { get; set; }
}
and use a helper like this: http://github.com/Necroskillz/NecroNetToolkit/blob/master/Source/NecroNet.Toolkit/Mvc/SelectHelper.cs to convert raw data to DDL friendly format beforehand.
In any case, you now use Html.RenderAction() instead of Html.RenderPartial() and specify you want "SearchForm" action from "MasterController".

ASP.MVC 2 RTM + ModelState Error at Id property

I have this classes:
public class GroupMetadata
{
[HiddenInput(DisplayValue = false)]
public int Id { get; set; }
[Required]
public string Name { get; set; }
}
[MetadataType(typeof(GrupoMetadata))]
public partial class Group
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
}
And this action:
[HttpPost]
public ActionResult Edit(Group group)
{
if (ModelState.IsValid)
{
// Logic to save
return RedirectToAction("Index");
}
return View(group);
}
That's my view:
<%# Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<Group>" %>
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
<% using (Html.BeginForm()) {%>
<fieldset>
<%= Html.EditorForModel() %>
<p>
<input type="submit" value="Save" />
</p>
</fieldset>
<% } %>
<div>
<%=Html.ActionLink("Back", "Index") %>
</div>
</asp:Content>
But ModelState is always invalid! As I can see, for MVC validation 0 is invalid, but for me is valid.
How can I fix it since, I didn't put any kind of validation in Id property?
UPDATE:
I don't know how or why, but renaming Id, in my case to PK, solves this problem.
Do you know if this an issue in my logic/configuration or is an bug or expected behavior?
Just before if (ModelState.IsValid) remove Id index using this line ModelState.Remove("Id") this way when MVC team remove this bug, you just need to remove this line code of your projects.
You have a required field, Id. It's required because it's non-nullable. You must either (1) submit it with your form or (2) change your model to make Id nullable or (3) use a different type.
I can confirm that removing the id index using ModelState.Remove("Id") works.
Could anyone give an elaborate explanation for this alleged "bug"?
Maybe someone from the Microsoft MVC Team could give an explanation?
There are no bugs like this when using the ADO.NET Entity Data Model as a data source - only on Linq To SQL.
Your problem is that you are using a property named 'id' in your model. Try using a different name and you will see it works. Mvc seems to have a problem with that.
I experienced the exact same issue.
One other reason can be:
If you declare your key as some type other than a number e.g String, then since by default it can't auto generate the keys for you, you'll face this error.