Ok, I've just started learning ASP.NET MVC after being an ASP.NET developer for awhile. I think my main problem I'm having is trying to "unlearn" ASP.NET when developing my MVC projects.
Here's my question: I have a page which has some input fields. These fields are parameters to a search I'm trying to run against a database. The user checks the boxes next to the types of items they want to see and then clicks "Search". Very simple stuff.
I'm having trouble wrapping my mind around how exactly to "postback" to the page to display the results. Is it better to use jQuery and serialize the form? Do I use my Entity Framework models I've created? What's the best way to go about
I'm really excited about MVC and the control it gives me, but I need to get over these initial obstacles if I ever want to "sell" it to my boss as the way to develop all of our web apps. Thanks for reading!
If you haven't already, consider taking a look at the NerdDinner Tutorial featured in Professional ASP.NET MVC 1.0 by Rob Conery, Scott Hanselman, Phil Haack, and Scott Guthrie. It contains a great demonstration of many of the features of ASP.NET MVC including performing a search and returning the data both through a full page post and also asynchronously using JSON.
If your inputs are inside html form element (different story if javascript is involved) - you can use default model binding (it binds route values and querystring parameters too).
<form ...>
<input type="text" name="query" />
<input type="submit" .../>
</form>
on submit it will automagically bind form values (by name) to action parameters:
public ActionResult PerformSearch(string query)
{
//whatever
}
In your case - i suspect you got inputs as checkboxes. Something like this should work:
<form...>
<input type="checkbox" name="p" value="value1" />
<input type="checkbox" name="p" value="value2" />
<input type="checkbox" name="p" value="value3" />
<input type="checkbox" name="p" value="value4" />
<input type="checkbox" name="p" value="value5" />
</form>
public ActionResult PerformSearch(string[] p)
{
//whatever
}
Only - if (form method == "GET"), URL won't look nicely. :)
To show results, make a model for your view in action and just show it through view:
public ActionResult PerformSearch(string[] p)
{
var model = _searchService(p);
return View("Results", model);
}
Views/Results.aspx
<% foreach(var bar in Model){ %>
<%= bar.Name %>
<%}%>
P.s. When considering AJAX calls, always remember that you are loosing ability to show URL + search engines don't understand JS.
Related
I am new to MVC and I was wondering what's the difference between the attribute [HttpPost] and the post method inside the form
<form action="" method="post">
<input type="text" name="something" value="something" />
<input type="text" name="something" value="something"/>
<input type="submit" value="Submit" />
</form>
? Are they the same thing or... I am confused
<form action="" method="post"> </form>
Here the method="post" is not an ASP.NET MVC thing. It is normal HTML form tag specification where it tells that when the form is submitted, it will use the POST method (rather than GET where form data is submitted as querystring values). When you use POST, the form data will be part of the request BODY.
In the ASP.NET MVC world, the attribute decorator [HttpPost] tells the framework that the action method (on which we used this decorator) is for handling a request coming from the client(browser) of type "POST" (the form method). This helps the framework to read the data from the request body and use that as needed (For Model binding etc)
In short, both are different things. One is one client side and other is on your server code. But you usually use it together to build a client side form and a server side method to handle the submitted form.
In ASP.NET MVC, on [HttpPost] methods, the MVC runtime will automatically maps and transfers the data from the form fields in the front end into a View Model, based on field names.
How can I accomplish the same thing in ASP.NET WebForm?
e.g. I have an object called Person with FirstName and LastName properties.
I have a WebForm page with Textbox controls with FirstName and LastName respectively.
When pressing Submit on the form, is there a way to automatically bind FirstName and LastName to the Person object in the code-behind Button_Click event?
ASP.net 4.5 is actually going to have built in Web Forms model binding.
The Gu has a post on it and a few other things here...
http://weblogs.asp.net/scottgu/archive/2011/09/05/web-forms-model-binding-part-1-selecting-data-asp-net-vnext-series.aspx
You can do this in webforms v4.5 using model binding. It's a way we call as Ad-Hoc Model Binding where you can bind to controls without using data bound controls such as formview. I plan to blog about it but following code describes the blog in short
The following is how your markup will look.
My model has 2 properties: name and description
Name<input type="text" name="Name" value=" " id="Name" />
<br />
Description<input type="text" name="Description" value=" " id="Description" />
<br />
<asp:Button Text="Submit" runat="server" OnClick="Unnamed_Click" />
The following is the code in the button click handler.
category is my model. In this case the model binding system pulls in the value from the form value provider which looks in the form collection.
var category = new Category();
var formValueProvider = new FormValueProvider(ModelBindingExecutionContext);
TryUpdateModel(category, formValueProvider);
if (ModelState.IsValid)
{
// save changes to database
}
Perhaps the easiest way is to assign the values explicitly in the Page_Load event, whenever it is a postback. Something like this:
if (this.IsPostBack)
{
person.FirstName = FirstNameTextBox.Text;
person.LastName = LastNameTextBox.Text;
}
Or were you looking for a more declarative approach?
Take a look at Model Binder for ASP.NET Web Forms. It is doing what you want - maps postback data to class via custom attributes applied to its properties.
ASP MVC scaffolding creates Index, Create, Update, Delete views, but how practical is that in reality. There seems to be a lot of duplication of UI code with the Create, Update and Delete views. Would it not be more practical to have one view for listing and another for Add/Edit and use some switch to format the view appropriately for Adding or Editing, and allowing deletion off the listing and edit views without redirecting to another view, instead simply popping up some sort of a "Please confirm the delete..." message?
If anyone has done something like this and is willing to share some code snippets or T4 scaffolding templates for generic cases it would be greatly appreciated.
Actually the NuGet package MvcScaffolding does exactly that, using a CreateOrEdit partial view. (See here.) The add/edit views are then created by referencing the partial view (targeting a different controller action respectively):
<fieldset>
#Html.Partial("_CreateOrEdit", Model)
<input type="submit" value="Create" />
</fieldset>
Another alternative would be to use the default MVC scaffold (as defined in the model using data annotations attributes).
<fieldset>
#Html.EditorForModel()
<input type="submit" value="Create" />
</fieldset>
As far as delete, you can always add a second mini-form at the bottom of any view (or in a list):
#{ using (Html.BeginForm("Delete", "MyController", FormMethod.Post))
{
#Html.HiddenFor(model => model.id)
<input type='submit' value='Delete' />
}
}
So we have [HttpPost], which is an optional attribute. I understand this restricts the call so it can only be made by an HTTP POST request. My question is why would I want to do this?
Imagine the following:
[HttpGet]
public ActionResult Edit(int id) { ... }
[HttpPost]
public ActionResult Edit(MyEditViewModel myEditViewModel) { ... }
This wouldn't be possible unless the ActionMethodSelectorAttributes HttpGet and HttpPost where used.
This makes it really simple to create an edit view. All the action links just points right back to the controller. If the view model validates false, you just pop right back to the edit view again.
I will be bold and say this is best practice when it comes to CRUDish things in ASP.NET MVC.
EDIT:
#TheLight asked what was needed in the view to accomplish the post. It's simply just a form with method POST.
Using Razor, this would look something like this.
#using (Html.BeginForm())
{
<input type="text" placeholder="Enter email" name="email" />
<input type="submit" value="Sign Up" />
}
This renders the following HTML:
<form action="/MyController/Edit" method="post">
<input type="text" name="email" placeholder="Enter email">
<input type="submit" value="Sign Up">
</form>
When the form is submitted, it will perform an Http Post request to the controller. The action with the HttpPost attribute will handle the request.
Its so you can have multiple Actions that use the same name, you can use the HttpPost attribute to mark which method gets handled on a Post request like so:
public ActionResult ContactUs()
{
return View();
}
[HttpPost]
public ActionResult ContactUs(ContactUsModel model)
{
//do something with model
return View();
}
As far as best practices for HttpGet and HttpPost, it is good practice in any web development to use HttpPost for Creates, Updates, and Deletes (data modification). Post is good because they require a form submission, which prevents users from clicking poisoned links(e.g. [https://www.example.com/Delete/1]) in emails, social sites, etc. and changing data inadvertently. If you are basically just Reading data HttpGet works great.
See OWASP for more in-depth security considerations and why the validation token increases security.
This is mainly so that you can have two Actions with the same name,
one which is used on GETs and perhaps displays a form for user entry and the other being used on POSTs when the user submits the form displayed by the original GET. If the Actions are not differentiated in this way, an error will occur due to being unable to resolve which Action is intended to handle the request.
I thought I understood MVC until now.
To me a GET should be from a clean slate. But I discoved today MVC assumes a GET to be a POST if a page request a get from itself.
The text box should always show the text "Red" but instead it persist its last value from the previous view.
Acting like an HTTPPost. You have to uncomment ModelState.Clear to act like a HttpGet.
This appears to me to be a bug.
<form action="" method="get">
<div>
<%=Html.TextBox("search") %>
<input type="submit" value="Search" />
</div>
</form>
[HttpGet]
public ActionResult Index(string search)
{
//ModelState.Clear();
ViewData["search"] = "Red";
var items = GetYourTestData;
if (!string.IsNullOrEmpty(search))
{
var items2 = items.Where(x => x.Color == search).ToList();
return View(items2);
}
return View(items);
}
The search results returns correct and different data so it is not browser cache.
For the purpose of a search results page it does not make sense to have to redirect to another page to avoid this. That is why I chose GET thinking it should be clean each time.
Like I stated in the description. Other contents on the page does change so it is not cache. Uncomment ModelState.Clear() and all is good so that is not cache.
You can put a dynamic datetime label always showing the latest time from the server and it does change. That also proves it is not page cache.
It is a very simple test to do. Yes, just a sure as there is gravity, MVC2 framework 4.0 considers it an HTTPPost if the HttpGet requested page is the same as the requester. If you are not aware of this during programming the results could be disastrous. For instance, If someone like TurboTax uses MVC I hope that their developer are aware of that.
... ViewData["AdjustedTaxAmount"]=3435.00; ... is not going to work unless they call this
ModelState.Clear().
I don't know of why there should be ModelState on a get so
one sure fire work around is to
Inherit Controller from a base class
protected override void OnActionExecuted(ActionExecutedContext filterContext)
{
if (string.Equals(filterContext.HttpContext.Request.HttpMethod, "GET", StringComparison.OrdinalIgnoreCase))
{
ModelState.Clear();
}
base.OnActionExecuted(filterContext);
}
The browser will cache GET requests that are from the same URL. If you add a querystring variable that is random then the GET request will be different each time and the browser will not cache the result.
That's to be expected.
When you submit a form via GET, you're serializing its elements and passing those to the target via the QueryString. So, it makes sense that your search value would be part of MVC's model in the next request. When you use the Html.TextBox helper, it's going to automatically inject the model's value as the HTML input's value. That's why you're seeing the value cover over.
One solution to avoid that is to not use the HTML Helper to render the input:
<form action="" method="get">
<div>
<input type="text" name="search" />
<input type="submit" value="Search" />
</div>
</form>
Then, you should get the clean slate you're expecting after each form submission.