Below is a very simple MVC triplet, implemented to try the things out.
It seems to work except the bit new { id = Model.StatusCode } in View, which is supposed to take the current value of Model.StatusCode and pass it as the parameter to the controller. It always pass '200', the initial value of the StatusCode (numeric value of 'Model.StatusCode'), although I change the value in the input field before hitting Submit button. How can I change my code to fix it?
I need to pass the parameter to the controller as string id since thsi controller's action is also used in routing.
Model
public class ErrorModel
{
[DisplayName("Status Code")]
public string StatusCode { get; set; }
public ErrorModel(string statusCode)
{
HttpStatusCode code;
if (! Enum.TryParse(statusCode, out code))
{
code = HttpStatusCode.NotFound;
}
StatusCode = ((int)code).ToString();
}
public ErrorModel(HttpStatusCode statusCode = HttpStatusCode.OK)
{
StatusCode = ((int)statusCode).ToString();
}
}
View
#using WebApplication1.Models
#model WebApplication1.Models.ExcelModel
#using (Html.BeginForm("StatusCode", "Error",
new { id = Model.StatusCode }, FormMethod.Post, null))
{
<p>
<div class="editor-label">
#Html.LabelFor(model => model.StatusCode)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.StatusCode)
</div>
<input type="submit" value="Submit" />
</p>
}
Controller
public class ErrorController : Controller
{
static readonly List<HttpStatusCode> ErrorCodes = new List<HttpStatusCode>(){
HttpStatusCode.Unauthorized, HttpStatusCode.Forbidden, HttpStatusCode.NotFound, HttpStatusCode.InternalServerError};
public ActionResult StatusCode(string id)
{
ViewBag.Message = "";
if ((id == null) || (ErrorController.AreEqual(HttpStatusCode.OK, id)))
{
return View("StatusCode", new ErrorModel(HttpStatusCode.OK));
}
foreach (HttpStatusCode errorCode in ErrorCodes)
{
if (ErrorController.AreEqual(errorCode, id))
{
return View("HttpError", new ErrorModel(errorCode));
}
}
ViewBag.Message = "Exception " + id
+ #" is not supported, see https://msdn.microsoft.com/en-us/library/system.net.httpstatuscode(v=vs.110).aspx for further details";
return View("HttpError", new ErrorModel(HttpStatusCode.InternalServerError));
}
static private bool AreEqual(HttpStatusCode httpCode, string statusCode)
{
return (statusCode == ((int)httpCode).ToString());
}
}
The overload you are using Html.BeginForm will append the routevalues to the form action attribute value. So the markup of your form will be like
<form action="Error/StatusCode/200">
<input type="text" name="StatusCode" />
</form>
When the form is posted, Model binder/Route engine will map the 200 value to the Id parameter because it matches our default route configuration {controllername}/{actionname}/{id}. That is the reason you are getting the initial value but not the updated value from form.
What you should do is use change the name of the input field rendered by Html.EditorFor helper method to match to the action parameter name (Id) . so that it will overwrite the values in the Url. You can use a different overload of the EditorFor method for that. You can totally get rid of the routevalues param from your BeginForm call since your form is going to post a field with name Id.
#using (Html.BeginForm("StatusCode", "Home" ,FormMethod.Post, null))
{
#Html.EditorFor(model => model.Name,null,"Id")
<input type="submit" />
}
This will render the input field with the name property value "Id" and when you post the form, The current value of this field will be mapped to your action method parameter.
Related
This question is really similar to my last one but this has to do with passing multiple parameters. My controller has two methods with the same name but one gets passed a nullable int to determine which action to take on post (I don't know if that's a good design decision). Here's the code:
public ActionResult EventDetails(int? eventID)
{
EventDetailsViewModel model = new EventDetailsViewModel();
if (eventID != null)
{
model.afterPost = false;
model.currentAttendance = getCountAttending(eventID);
model.currentUser = getCurrentUserProfile(User.Identity.Name);
model.eventDetails = getEventDetails(eventID);
model.eventRestrictions = getEventRestrictions(eventID);
model.usersAttending = getUsersAttending(eventID);
return View(model);
}
else
{
return View();
}
}
[HttpPost]
public ActionResult EventDetails(int? eventID, int? action)
{
EventDetailsViewModel model = new EventDetailsViewModel();
if (eventID != null)
{
model.afterPost = true;
model.currentAttendance = getCountAttending(eventID);
model.currentUser = getCurrentUserProfile(User.Identity.Name);
model.eventDetails = getEventDetails(eventID);
model.eventDetails["ActionID"] = action.ToString();
model.eventRestrictions = getEventRestrictions(eventID);
model.usersAttending = getUsersAttending(eventID);
return View(model);
}
else
{
return View();
}
}
the view is huge but I'll post the relevant piece:
#if(User.Identity.IsAuthenticated)
{
if (!Model.eventDetails["Event_Owned"].Equals("true"))
{
<div class="joinEventButton">
#if(Model.eventDetails["Event_Current"].Equals("true"))
{
<form method="post" action="#Url.Action("EventDetails", "Event", new{eventID = Int32.Parse(Model.eventDetails["EventID"]), action = Int32.Parse(Model.eventDetails["CODE_RequestInvitation"])})">
<input type="submit" value="Request Invitation" class="submitButton submitButton-green"/>
</form>
}
else
{
<button class="disabledButton disabledButton-grey">Request Invitation</button>
}
</div>
}
and just for good measure, my routes:
public static void RegisterRoutes(RouteCollection routes)
{
//routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
So the idea is that based on authentication and whether they own a specific event they are looking at, they will have different buttons show on the screen. This certain one is for when they want to join an event they can push a button to request an invitation. The controller knows this because of the int value being passed back as CODE_RequestInvitation. The action being returned is always null. I can't figure out what's wrong.
You problem is the use of the parameter name action (which is conflicting with the action attribute). Change it to another name and it will work. Inspect he html of the <form> element before and after the change.
You code for generating the form is awful and the use of Int32.Parse() is pointless.
#using (Html.BeginForm("EventDetails", "Event", { eventID = Model.eventDetails["EventID"], actionID = Model.eventDetails["CODE_RequestInvitation"] }, FormMethod.Post))
{
<input type="submit" value="Request Invitation" class="submitButton submitButton-green"/>
}
and post back to
[HttpPost]
public ActionResult EventDetails(int? eventID, int? actionID)
How to get the textbox value from view to controller in mvc4?If I using httppost method in controller the page cannot found error was came.
View
#model MVC_2.Models.FormModel
#{
ViewBag.Title = "DisplayForm";
}
#using (Html.BeginForm("DisplayForm", "FormController", FormMethod.Post))
{
<form>
<div>
#Html.LabelFor(model => model.Empname)
#Html.TextBoxFor(model => model.Empname)
#* #Html.Hidden("Emplname", Model.Empname)*#
#Html.LabelFor(model => model.EmpId)
#Html.TextBoxFor(model => model.EmpId)
#* #Html.Hidden("Emplid", Model.EmpId)*#
#Html.LabelFor(model => model.EmpDepartment)
#Html.TextBoxFor(model => model.EmpDepartment)
#* #Html.Hidden("Empldepart", Model.EmpDepartment)*#
<input type="button" id="submitId" value="submit" />
</div>
</form>
}
model
public class FormModel
{
public string _EmpName;
public string _EmpId;
public string _EmpDepartment;
public string Empname
{
get {return _EmpName; }
set { _EmpName = value; }
}
public string EmpId
{
get { return _EmpId;}
set {_EmpId =value;}
}
public string EmpDepartment
{
get { return _EmpDepartment; }
set { _EmpDepartment = value; }
}
}
controller
public ActionResult DisplayForm()
{
FormModel frmmdl = new FormModel();
frmmdl.Empname=**How to get the textbox value here from view on submitbutton click???**
}
First you need to change your button type to "submit". so your form values will be submitted to your Action method.
from:
<input type="button" id="submitId" value="submit" />
to:
<input type="submit" id="submitId" value="submit" />
Second you need to add your model as parameter in your Action method.
[HttpPost]
public ActionResult DisplayForm(FormModel model)
{
var strname=model.Empname;
return View();
}
Third, If your Controller name is "FormController". you need to change the parameter of your Html.Beginform in your view to this:
#using (Html.BeginForm("DisplayForm", "Form", FormMethod.Post))
{
//your fields
}
P.S.
If your view is the same name as your Action method which is "DisplayForm" you don't need to add any parameter in the Html.BeginForm. just to make it simple. like so:
#using (Html.BeginForm())
{
//your fields
}
Have an ActionResult for the form post:
[HttpPost]
public ActionResult DisplayForm(FormModel formModel)
{
//do stuff with the formModel
frmmdl.Empname = formModel.Empname;
}
Look into Model Binding. Default model binding will take the data embedded in your posted form values and create an object from them.
Let's implement simple ASP.NET MVC subscription form with email textbox.
Model
The data from the form is mapped to this model
public class SubscribeModel
{
[Required]
public string Email { get; set; }
}
View
View name should match controller method name.
#model App.Models.SubscribeModel
#using (Html.BeginForm("Subscribe", "Home", FormMethod.Post))
{
#Html.TextBoxFor(model => model.Email)
#Html.ValidationMessageFor(model => model.Email)
<button type="submit">Subscribe</button>
}
Controller
Controller is responsible for request processing and returning proper response view.
public class HomeController : Controller
{
public ActionResult Index()
{
return View();
}
[HttpPost]
public ActionResult Subscribe(SubscribeModel model)
{
if (ModelState.IsValid)
{
//TODO: SubscribeUser(model.Email);
}
return View("Index", model);
}
}
Here is my project structure. Please notice, "Home" views folder matches HomeController name.
You model will be posted as a object on the action and you can get it in action on post like this:
[HttpPost]
public ActionResult DisplayForm(FormModel model)
{
// do whatever needed
string emp = model.EmpName;
}
it you are posting data always put HttpPost attribute on the action.
Your view also has mistakes, make it like this:
#using (Html.BeginForm("DisplayForm", "Form", FormMethod.Post))
{
<div>
#Html.LabelFor(model => model.Empname)
#Html.TextBoxFor(model => model.Empname)
#* #Html.Hidden("Emplname", Model.Empname)*#
#Html.LabelFor(model => model.EmpId)
#Html.TextBoxFor(model => model.EmpId)
#* #Html.Hidden("Emplid", Model.EmpId)*#
#Html.LabelFor(model => model.EmpDepartment)
#Html.TextBoxFor(model => model.EmpDepartment)
#* #Html.Hidden("Empldepart", Model.EmpDepartment)*#
<input type="button" id="submitId" value="submit" />
</div>
}
There are two ways you can do this.
The first uses TryUpdateModel:
public ActionResult DisplayForm()
{
FormModel frmmdl = new FormModel();
TryUpdateModel (frmmdl);
// Your model should now be populated
}
The other, simpler, version is simply to have the model as a parameter on the [HttpPost] version of the action:
[HttpPost]
public ActionResult DisplayForm(FormModel frmmdl)
{
// Your model should now be populated
}
Change your controller like below.
[HttpPost]
public ActionResult DisplayForm(FormModel model)
{
var Empname = model.Empname;
}
You need to have both Get and Post Methods:
[HttpGet]
public ActionResult DisplayForm()
{
FormModel model=new FormModel();
return View(model);
}
[HttpPost]
public ActionResult DisplayForm(FormModel model)
{
var employeeName=model.Empname;
return View();
}
[HttpPost]
public ActionResult DisplayForm(FormModel model)
{
var value1 = model.EmpName;
}
Model values from hidden field? I recommend the strongly typed approach shown below:
public ActionResult DisplayForm(string Emplname, string Emplid, string Empldepart)
[HttpPost]
public ActionResult DisplayForm(FormModel model)
{
FormModel frmmdl = new FormModel();
frmmdl.Empname=**How to get the textbox value here from view on submitbutton //click???**
}
model.Empname will have the value
I have recently seen some sites that ask you to "prove you are not a robot" by solving a Math equation, instead of using something like reCAPTCHA...
I am trying to take that idea a step further by randomly generating a simple Math equation, with a single operator and two operands, and requiring the user to solve the equation before submitting a form:
Here is my model:
public class ContactModel
{
public string Expression;
public static string AnswerToExpression;
[Required]
// compile-error on this line! see bottom of question...
[RegularExpression(ContactModel.AnswerToExpression.ToString())]
public string AnswerField { get; set; }
public ContactModel()
{
this.Expression = this.GetRandomExpression();
AnswerToExpression = this.GetAnswerFromExpression(this.Expression);
}
private string GetRandomExpression()
{
Random rand = new Random(); // using only one instance for unique numbers
return rand.Next(1, 10).ToString() + " * " + rand.Next(1, 10).ToString();
}
private int GetAnswerFromExpression(string expr)
{
// splitting by a "char space":
string[] tokens = expr.Split(' ');
return Int16.Parse(tokens[0]) * Int16.Parse(tokens[2]);
}
}
...the relevant view code:
#model MyWebsite.Models.ContactModel
#using (Html.BeginForm("Contact", "Home", FormMethod.Post))
{
<span>#Html.LabelFor(model => model.AnswerField, Model.Expression + " = ")</span>
#Html.TextBoxFor(model => model.AnswerField, new { #placeholder = "answer to " + Model.Expression + "..." })
<input type="submit" value="Submit" title="submit your message" />
}
...and the relevant controller code:
public class HomeController : Controller
{
[HttpGet]
public ActionResult Contact()
{
ContactModel model = new ContactModel();
return View(model);
}
[HttpPost]
public ActionResult Contact(ContactModel model)
{
if (ModelState.IsValid)
{
// insert into database, send email, etc..
// redirecting to the home page here:
return RedirectToAction("Index", "Home");
}
else
{
// staying on the contact page if the form does not validate:
return View(model);
}
}
}
So when trying to pass a static variable into RegularExpression, I am getting the compile-error "An attribute argument must be a constant expression, typeof expression or array creation expression of an attribute parameter type."
Do I need to create a custom class that extends from RegularExpressionAttribute and use that instead?
You can only pass a constant as a param to attribute. This means that your param value has to be defined during the compilation, in your case the param will only be defined at runtime.
If you try [RegularExpression("MY_EXPRESSION")] no compilation error will occur.
Attribute Usage Doc should help you out.
#CoffeeCode is correct, and so I have a javascript / jQuery solution:
First I have made this change in my model:
// this is now an instance variable:
public string AnswerToExpression;
...and I have added the js to a section reference I named "Scripts" in my view:
#model MyWebsite.Models.ContactModel
#section Scripts {
<script type="text/javascript">
$("form").submit(function () {
var answerField = $("#answer");
// comparing the user's answer to the model variable here:
if (answerField.val() !== "#Model.AnswerToExpression") {
alert("Incorrect answer!");
// preventing the form from being submitted here:
return false;
}
}); // end of form submit
</script>
}
#using (Html.BeginForm("Contact", "Home", FormMethod.Post))
{
<span>#Html.LabelFor(model => model.AnswerField, Model.Expression + " = ")</span>
#Html.TextBoxFor(model => model.AnswerField, new {
#placeholder = "answer to " + Model.Expression + "...",
// added this id for javascript use:
#id = "answer"
})
<input type="submit" value="Submit" title="submit your message" />
}
...where #RenderSection("Scripts", required: false) is placed at the bottom of my _Layout.cshtml file, before the closing </body> tag (this is where the javascript embedded in my view is placed).
I am trying to display a Partial View inside a master (Index) View:
Steps:
User selects a Dropdown item from the Index View.
This displays a Partial View that has a search Form.
User fills the search Form and then clicks the Submit button.
If the search Form is valid, a new page (Results View) is displayed.
Else, the search Form Partial View should be re displayed with errors INSIDE the master View
I'm having a problem with number 4 because when the search Form submits, it only displays the partial View in a new window. I want to display the whole page : Index View + Partial View with errors.
Suggestions? This is what I have:
Image
Controller
public class AppController : Controller
{
public ActionResult Index()
{
return View(new AppModel());
}
public ActionResult Form(string type)
{
if (type == "IOS")
return PartialView("_IOSApp", new AppModel());
else
return PartialView("_AndroidApp", new AppModel());
}
public ActionResult Search(AppModel appModel)
{
if (ModelState.IsValid)
{
return View("Result");
}
else // This is where I need help
{
if (appModel.Platform == "IOS")
return PartialView("_IOSApp", appModel);
else
return PartialView("_AndroidApp", appModel);
}
}
}
Model
public class AppModel
{
public string Platform { get; set; }
[Required]
public string IOSAppName { get; set; }
[Required]
public string AndroidAppName { get; set; }
public List<SelectListItem> Options { get; set; }
public AppModel()
{
Initialize();
}
public void Initialize()
{
Options = new List<SelectListItem>();
Options.Add(new SelectListItem { Text = "IOS", Value = "I" });
Options.Add(new SelectListItem { Text = "Android", Value = "A"});
}
}
Index.cshtml
#{ ViewBag.Title = "App Selection"; }
<h2>App Selection</h2>
#Html.Label("Select Type:")
#Html.DropDownListFor(x => x.Platform, Model.Options)
<div id="AppForm"></div> // This is where the Partial View goes
_IOSApp.cshtml
#using (Html.BeginForm("Search", "App"))
{
#Html.Label("App Name:")
#Html.TextBoxFor(x => x.IOSAppName)
<input id="btnIOS" type="submit" value="Search IOS App" />
}
AppSearch.js
$(document).ready(function () {
$("#Platform").change(function () {
value = $("#Platform :selected").text();
$.ajax({
url: "/App/Form",
data: { "type": value },
success: function (data) {
$("#AppForm").html(data);
}
})
});
});
You need to call the search method by ajax too.
Change the index.html and then
1- if Form is valid replace the whole html or the mainContainer( The div that i have added to view).
2- else just replace the partial view.
#{ ViewBag.Title = "App Selection"; }
<div id="mainContainer">
<h2>App Selection</h2>
#Html.Label("Select Type:")
#Html.DropDownListFor(x => x.Platform, Model.Options)
<div id="AppForm"></div> // This is where the Partial View goes
</div>
Remove the form tag from your partial view just call an ajax call method for searching.
May be easiest way to handle this problem is using MVC unobtrusive ajax.
I would say use inline Ajax to submit this form.
#using (Html.BeginForm("Search", "App"))
{
#Html.Label("App Name:")
#Html.TextBoxFor(x => x.IOSAppName)
<input id="btnIOS" type="submit" value="Search IOS App" />
}
change upper given code into following code
#using (
Ajax.BeginForm(
"Form", "App",
new AjaxOptions()
{
UpdateTargetId = "App",
HttpMethod = "Post"
}
)
)
{
<div class="editor-label">
#Html.Label("App Name:")
</div>
<div class="editor-field">
#Html.TextBoxFor(x => x.IOSAppName)
</div>
<input id="btnIOS" type="submit" value="Search IOS App" />
}
//in controller change the parameter of the given method from string type to model object which will be posted by ajax form.
public ActionResult Form(AppModel appModel)
{
if (appModel.type == "IOS")
return PartialView("_IOSApp", new AppModel());
else
return PartialView("_AndroidApp", new AppModel());
}
I use an update action to update based on the input from the #Html.Textbox.
#using (Html.BeginForm("Update", "Shopping", new { UserID = Request.QueryString["UserID"] }, FormMethod.Post, new { id = "myForm" }))
{
#Html.ValidationSummary()
#Html.Hidden("id", #Request.QueryString["UserID"] as string)
#Html.Hidden("productid", item.ProductID as string)
#Html.TextBox("Quantity", item.Quantity)
#Html.ValidationMessage("Quantity", "*")
#Html.Hidden("unitrate", item.Rate)
<input type="submit" value="Update" />
}
and In My Model class
[Required(ErrorMessage = "Quantity is required.")]
[Display(Name = "Quantity")]
[Range(2, 100, ErrorMessage = "There is not enough inventory for the product to fulfill your order.")]
public int? Quantity { get; set; }
The problem is I m not getting the validation message when the textbox is empty.
But when I use #Html.TextBoxFor
#Html.TextBoxFor(modelItem => item.Quantity)
#Html.ValidationMessageFor(modelitem => item.Quantity)
I am getting the validation message. and my update action is not working.
Here I have two options.
1. How to pass the textbox name "qty" in #Html.TextboxFor ?? (or)
2. How to get the validation message in #Html.Textbox() using #Html.ValidationMessage()
Any suggestions ..
EDIT :
My Update Action
[HttpPost]
public ActionResult Update(string id, string productid, int Quantity, decimal unitrate)
{
if (ModelState.IsValid)
{
int _records = UpdatePrice(id, productid, Quantity, unitrate);
if (_records > 0)
{
return RedirectToAction("Index1", "Shopping", new { UserID = Request.QueryString["UserID"] });
}
else
{
ModelState.AddModelError("","Can Not Update");
}
}
return View("Index1");
}
you have the answer in your question, when you use
#Html.TextBoxFor(modelItem => item.Quantity)
#Html.ValidationMessageFor(modelitem => item.Quantity)
you get the error message becasue the MVC model validation works on the name attributes as #Mystere Man said in the comments you are defying all the conventions and conventions is what MVC is all about, either change the property name in your model or use it as it is in the view if you want to leverage the MVC's model validation.
Not entirely relevant but a good read.