ASP.NET MVC - Dynamic action and parameter for a form - asp.net-mvc

I'm working on a bit of MVC where I'm needing to dynamically route a form to a certain action and parameter combination. So far, I've got this:
PageViewModel
{
public string Action {get;set;}
public string Parameter {get;set;}
/*... other properties for the form */
}
PageController
{
public ViewResult MyAction(string myParamterName) {
return View("CommonView",
new PageViewModel{Action="MyAction", Parameter="myParameterName"));
}
public ViewResult YourAction(string yourParamterName) {
return View("CommonView",
new PageViewModel{Action="YourAction", Parameter="yourParameterName"));
}
/* ... and about 15 more of these */
}
CommonView.aspx:
<%-- ... --%>
<% using (Html.BeginForm(Model.Action,"PageController",FormMethod.Get)) {%>
<%=Html.TextBox(Model.Parameter)%>
<input id="submit" type="submit" value="Submit" />
<%}%>
<%-- ... --%>
This works, but it's got a lot of strings floating around to tell it where to go.
What I'd like to have is a type-safe way of defining the form parameters inside the view, but I'm a bit lost on how to accomplish this. Perhaps something that looks like this -
<% using (Html.BeginForm<PageController>(Model.??ExpressionToGetAction??)) {%>
<%=Html.TextBox(Model.??ExpressionToGetParameter??)%>
<input id="submit" type="submit" value="Submit" />
<%}%>
Or, is there a way to get the action and parameter used to generate this view, perhaps from route data?
Or should there be a custom routing scheme that can handle all of this automagically?
So, what I'm really wanting is the most elegant and type-safe way to accomplish this. Thanks!
EDIT
As Josh points out, the form will submit back to the action. This trims the code somewhat :
PageViewModel
{
public string ParameterName {get;set;}
/*... other properties for the form */
}
PageController
{
public ViewResult MyAction(string myParamterName) {
return View("CommonView",
new PageViewModel{ParameterName ="myParameterName"));
}
public ViewResult YourAction(string yourParamterName) {
return View("CommonView",
new PageViewModel{ParameterName ="yourParameterName"));
}
/* ... and about 15 more of these */
}
CommonView.aspx:
<%-- ... --%>
<% using (Html.BeginForm(FormMethod.Get)) {%>
<%=Html.TextBox(Model.ParameterName)%>
<input id="submit" type="submit" value="Submit" />
<%}%>
<%-- ... --%>
It is still unclear how to have the textbox bind a parameter by name back to the action from which the view was created without explicitly specifying it.

Or, is there a way to get the action and parameter used to generate this view
If you leave the action and controller portion of the BeginForm arguments empty, it will to post back to where it came from. You can have two action with the same name, one decorated as HttpGet and the other HttpPost, as long as they have different parameters. Usually the get has one or none, and the post has several or a model bind.

Related

MVC HTTP Post input return null

