This must be something very obvious but for me it looks very strange. I have simple controller, model with one property, and view which displays value of property and renders editor for that property. When I click the button, form is posted and exclamation mark is appened to property. This exclamation mark is visible in my view but only in p tag, not in input tag rendered by Html.TextBoxFor().
Why Html.TextBoxFor() ignores that I updated my model in post action?
Is there any way to change this behavior of Html.TextBoxFor()?
View
#model ModelChangeInPostActionNotVisible.Models.IndexModel
#using (Html.BeginForm())
{
<p>#Model.MyProperty</p>
#Html.TextBoxFor(m => m.MyProperty)
<input type="submit" />
}
Model
namespace ModelChangeInPostActionNotVisible.Models
{
public class IndexModel
{
public string MyProperty { get; set; }
}
}
Controller
namespace ModelChangeInPostActionNotVisible.Controllers
{
public class HomeController : Controller
{
public ActionResult Index()
{
return View(new IndexModel { MyProperty = "hi" });
}
[HttpPost]
public ActionResult Index(IndexModel model)
{
model.MyProperty += "!";
return View(model);
}
}
}
HTML after clicking on submit button
<form action="/" method="post"> <p>hi!</p>
<input id="MyProperty" name="MyProperty" type="text" value="hi" /> <input type="submit" />
</form>
This is by design.
The helper methods are using the ModelState, thus if the response of your request is using the same Model, it will display the value that was posted.
This is to allow you to render the same view in the situation where the validation would have failed.
To make sure you display the new information add : ModelState.Clear(); before you return.
Read more here : http://blogs.msdn.com/b/simonince/archive/2010/05/05/asp-net-mvc-s-html-helpers-render-the-wrong-value.aspx
namespace ModelChangeInPostActionNotVisible.Controllers
{
public class HomeController : Controller
{
public ActionResult Index()
{
return View(new IndexModel { MyProperty = "hi" });
}
[HttpPost]
public ActionResult Index(IndexModel model)
{
model.MyProperty += "!";
ModelState.Clear();
return View(model);
}
}
}
Yan Brunet is absolutely correct that the variable needs to be removed from the ModelState in order to be modified in the controller. You don't have to clear the entire ModelState, though. You could do the following to remove just the variable to want to modify:
ModelState.Remove("MyProperty");
This would be useful in case you wanted to retain other values which the user had entered.
Related
I am new to mvc and this is my requirement. I am developing a page which should render a text and a checkbox. The checkbox will be checked depending on the T/F value from the database. So I am passing all the necessary data from db to the view as Json object in GetData() method.
namespace ClinicalAdvantage.Web.Controllers.UserAppSettingC
{
using System;
using System.Collections.Generic;
using Newtonsoft.Json.Linq;
using NHibernate.Mapping;
public class UserAppSettingsController : Controller
{
private readonly IAgg1 agg;
public UserAppSettingsController(IAgg1 agg)
{
this.agg = agg;
}
#region Public Methods and Operators
public ActionResult Index()
{
return this.View();
}
public ActionResult GetData()
{
return new JsonNetResult() { Data = this.agg.GetAllUserAppSettings() };
}
public ActionResult Save(JObject userAppSettings)
{
if (userAppSettings != null)
{
this.agg.SaveAllUserAppSettings(userAppSettings);
}
return this.Json(new { Status = "Success" });
}
#endregion
}
}
I have once tried returning the same data written as a viewmodel as a result of the index(). I had done something like
public ActionResult Index()
{
return this.View(model);
}
And for this I wrote out the in the corresponding view as
#model ClinicalAdvantage.Web.ViewModels.UserAppSettings1.UserAppSettingsViewModel
<form action="#Url.Action("Save")" method="post">
#Html.CheckBoxFor(x => x.IsM, new { maxlength = "50", size = "50" })
<!-- Form content goes here -->
<input type="submit" value="Save" />
</form>
But for some reason I am not using viewmodel to return data. So the above way of coding the veiw might not be right. I am not using GetData() to pass data to the front end and I can't really change this.
public ActionResult GetData() { return new JsonNetResult() { Data = this.agg.GetAllUserAppSettings() }; }
But I want to know how to code the front end to parse this json data when I am returning it as result of GetData method as tyype JsonNetResult.. Where will my view be. What should be the code if I want to display a checkbox and save button. The checkbox will be populated based on value returned by json.
This is the json I am returning
{"MaskPatientName":{"enabled":true,"value":false}}
There should be a label called MaskPatienTName
The check box should be checked if value property is true
On click of save butoon the save method in the controller shld be called.
Please help me
Simplest solution is to pass the populated view model to the view in your Index action
public ViewResult Index()
{
return View(agg.GetAllUserAppSettings());
}
And then your view should look something like this (use the Html helper to create form markup). This assumes that IsM is a property of UserAppSettingsViewModel.
#model ClinicalAdvantage.Web.ViewModels.UserAppSettings1.UserAppSettingsViewModel
#using (Html.BeginForm("Save", "UserAppSettings")) {
#Html.CheckBoxFor(x => x.IsM, new { maxlength = "50", size = "50" })
<!-- Form content goes here -->
<input type="submit" value="Save" />
}
I've been looking at Sitecore MVC but I'm stuck at how to handle a case where my page has two controller renderings and each contains a form. I want the individual controllers to handle their HttpPost and return the whole page after post.
I've set up a simple example. Both controllers are similar:
public class ExampleController : Sitecore.Mvc.Controllers.SitecoreController
{
public override ActionResult Index()
{
return View("Index");
}
[HttpPost]
public ActionResult Index(string formPostData)
{
ViewBag.SaveForLater = formPostData;
return Index();
}
}
The views look like this:
#using Sitecore.Mvc
#using (Html.BeginRouteForm(Sitecore.Mvc.Configuration.MvcSettings.SitecoreRouteName, FormMethod.Post))
{
#Html.AntiForgeryToken()
var term = ViewBag.SaveForLater as string;
if (!string.IsNullOrEmpty(term))
{
<p>Submitted: #term</p>
}
<p>
#Html.Sitecore().FormHandler("Example", "Index")
<input type="text" name="formPostData" placeholder="Enter something" />
<input type="submit" name="submit" value="Search" />
</p>
}
With this setup both forms submit their data but the returned page consists only of the partial view and not the whole page.
If I replace the line #Html.Sitecore().FormHandler("Example", "Index") with #Html.Sitecore().FormHandler() then the whole page is returned but the post action for both forms is processed.
Neither scenario is ideal. I must be missing something and would appreciate a pointer.
Unfortunately there are multiple ways how you can integrate with Sitecore MVC and Sitecore doesn't provide many best practice examples. One example you can find here.
In our projects we do it a bit different, because we want to use as much as possible the conventions etc. from default ASP.NET MVC. I try to include a complete simple example in this post.
We add two different hidden fields to the form with the current action and the current controller, the view looks like this:
#model Website.Models.TestViewModel
#using (Html.BeginForm())
{
#Html.LabelFor(model => model.Text)
#Html.TextBoxFor(model => model.Text)
<input type="submit" value="submit" />
<input type="hidden" name="fhController" value="TestController" />
<input type="hidden" name="fhAction" value="Index" />
}
With this simple ViewModel:
namespace Website.Models
{
public class TestViewModel
{
public string Text { get; set; }
}
}
Then we have created a custom attribute which checks if the current controller/action is the same as posted:
public class ValidateFormHandler : ActionMethodSelectorAttribute
{
public override bool IsValidForRequest(ControllerContext controllerContext, MethodInfo methodInfo)
{
var controller = controllerContext.HttpContext.Request.Form["fhController"];
var action = controllerContext.HttpContext.Request.Form["fhAction"];
return !string.IsNullOrWhiteSpace(controller)
&& !string.IsNullOrWhiteSpace(action)
&& controller == controllerContext.Controller.GetType().Name
&& methodInfo.Name == action;
}
}
The controller actions then gets the new attribute:
namespace Website.Controllers
{
public class TestController : Controller
{
public ActionResult Index()
{
return View();
}
[HttpPost]
[ValidateFormHandler]
public ActionResult Index(TestViewModel model)
{
return View(model);
}
}
}
We always return the view resolved by ASP.NET MVC. By convention this is the view with the same name as the action within the folder with the same name as the controller.
This approach works very well for us. If you would like to add the AntiForgeryToken, this also works fine.
You should create the main Index view having two partial view ,
like this:
You can define main view model like this
public class MainViewModel
{
public LoginViewModel LoginModel { get; set; }
public RegisterViewModel RegisterModel { get; set; }
}
then the separate model:
public class RegisterViewModel
{
// Your model properties
}
public class LoginViewModel
{
// Your model properties
}
then define your action for main view as:
public ActionResult MainView()
{
MainViewModel model = new MainViewModel
{
LoginModel = new LoginViewModel(),
RegisterModel = new RegisterViewModel()
};
return View(model);
}
Your main view as
#Html.Partial("_Login", Model.LoginModel)
#Html.Partial("_Register", Model.RegisterModel)
after that you can saperately create your views ,thanks
Sitecore Habitat does it similarly as above, but using a unique rendering ID.
public class ValidateRenderingIdAttribute : ActionMethodSelectorAttribute
{
public override bool IsValidForRequest(ControllerContext controllerContext, MethodInfo methodInfo)
{
var ignoreCase = StringComparison.InvariantCultureIgnoreCase;
var httpRequest = controllerContext.HttpContext.Request;
var isWebFormsForMarketersRequest = httpRequest.Form.AllKeys
.Any(key => key.StartsWith("wffm", ignoreCase) && key.EndsWith("Id", ignoreCase));
if (isWebFormsForMarketersRequest)
{
return false;
}
string renderingId;
if (!httpRequest.GetHttpMethodOverride().Equals(HttpVerbs.Post.ToString(), ignoreCase) || string.IsNullOrEmpty(renderingId = httpRequest.Form["uid"]))
{
return true;
}
var renderingContext = RenderingContext.CurrentOrNull;
if (renderingContext == null)
{
return false;
}
Guid id;
return Guid.TryParse(renderingId, out id) && id.Equals(renderingContext.Rendering.UniqueId);
}
}
Link to repo
i did this all but now how to get values being typed in Textbox, password box etc in CONTROLLER. I defined all necessary methods, boxes and buttons etc. So the only problem is to get values in controller and then to send them to model for accessing db data
.csHtml
#using (Html.BeginForm("register","Home", FormMethod.Post, new {id="submitForm"}))
{
<div>
<i>#Html.Label("Name:")</i>
#Html.TextBox("txtboxName")
</div>
<div>
<i>#Html.Label("Email:")</i>
#Html.TextBox("txtboxEmail")
</div>
<div>
<i>#Html.Label("Password:")</i>
#Html.Password("txtboxPassword")
</div>
<div>
<button type="submit" id="btnSubmit" name="Command" value="Submit">Submit</button>
</div>
}
Controller code:
namespace LoginSys.Controllers
{
public class HomeController : Controller
{
public ActionResult Index()
{
ViewBag.Message = "Registration";
return View();
}
[HttpPost]
public ActionResult register(string command, FormCollection formData )
{
if (command == "submit")
{
var name = formData["txtboxName"];
var email = formData["txtboxEmail"];
}
return View();
}
}
}
i'm intentionally using this way of coding it instead of complex and advance one. Just help me to get values in controller
[HttpPost]
public ActionResult register(YOURMODEL model)
{
//db operation
return View();
}
NOTE: make sure your textbox name should be same as your model name
You should use viewmodels. create a model for the view that can be posted to the action. However, if you wish to continue your current approach you need to change the controller action to something like this:
[HttpPost]
public ActionResult register(string btnSubmit, string txtboxName, string txtboxEmail, string txtboxPassword)
{
if (command == "submit")
{
}
return View();
}
if this doesn't work, you can test it by using this:
[HttpPost]
public ActionResult register(FormCollection form)
{
if (command == "submit")
{
}
return View();
}
When you debug you can check the 'form' parameter and see that your fields exists in the form, and get the proper names for the parameters you need.
I have the following model:
public class Person
{
public int ID{get;set;}
public string Name {get;set;}
public string Address{get;set;}
}
For other hand i have the following view called Index:
#model List<Person>
#{
foreach(Person person in Model)
{
#person.Name
}
}
Finally i have the following action:
public ActionResult Index()
{
List<Person> persons=new List<Person>();
persons.Add(new Person(){ID=1,Name="John"});
persons.Add(new Person(){ID=2,Name="Tom"});
persons.Add(new Person(){ID=2,Name="Derek"});
}
Im thinking to create a form (since i cannot use ajax for this app due to some requirements), to post an instance of the person chosen by the user (when clicks an anchor of my view). I would like to know how i could post a Person instance to another action described below (since my view is typed to a generic list of persons).
[HttpPost]
public ActionResult Index(Person person)
{
... Do whatever
}
Solution with POST:
in order to work it with POST you will have to palce a form and plant hidden fields like
#model List<Person>
#{
foreach(Person person in Model)
{
<form action="/controller/actionresult">
<input type="submit" value=person.Name />
<input type="hidden" name="ID" value="#person.ID"
<input type="hidden" name="Name" value="#person.Name"
</form>
}
}
and on the server side
[HttpPost]
public ActionResult Index(Person person)
{
... Do whatever
}
Solution with GET:
a tags normally dont work with POST there default behaviour is to request the server for a resource via GET although you can override this behaviour using javascript but in your case that is not an option therefore you can try
#model List<Person>
#{
foreach(Person person in Model)
{
#Html.ActionLink(persno.Name,"Index","home",new{ID=person.ID,Name=person.Name,null);
}
}
the only drawback is you will have to use a submit button instead of a tags, you can use css styling to style the button like an a tag. also if you have not set custom routes for this kind of request the uri will have query string params like
http://yourdomain/home/index?ID=1&Name=john
You're approaching it from the wrong angle. As you're not presenting any way to edit a Person you should just pass it by reference (i.e. the PersonId).
So just use a straight-forward link:
// View:
Html.ActionLink( person.Name, "ViewPerson", new { personId = person.ID } );
// Controller action:
public ActionResult ViewPerson(String personId) {
Person person = GetPersonFromDatabase( personId );
}
Pretty Basic one here guys.
I have a View which holds 2 textfields for input and a submit button
<%using (Html.BeginForm("DateRetrival", "Home", FormMethod.Post)){ %>
<%=Html.TextBox("sday")%>
<%=Html.TextBox("eday")%>
<input type="submit" value="ok" id="run"/>
<% }%>
the following controller action which I want to bind the data input is as follows
[AcceptVerbs(HttpVerbs.Get)]
public ActionResult DateRetrival()
{
return View();
}
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult DateRetrival(string submit)
{
return null;
}
When I debug this and look in the action methods parameter, the value is null. When I've entered values in both textboxes and and clicked the submit method.
You probably want to do something like this:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult DateRetrival(string sday, string eday)
{
return null;
}
Ideally, though you probably want to be passing a model to your controllers:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult DateRetrival(DateModel dates)
{
var date1 = dates.sday;
var date2 = dates.eday;
return null;
}
See http://msdn.microsoft.com/en-us/library/dd394711.aspx
Add parameters to catch each input field value.
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult DateRetrival(string sday, string eday)
{
return null;
}
Try:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult DateRetrival(string sday, string eday, string submit)
{
return null;
}
and if you want sumbit button value
<input type="submit" value="ok" id="run" name="submit"/>
If you want to have value posted, name attribute has to be set. Html.TextBox automatically sets name from parameter.