I created a application in MVC2 by choosing (ASP.Net MVC 2 Web Application). This provided some Home/About Controllers/Models/Views.
I additionally created a Model with the Name of Index like below...
namespace MvcApplication1.Models
{
public class Index
{
[DataType(DataType.Text)]
public String Name { get; set; }
}
}
Following is my Index View
<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
Index
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
<% using (Html.BeginForm())
{%>
<%:Html.TextBoxFor(x=> x.Name) %>
<input type="submit" name="Click here" />
<%} %>
</asp:Content>
Following is my Controller
[HttpPost]
public ActionResult Index(Index Model)
{
ViewData["Message"] = "Welcome to ASP.NET MVC!";
return View();
}
Question
When I keep the Index controller like below. and If I click the submit button. This is clearing the TextBox COntrols. Like below
public ActionResult Index()
{
ViewData["Message"] = "Welcome to ASP.NET MVC!";
return View();
}
TextBox will not be cleared in case in incorporating the Model as Parameter in the Action method...
What's the reason for this behaviour ?
MVC doesn't maintain state between postbacks like WebForms does.
Fields are repopulated from values in the ModelState which are only added there if they are seen by the modelbinder on postback (and potentially only if there is a validation error?). Honestly i would almost prefer if it didn't do it automatically. However if you postback an invalid value (eg a string to an integer field) you need an somewhere which can store the invalid value so it can be repopulated along with a validation error.
Other than that automatic method, you need to manually pass the model back to the view for it to be populated
[HttpPost]
public ActionResult Index(Index Model)
{
ViewData["Message"] = "Welcome to ASP.NET MVC!";
return View(Model);
}
Your controller should look like this for the user input to persit in the view after the submit button is clicked '
public ActionResult Index( )
{
ViewData["Message"] = "Welcome to ASP.NET MVC!";
var model = new Index();
return View( model );
}
[HttpPost]
public ActionResult Index(Index model )
{
return View(model);
}
Related
I'd like to ask a very simple question from a rookie. I want to pass data from view Alpha.cshtml, into controller HomeController.cs with action Beta(), and then display this data in view Beta.cshtml.
Here's my Alpha.cshtml:
#using (Html.BeginForm("Beta", "Home", null, FormMethod.Post, null))
{
#Html.TextBox("data")
<input type="submit" value="Submit" />
}
Here's my Beta.cshtml:
<p>The submitted value is: #ViewBag.Data</p>
And here's my Beta() action:
public ActionResult Beta()
{
ViewBag.Data = ???
return View();
}
What do I put in place of the ???
Thanks!
When the form is submitted, all form input fields will be posted to the server. ModelBinding will take care of reading the posted values and supplying them to your action method in the list of parameters.
[HttpPost]
public ActionResult Beta(string data)
{
ViewBag.Data = data
return View();
}
I have a controller:
public class LanguageController : Controller
{
[HttpGet]
public ActionResult Index()
{
// populate viewModel from database
return PartialView(viewModel)
}
[HttpPost]
public ActionResult Index(string language)
{
LanguageCookie.Write(Response, language);
return RedirectToAction(ACTION, CONTROLLER, new {culture = language});
}
}
and its partial view:
#model MyModel
#using (Html.BeginForm("Index", "Language"))
{
#Html.DropDownList(
Model.SelectedLanguageShortName,
Model.AllLanguages
)
<input type="submit" value="Select" />
}
which I render in _Layout.cshtml:
<div>
#Html.Action("Index", "Language")
</div>
Please let me know how can I get ACTION/CONTROLLER names of main (not partial) controller, from my LanguageController was called. I need this information on the postback where I set cookie and want to redirect user on the same page but with prefered language.
I have found this example:
var rd = ControllerContext.ParentActionViewContext.RouteData;
var currentAction = rd.GetRequiredString("action");
var currentController = rd.GetRequiredString("controller");
But ControllerContext.ParentActionViewContext is null in the postback. I am able to get what I need in the view but it is ugly:
#Html.Hidden("Controller", HttpContext.Current.Request.RequestContext.RouteData.Values["controller"].ToString());
#Html.Hidden("Action", HttpContext.Current.Request.RequestContext.RouteData.Values["action"].ToString());
How to get the same information in the controller scope?
When Index(string language) is processed ParentActionViewContext is null because this is another request to server and it doesn't know anything about previous request that invoked child action.
Instead of storing control and action in hidden field you can store the whole address and invoke Redirect:
#model MyModel
#using (Html.BeginForm("Index", "Language", new { redirectUrl = Request.Url }))
{
#Html.DropDownList(
Model.SelectedLanguageShortName,
Model.AllLanguages
)
<input type="submit" value="Select" />
}
and then
[HttpPost]
public ActionResult Index(string language, string redirectTo)
{
LanguageCookie.Write(Response, language);
return Redirect(redirectTo);
}
Another way is to save CONTROLER and ACTION in TempData, but in this way you can have problem if somebody open multiple pages of your site.
Third solution is to invoke that method with Ajax and when response arrive reload the page with javascript.
I am just trying to populate a html.dropdown list using mvc2 in VS2008.
But the control is not displayed at all.
Here is my code
public ActionResult Index()
{
ViewData["Time"] = DateTime.Now.ToString();
var mdl = new List<SelectListItem>();
mdl.Add(new SelectListItem
{
Value = "1",
Text = "Module One"
});
mdl.Add(new SelectListItem
{
Value = "2",
Text = "Module Two"
});
ViewData["moduleList"] = new SelectList(mdl,"Value", "Text");
return View("MainMenu");
}
and here is the markup
<div>
<%Html.DropDownList("moduleList", (IEnumerable<SelectListItem>)ViewData["moduleList"]); %>
</div>
Where did i go wrong ?
You are best putting that stuff in your model so for example
in the controller
[HandleError]
public class HomeController : Controller
{
public ActionResult Index()
{
ViewData["Message"] = "Welcome to ASP.NET MVC!";
ViewData["Time"] = DateTime.Now.ToString(CultureInfo.InvariantCulture);
var mdl = new List<SelectListItem>
{
new SelectListItem
{
Value = "1",
Text = "Module One"
},
new SelectListItem
{
Value = "2",
Text = "Module Two"
}
};
ViewData["moduleList"] = new SelectList(mdl, "Value", "Text");
var model = new HomeModel
{
SelectedItem = 1,
items = mdl
};
return View(model);
}
}
Now create the model
namespace MvcApplication1.Models
{
public class HomeModel
{
public int SelectedItem { get; set; }
public IEnumerable<SelectListItem> items { get; set; }
}
}
your page will look like this on a test site
<%# Page Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<MvcApplication1.Models.HomeModel>" %>
<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
Home Page
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
<h2><%: ViewData["Message"] %></h2>
<p>
To learn more about ASP.NET MVC visit http://asp.net/mvc.
</p>
<div>
<%=Html.DropDownList("SelectedItem", Model.items)%>
</div>
</asp:Content>
Now an explanation, you have created a model for the view and this model is returned to the page by the controller the page is inheriting from a ViewPage which takes the generic argument of the model that was supplied to it by the controller
The markup is saying "give me a html drop down and mark the selected item as the first selected, the items come from the model (which is what your controller supplied it).
In the real world the data would come from your data layer and not directly in the controller (I like as little code in the controller as possible)
edit:
You have a typo for your example try this
<%= Html.DropDownList("moduleList") %>
We're trying to use the [AllowHtml] decoration on one of our ViewModel properties so that we can avoid the YSOD:
A potentially dangerous Request.Form value was detected from the
client (RequestText="<br>").
when we try to submit html text, like: <br>. We want to then use Server.HtmlEncode within the controller action to prevent attacks, but when we decorate the property with [AllowHtml] it has no affect, and if we try to use [ValidateInput(false)] on the controller action, it has no effect either. We saw a StackOverflow Post saying that in MVC 3 RC2 that you have to add:
ModelMetadataProviders.Current = new
DataAnnotationsModelMetadataProvider(); to the global.asax
We tried that too, even though we are using the release version of MVC 3, not RC2, but that had no effect either. Does anyone know how to fix this?
Model:
namespace UI.Models.ViewModel
{
public class CustomerRequestSupport
{
/// <summary>
/// Gets or Sets the textual description entered by the Customer for
/// the support requested.
/// </summary>
[AllowHtml]
public string RequestText { get; set; }
}
}
Controller:
[HttpPost]
[TabsActionFilter]
public ActionResult RequestSupport(CustomerRequestSupport collection)
{
if (ModelState.IsValid)
{
Ticket ticket = new Ticket();
ticket.Requestor = LoggedInCustomer;
ticket.Summary = "General Support Ticket";
ticket.Notes = Server.HtmlEncode(collection.RequestText);
var errors = _ticketService.SubmitTicket(ticket);
if (errors.Any())
{
ModelState.AddModelError("collection",
String.Format("An error has occurred in your Request for Support: " +
"{0} Please try again later or call the help desk " +
"for immediate assistance.",
errors.Aggregate((acc, st) => acc + " " + st)));
}
else
{
TempData["FlashMessage"] = String.Format("Your request for support has been " +
"submitted, the Ticket Number is: {0}.", ticket.TicketNumber);
return AutoMapView<CustomerDetails>(View("Details", base.LoggedInCustomer));
}
}
//needed for tabs to show
ViewData.CustomerContactSet(base.LoggedInCustomer);
return View();
View:
<%# Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<UI.Models.ViewModel.CustomerRequestSupport>" %>
<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
Request Support
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="PageTitle" runat="server">
Request Support
</asp:Content>
<asp:Content ID="Content3" ContentPlaceHolderID="MainContent" runat="server">
<% using (Html.BeginForm())
{ %>
<%= Html.ValidationSummary() %>
<h2>Enter a description of the support needed</h2>
<%: Html.TextAreaFor( m => m.RequestText, 4, 90, null) %>
<input type="submit" value="Submit" />
<% } %>
</asp:Content>
<asp:Content ID="Content4" ContentPlaceHolderID="JavaScriptContent" runat="server">
</asp:Content>
You gotta be doing something wrong. Unfortunately as you haven't shown your example we cannot know what you are doing wrong. So let me write you a full working example:
Model:
public class MyViewModel
{
[AllowHtml]
public string RequestText { get; set; }
}
Controller:
public class HomeController: Controller
{
public ActionResult Index()
{
var model = new MyViewModel
{
RequestText = "<strong>Hello World</strong>";
};
return View(model);
}
[HttpPost]
public ActionResult Index(MyViewModel model)
{
return View(model);
}
}
View:
#model MyViewModel
#using (Html.BeginForm())
{
#Html.TextAreaFor(x => x.RequestText)
<button type="submit">OK</button>
}
So you gotta be doing something different than what I showed here. What is it?
In his answer Darin is definitely onto something when he asks
So you gotta be doing something different than what I showed here.
What is it?
I am guessing you have something else affecting the ASP.NET pipeline that is accessing the FormCollection prior to your [AllowHtml] being taken into account. Off the top of my head some common ASP.NET MVC OSS libraries that touch the pipeline are ELMAH, Glimpse, WebActivator, MvcContrib, there are many more but you get the idea.
I have to believe you are using one of the tools above or something similar. Assuming you are make sure you are on the latest release of each and check their open bug reports.
Finally, a quick way to determine if its your code, your MVC instance or an OSS library would be to create a test project. Try creating a vanilla ASP.NET MVC project. Ensure that AllowHtml works. Then add in your various OSS components until it breaks. Just be sure when you are adding in OSS components that the versions match what you are using in your current project.
In mu MVC application page I've a Product page (Controller: ProductController, Action: Index) which lists all the products from DB. Now I've a "Add Product" link which offers a form to fill Product details. On submitting the from AddProduct action is called which calls for a StoredProcedure (modelled in my model class). On succesful addition I use to RedirectAction("Index").Now my StoredProcedure returns me a message indicating the reslult of addition.I want this message to be stored in ViewData and show that on Index page. How can I do that? Any help would be much appreciated.
Use TempData instead of ViewData:
public ActionResult Index()
{
ViewData["message"] = TempData["message"];
return View();
}
public ActionResult AddProduct()
{
TempData["message"] = "product created";
return RedirectToAction("Index");
}
And in the Index view:
<% if (ViewData["message"] != null) { %>
<div><%= Html.Encode((string)ViewData["message"]) %></div>
<% } %>