I am trying to prefill some HTML form fields with data contained in the request.
My original setup, before any changes, looks like this:
Inside TicketController:
//GET: Retrieve the form
[Route("createticket")]
public ActionResult CreateTicket()
{
//Irrelevant code ommited
return View("CreateTicket");
}
//POST: Submit the form
[HttpPost]
[Route("createticket")]
public ActionResult CreateTicket(CreateTicketModel createTicketModel)
{
//Irrelevant code ommited (saving the submitted data)
return RedirectToAction("ViewTicket", new { ticketId = ticket.TicketId });
}
I could pass the data as a GET parameter, but the problem is often the data will be too long to be contained in the URL (2000+ characters).
The only solution I can currently think of is to make retrieving the form a POST instead of a GET, so I can use POST parameters to prefill the form. I have two problems with this solution:
I will have two possible POST requests at the /createticket path, which will be conflicting
It just doesn't feel right to use a POST request to retrieve a form
How could I tackle this?
Why cant you just instantiate a CreateTicket model and set the properties with the data you need to be prefilled? Via model binding the values of your properties will be set into the HTML formfields.
[Route("createticket")]
public ActionResult CreateTicket()
{
//Irrelevant code ommited
var data = GetSomeDataYouWantToPrefill();
var model = new CreateTicketModel(data);
return View("CreateTicket", model);
}
Then in the constructor of the model you can assign the values in the data object to the properties. In your view with the Html.TextboxFor method you can bind to the properties in the model
Related
I've got a page in an app I'm building. The page contains a few bits and pieces, then a partial view that loads a different view depending on what's selected from a dropdown. Each of the options from the dropdown has a different view associated with it, and each view has its own fields and model.
Whatever the view is that loads, I'm performing the same action - I'm serializing the model and storing the XML in a database. This is always the case, and there is no unique processing based on the views/models (other than the fact that the fields are different). All models inherit from the same base class for serialization purposes.
I wanted to be able to do something like:
public ActionResult SubmitPartialView<T>(T model)
{
BaseClass baseClassModel = (BaseClass)(object)model;
// serialize and save to database
}
But MVC doesn't allow this - "cannot call action on controller because the action is a generic method".
If I try passing the BaseClass in as a parameter itself, it only contains the properties of the base class and therefore none of the model's properties.
Is there no other option other than to create a separate action for every single view that can submit, and make each one call a separate method that handles the logic?
I see this question is a little old, but if it helps anyone - I was doing some reading with dynamic models and MVC, saw this and it led me to think of a possible solution. Not sure why you would want to have dynamic models. But the great thing with MVC is, you can!
So;
[HttpPost]
public ActionResult SubmitPartial([DynamicModelBinder] dynamic model)
{
// Our model.ToString() serialises it from the baseModel class
var serialisedString = model.ToString();
// do something .. echo it back for demo
return Content(serialisedString);
}
And the model binder is something like this;
public class DynamicModelBinder : DefaultModelBinder
{
public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
var currentModel = controllerContext.HttpContext.Request.Form["CurrentModel"];
if (currentModel == "CompanyModel")
{
Type customModel = typeof(CompanyModel);
bindingContext.ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(null, customModel);
}
if (currentModel == "UserModel")
{
Type customModel = typeof(UserModel);
bindingContext.ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(null, customModel);
}
return base.BindModel(controllerContext, bindingContext);
}
}
hth
I have following controller method
public class ReportController : Controller
{
// GET: Report
public ActionResult Incomplete_Product_Report()
{
return View();
}
}
I want to call following method which is ShowReport() inside the code behind file in aspx web-form .
private void ShowReport()
{
//DataSource
DataTable dt = GetData(type.Text, category.Text,subsidary.Text,country.Text, dateHERE.Text);
....................
}
private DataTable GetData(string type, string category, string country, string subsidary, string dateHERE)
{
................
}
then ShowReport() method call and pass parameters call GetData()
I have following view form to filter results ,
http://s9.postimg.org/95xvv21z3/wewrwr.png
also I have following aspx webform to generate report
http://s7.postimg.org/iz4zdety3/44r3.png
once I click "Generate Report" button in view form I should be able to pass parameters generate results in webform and show Microsoft report wizard(RDLC) like 2nd image .
Now I have done these things separately, I want to link those together
You asked in the question title about calling method inside code behind file from controller, and it's simple. Since code behind files are nothing but the class, you can call them just like any other method's class something like this
public ActionResult ShowForm()
{
MvcApplication1.Webforms.Reportform form = new Webforms.Reportform();
form.ShowForm();
}
But I don't think you are looking for that. As you explain with your images, you want to call the ASPX functionality on the "Generate Report" view that is generated in MVC. You can do that in two ways, either you collect all the necessary parameters on the client side (javascript, jquery etc), and then from there directly redirect to your aspx with query string such as
$("#generateReport").on('click', function() {
var category = $("#category").val();
//similarly do for all the fields
if (category!= undefined && category != null) {
window.location = '/Report.aspx?category=' + category;
}
});
In this case, you will also need to write logic inside the Report.aspx to reach values from query string and then call your showreport method with appropriate inputs.
Another way to do this would be to post the GenerateReport back to MVC, collect parameters and then send it further to aspx, something like this
[HttpPost]
public ActionResult GenerateReport(FormCollection collection)
{
try
{
string category = collection["category"];
// TODO: Add similar information for other fields
return Redirect(string.format("/Report.aspx?category={0}",category)); //add additional parameters as required
}
catch
{
return View();
}
}
This would cause an extra return trip though as compared to direct call from client side script.
what I try to do is to bind every incomming value from my response to a string or stringlist dynamicly / generic.
So assume I would know each POST-Value of my request, e.g.
string1 = Test
string2 = Test2
I would write:
[HttpPost]
public ActionResult DoFoo(string string1, string string2)
{
}
or
[HttpPost]
public ActionResult DoFoo(string string1, [Bind(Prefix = "string2")string myString2)
{
}
My situation know is, that I have X strings with my post request. So I dont know the exact number nor the names to catch in my backend.
How to catch every given Post-value without knowing this / how to catch the values dynamicly?
I don't feel that why you have to use Prefix with BIND, when you have to bind every incoming field of response. Bind is not a good choice for that. You can use bind if you have multiple entities at the same time. Reference here
that I have X strings with my post request.
If you have to use all the fields then you can use FormCollection or Model object to receive those fields. FormCollection automatically receive all the fields from view and bind them to a collection. See this for proper example. And a code snippet is below for reference.
[HttpPost]
public ActionResult Create(FormCollection collection)
{
try
{
Student student = new Student();
student.FirstName = collection["FirstName"];
student.LastName = collection["LastName"];
DateTime suppliedDate;
DateTime.TryParse(collection["DOB"], out suppliedDate);
student.DOB = suppliedDate;
student.FathersName = collection["FathersName"];
student.MothersName = collection["MothersName"];
studentsList.Add(student);
return RedirectToAction("Index");
}
catch
{
return View();
}
}
However if you have to deal with only one particular field/set of fields then you can use either Include or Exclude as per your convenience with BIND. Example shown here and code snipped is added below.
In following way you are telling that you only want to include "FirstName" of User model while receiving the form content. Everything else will be discarded.
[HttpPost]
public ViewResult Edit([Bind(Include = "FirstName")] User user)
{
// ...
}
And in following example you are telling that, please exclude "IsAdmin" field while receiving the fields. In this case, value of IsAdmin will be NULL, irrespective of any data entered/modified by end-user in view. However, in this way, except IsAdmin, data rest of the fields will be available with user object.
[HttpPost]
public ViewResult Edit([Bind(Exclude = "IsAdmin")] User user)
{
// ...
}
I have an MVC2 Application that uses MVVM pattern. I am trying use Data Annotations to validate form input.
In my ThingsController I have two methods:
[HttpGet]
public ActionResult Index()
{
return View();
}
public ActionResult Details(ThingsViewModel tvm)
{
if (!ModelState.IsValid) return View(tvm);
try
{
Query q = new Query(tvm.Query);
ThingRepository repository = new ThingRepository(q);
tvm.Things = repository.All();
return View(tvm);
}
catch (Exception)
{
return View();
}
}
My Details.aspx view is strongly typed to the ThingsViewModel:
<%# Page Title=""
Language="C#"
MasterPageFile="~/Views/Shared/Site.Master"
Inherits="System.Web.Mvc.ViewPage<Config.Web.Models.ThingsViewModel>" %>
The ViewModel is a class consisting of a IList of returned Thing objects and the Query string (which is submitted on the form) and has the Required data annotation:
public class ThingsViewModel
{
public IList<Thing> Things{ get; set; }
[Required(ErrorMessage="You must enter a query")]
public string Query { get; set; }
}
When I run this, and click the submit button on the form without entering a value I get a YSOD with the following error:
The model item passed into the dictionary is of type
'Config.Web.Models.ThingsViewModel', but this dictionary
requires a model item of type
System.Collections.Generic.IEnumerable`1[Config.Domain.Entities.Thing]'.
How can I get Data Annotations to work with a ViewModel? I cannot see what I'm missing or where I'm going wrong - the VM was working just fine before I started mucking around with validation.
I don't think the problem is with the validation.
Change this line;
tvm.Things = repository.All(); //Is this the Linq extension method 'All()'?
to this
tvm.Things = repository.ToList();
I don't know what this is or what it does;
new ThingRepository(q);
It takes a string parameter and returns some kind of Linq IQueriable or List? If that's returning something else it could be causing the problem.
Do you have client-side validation enabled? It might even be a quick hacky-fix, but regarding the error message - it's tough to say without extra info. Could you post your View and the rendered Html?
What does your route for Details look like?
If you set a breakpoint at the start of the Details method, does it get hit when you click on the submit button?
It looks like you could just declare your ThingsViewModel like so:
public class ThingsViewModel: IEnumerable<Thing>
and then implement the interface as appropriate to access the Things list.
I think that ASP.NET MVC might be trying to map your view to the wrong controller. When you return the view you might need to specify the view file name you're trying to use.
return View("ViewName")
I have a page which is strongly typed to my "User" class. When it is loaded, I load it by Id from the database and pass it to the view.
When the edit form is posted, the object gets posted to the controller method fine, with some other parameters. The object has its properties filled from the form, but it's ID (which obviously isnt on the form) doesnt get posted.
Even when I manually set it to an ID in code and try and save my context, nothing happens on the database.
Here is a rough view of the code with stuff taken out for brevity.
public ActionResult MyProfile()
{
ViewData["Countries"] = new SelectList(userService.GetCountries(), "id", "name");
return View(userService.GetById(CurrentUser.id));
}
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult MyProfile(MSD_AIDS_Images_Data.LINQRepositories.User user, string password2)
{
user.id = CurrentUser.id; //user id isn't posted, so need to reassign it
userService.SaveChanges();
}
I have written code like this a dozen times and it has worked, what is going wrong?
EDIT
When I debug the user object, it's PropertyChanged and PropertyChanging properties are set to NULL
The User object coming into the MyProfile method is not associated with a LINQ context. You need to use explicit binding using UpdateModel, e.g.:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult MyProfile(int id, string password2)
{
MSD_AIDS_Images_Data.LINQRepositories.User user = <LINQ query to load user by id>;
UpdateModel(user); // updates the model with form values
userService.SaveChanges();
}
Note you can implement a custom model binder that does this before calling your controller method so you can accept User as a parameter, but I'm assuming you haven't done this.
I fixed the Model binding issues by using an Update Model overload which allows you to specifiy which properties in the model you wish to update:
string[] includeProperties = {"password", "firstname", "lastname", "email", "affiliation", "countryId"};
UpdateModel(user, includeProperties);