Controller:
public ActionResult MyController()
{
ViewBag.DateNow = DateTime.Now.ToString("yyyy-MM-dd");
}
[HTTPPost]
public ActionResult MyController(string fromDate)
{
ViewBag.DateNow = fromDate;
}
View:
#using (Html.BeginForm("MyController", "Account", FormMethod.Post))
{
//datepicker class: bootstrap-datepicker.js
<input id="fromDate" type="text" class="datepicker" />
<buttontype="submit" value="Search" class="btn btn btn-primary">
Search
</button>
}
What I'm trying to achieve is before POST the data that pass into ViewBag.DateNow is the current date and it successfully bring in to the view. However when I'm trying to fill up the input form with (eg: 2016-05-10) and click on the Search button. But seems like the fromDate string return NullReferenceException. I'm trying out with some solution online but I still can't get it right and that's why I decided to get this posted up. Thanks in advance!
For this to work properly you need to specify the name attribute in your textbox. It needs to be the same value as the input variable in your HTTP post action method, namely fromDate. Currently the id attribute is set to fromDate:
<input id="fromDate" name="fromDate" type="text" value="#ViewBag.DateNow" />
If you do not specify this name attribute then when you post your form fromDate will always be null. Specifying it like above will make sure that fromDate will always have a value (if entered).
I want to go a bit off-topic here, I would like to suggest that you make use of view models for your form submissions. Instead of having individual input variables in your action method you can just have your view model as input parameter.
I wrote an answer as to what view models are here, please go and read it if you have the time:
What is ViewModel in MVC?
Working on your example, I would have a view model that contains just one property, namely FromDate. FromDate will contain the value in your textbox. It is setup as a string because you want to pass it a formatted date value:
public class TestModel
{
public string FromDate { get; set; }
}
This value will be set in your HTTP get action method and the view model will be sent to the view:
public ActionResult Index()
{
TestModel model = new TestModel();
model.FromDate = DateTime.Now.ToString("yyyy-MM-dd");
return View(model);
}
In your view you will accept this view model and create the form accordingly:
#model WebApplication_Test.Models.TestModel
#using (Html.BeginForm())
{
#Html.TextBoxFor(m => m.FromDate)
<button type="submit">Search</button>
}
When you submit this form, you need an HTTP post action method to handle the submission. Because the view is bound to the view model, the action method will accept it as an input parameter:
[HttpPost]
public ActionResult Index(TestModel model)
{
// Do what you need to do
string date = model.FromDate;
return View(model);
}
Your way of doing it is also correct. I have just shown you an alternative way to do it. Some day you might have a huge form with many input values, then my approach will be 'cleaner'.
Try this:
1) Replace with [HttpPost] instead of [HTTPPost]
2) You should add name=" " for input like this:
<input id="fromDate" name="fromDate" type="text" class="datepicker" />

ASP.NET MVC Html.BeginForm decodes URL

I'm requesting ASP.NET MVC the controller using the URL like this:
http://mysite.com/controller/myaction/Invalid%23name%25x
where Invalid%23name%25x is a parameter to
public ActionResult MyAction(string id) {
return View();
}
The GET request works fine.
MyAction view looks like this:
#using (Html.BeginForm()) {
...
<input name="Save" type="submit" value="Save" />
}
The generated HTML is:
<form action="/Controller/MyAction/Invalid#name%x" method="post">
...
<input name="Save" type="submit" value="Save" />
</form>
When I click on "Save", the form gets posted and the POST request goes to
http://mysite.com/controller/myaction/Invalid#name%x
i.e. the initial URL is decoded. This means the the POST action receives only the first part of the parameter - "Invalid"
[HttpPost]
public ActionResult MyAction(string id, ...) {
return View();
}
How can I prevent Html.BeginForm from decoding the initial URLs in order to preserve the initial state?
Pass ActionName and Controller in your form
#using (Html.BeginForm("ActionName", "Controller")) {
I would personally recommend you not to use id as string because as you have seen string can have many words in it.. let it mean what usually it does(numeric value).
use something like http://mysite.com/controller/myaction?Name=Invalid%23name%25x
public ActionResult MyAction(string Name) {
return View();
}
I suppose this would work for you..

string Model, passing value to controller issue

I have a ASP.NET Razor view which binds to string. Its very simple:
#model string
#{
Layout = "~/Views/Shared/_Layout.cshtml";
}
Hello, #Model
#using(Html.BeginForm())
{
<fieldset>
<label for="name" style="color: whitesmoke">Name:</label>
<input type="text" id="name"/>
<br/>
<input type="submit" value="Submit"/>
</fieldset>
}
And a simple controller:
[HttpGet]
public ActionResult Index()
{
object model = "foo";
return View(model);
}
private string name;
[HttpPost]
public ActionResult Index(string name)
{
return View();
}
When I push the submit button, the Index Post action result triggers, but the 'string name' parameter is null. Isn't Razor smart enough to automatically bind this property to my controller from the view because the input id matches the name of the param on the controller? If not, how do I bind this? I know with a model with properties I can use Html.HiddenFor(m => m.Foo), but since there's no properties, I don't know how to call this method properly.. I can set it properly calling Html.Hidden("name","foo"), but I don't know how to pass a the value here. I know I can use jquery call such as:
#Html.Hidden("name", "$('input[id=name]').val())");
This literally sends the jquery string to the controller as the value... I'm not sure what to do at this point. Thanks!
It is smart enough to bind the property, just give your input a name which matches with the action parameter:
<input type="text" id="name" name="name" />

FormsCollection in Mvc Controller method - any other way of accessing?

I have a form which I want to post to ensure the page refreshes on posting the data (not the ajax way). The problem is really I only want to post the Id of the record and extract that in the controller method. I'm finding that the form is posting everything (I might not be able to resolve that since the same form is used for updating). But I'd like to be able to have the variable pop into the parameter of controller method rather than extracting from the FormCollection. I've tried the various parameters below, but all are being passed null. Any idea what the problem is?
I have the following in my controller method:
public ActionResult Delete(FormCollection collection)
{
var idToDelete = collection["Current.CommissionStructureId"].ToInt32();
}
// tried the following but none of them bind
public ActionResult Delete(int? Current_CommissionStructureId, int? CommissionStructureId, int? Id, int? id)
{
// none of the above are binding (set to null)
}
You should use HttpPost only for deleting of records
#using (Html.BeginForm()) {
<input type="hidden" name="CommissionStructureId" value="#item.CommissionStructureId" />
<p>
<input type="submit" value="Delete" />
</p>
<p>
#Html.ActionLink("Back to List", "Index")
</p>
}
[HttpPost]
public ActionResult Delete(int CommissionStructureId)
{
CommissionStructure commissionStructure = db.CommissionStructures.Find(CommissionStructureId);
db.CommissionStructures.Remove(commissionStructure);
db.SaveChanges();
return RedirectToAction("Index");
}
When your form value is named Current.CommissionStructureId the default modelbinder will bind it only to a class parameter called Current with a property called Id.
So your options are
Create a small class with one property Id and use it as your parameter type
or
Write a custom modelbinder

Basic Problem with Asp.net MVC UpdateModel(myClass)

In my Controller in a Asp.net MVC 1 app I want to use UpdateModel to populate a variable with POST data in my controller. I've looked at dozens of examples but even the most basic ones seem to fail silently for me.
Here's a very basic example that's just not working.
What am I doing wrong?
public class TestInfo
{
public string username;
public string email;
}
public class AdminController : Controller
{
public ActionResult TestSubmit()
{
var test = new TestInfo();
UpdateModel(test);//all the properties are still null after this executes
//TryUpdateModel(test); //this returns true but fields / properties all null
return Json(test);
}
}
//Form Code that generates the POST data
<form action="/Admin/TestSubmit" method="post">
<div>
<fieldset>
<legend>Account Information</legend>
<p>
<label for="username">Username:</label>
<input id="username" name="username" type="text" value="" />
</p>
<p>
<label for="email">Email:</label>
<input id="email" name="email" type="text" value="" />
</p>
<p>
<input type="submit" value="Login" />
</p>
</fieldset>
</div>
</form>
It looks like you're trying to get the controller to update the model based on the form elements. Try this instead:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult TestSubmit(TestInfo test)
{
UpdateModel(test);
return Json(test);
}
In your code, you're creating a new TestModel instead of letting the MVC runtime serialize it from the HttpPost. I've let myself get wrapped around the axel on this also, you're not the only one!
make properties of your public field:
public class TestInfo
{
public string username {get;set;}
public string email{get;set;}
}
I'm not too familiar with ASP.NET MVC, but shouldn't your TestSubmit method look more like this:
public ActionResult TestSubmit(TestInfo test)
{
UpdateModel(test);
return Json(test);
}
In the controller you should have two methods, one to respond to the GET, the other, if required is for responding to the POST.
So, firstly have a GET method:
public ActionResult Test ()
{
return View (/* add a TestInfo instance here if you're getting it from somewhere - db etc */);
}
Secondly, you'll need a POST method:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Test (TestInfo test)
{
return Json (test);
}
Notice that there's no UpdateMethod there, the ModelBinder would have done that for you.

Resources