Pass a simple ViewModel from a View to a Controller via hidden field - asp.net

I am trying to pass a simple object containing only one field (a DateTime) from my view back to my controller.
I have a ViewModel that looks like this:
public class TheViewModel
{
public DateTime StartTime { get; set; }
}
I have a controller post method that looks like this
[HttpPost]
public async Task<ActionResult> StartNew(TheViewModel viewModel)
{
....
}
In my view, my model is set to TheViewModel, and I am trying to simply send the same value for the StartTime field of the ViewModel back to the Controller:
#using (Html.BeginForm())
{
#Html.HiddenFor(m => m.StartTime, new { id = "StartTimeField" })
<input type="submit" value="Start" />
}
However, the value of the StartTime field is always the default date time. I have verified that this is not the value for the time sent to the view (by looking at the page source)
What am I mnissing?

Why are you creating a new object inside the HtmlFor? Why not just #Html.HiddenFor(m=>m.StartTime) ?

Related

How should I send viewmodel to Create method in Controller?

I am trying to send data from a view to a controller Create method. But the view model parameter is getting null values when the create method is called.
In my view I want to add a item and show a list of added items.
I have tried to send data to the create method but its view model parameter is getting null values.
In the following code whenever Create method is hit the value of p.posts and p.post is null. How can I get the value of p.post and p.posts here?
Controller method
public ActionResult Create(PostsViewModel p) {}
View Model
public class PostsViewModel
{
public IEnumerable<Post> posts;
public Post post;
}
View
#model NotesWebApplication.ViewModels.PostsViewModel
...
#using (Html.BeginForm()) {
...
#Html.EditorFor(model => model.post.postText, new { htmlAttributes = new { #class = "form-control" } })
...
<input type="submit" value="Create" class="btn btn-default" />
Also in my Create method if I wanted to add Bind then which should be added
[Bind(Include="postText")]
or
[Bind(Include="post.postText")]
update
I made the following changes in PostsViewModel class
public class PostsViewModel
{
public IEnumerable<Post> posts { get; set; }
public Post post { get; set; }
}
and the Create method in the controller is changed to
[HttpPost]
public ActionResult Create([Bind(Include="post, posts")]PostsViewModel p) {}
This is what the httpget Create method looks like
// GET: Posts/Create
public ActionResult Create()
{
PostsViewModel postsViewModel = new PostsViewModel();
postsViewModel.posts = db.Posts;
postsViewModel.post = new Post();
return View(postsViewModel);
}
Now when I submit the form p.post in the controller parameter receives the desired value. But p.posts remains null. Why is this hapenning?
I guess the reason is u don`t have an instance of your post object. Try to make your viewModel something like this:
public class PostsViewModel
{
public string PostText {get;set;} // don`t forget to make it like property, not just a field
}
and then create an instance in your controller:
public ActionResult Create(PostsViewModel p)
{
Post post = new Post{ postText = p.PostText};
//and do what you want with it
}

MVC ViewModel returns ArgumentNullException

I am having a problem returning values to the controller when using a ViewModel.
For clarity I have simplified the code below where the original has many more fields.
When the page is loaded, the value in the hidden field is as expected. However when the form is submitted the value in the field is not being sent and instead I get an ArgumentNullException.
Please can you advise on what I am doing wrong.
View
#model Project.Models.SCView
#using (Html.BeginForm("ScorecardEdit"))
{
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
#Html.HiddenFor(model => model.FV.ID)
<input type="submit" value="Save" />
}
Model
public class FixView
{
public int ID { get; set; }
[DisplayFormat(DataFormatString = "{0:ddd dd/MM/yyyy}")]
public DateTime MatchDate { get; set; }
}
public class SCView
{
public FixView FV { get; set; }
public SCView()
{
this.FV = new FixView();
}
}
Controller
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult ScorecardEdit(SCView ReturnSC)
{
}
The code that you have should be working as MVC should be able to map your FV.ID property as expected as the HiddenFor() helper will generate the proper names to handle mapping it :
Using the same code that you provided, the debugger demonstrated the following after submitting the form :
The issue here sounds like you have a few other properties, possibly collection-based ones that use the DropDownListFor() helpers that have collections which are not posted to the server, so when you attempt to use the model you have to render populate one of those helpers, you are getting your ArgumentNullException.

Passing partially populated Model to Create page without initializing other properties to default

ASP.NET MVC 5
I have a strongly typed create page.
My model is like this,
public class DemoViewModel
{
public int PackageId { get; set; }
public DateTime Date { get; set; }
}
my view is like this,
#model DemoViewModel
<div >
#Html.TextBoxFor(model => model.PackageId)
#Html.ValidationMessageFor(model => model.PackageId)
</div>
<div >
#Html.TextBoxFor(model => model.Date)
#Html.ValidationMessageFor(model => model.Date)
</div>
When the page is called, the PackageId is selected. So in my control I call the view like this,
public ActionResult Create()
{
return View(new DemoViewModel() {
PackageId = 1
});
}
The problem is that when I call like above the Date field get initialized to default and it shows "1/1/0001 12:00:00 AM" in the textbox. It is ok (that means empty when load) if I call the view without an model instance. But I need to pass some initial Model data to the view.
How can I pass an partially populated instance of mode without showing default values in other fields?
If you don't want your properties to be set to default value, you have to make them nullable by adding ?
So, in your case Date would be:
public DateTime? Date { get; set; }
More info here
You can create a wrapper method which takes as a parameter your model, binds it to the view, but excludes the Date property while binding, and then returns that view.
private GetCreateViewExcludingDate([Bind(Exclude="Date")]DemoViewModel demoVM)
{
return View("Create",demoVM);
}
And in your Create action method,
public ActionResult Create()
{
return GetCreateViewExcludingDate(new DemoViewModel() {
PackageId = 1
});
}
Of course, the other alternative is what #Mark said. Make your Date property nullable. But then you will have to check for null values in your controller whenever you use it.
EDIT:
As #sajith mentions in the comments, this solution DOES NOT WORK.
EDIT 2:
Here is an alternate solution. The textbox can be cleared on the client side using JavaScript/jQuery.
$(document).ready(function () {
$("input#Date").val("");
});
I tried this and it works fine.

Reading in RouteValues to Controller

so I have a Url Action
Create new teacher & assign to account.
That passes in two routeValues: createAndAssign, and teacherID.
Now when I go to my Teacher/Create page, my URL is like so:
.../Teacher/Create?createAndAssign=True&teacherID=ea817321-5633-4fdc-b388-5dba2c4a728e
Which is good, I want this. Now when I POST to create my teacher, how do I grab createAndAssign and teacherID value?
You can set the Querystring value in a hidden variables in the form and render in your GET action method and accept that in your POST action method.
View rendered by your GET Action
#using (Html.BeginForm())
{
//Other form elements also
#Html.Hidden("teacher",#Request.QueryString["teacherID"] as string)
#Html.Hidden("createAndAssign",#Request.QueryString["createAndAssign"]
as string)
<input type="submit" />
}
and now have a teacher parameter and createAndAssign parameter in your HttpPost action method so that it will be available when you submit the form.
[HttpPost]
public ActionResult Create(string teacher,string createAndAssign)
{
//Save and Redirect
}
If your view is strongly typed (which is my personal preference), it is quite easy,
public ActionResult GET(string teacherID,string createdAndAssing)
{
var yourVMObject=new YourViewModel();
yourVMObject.TeacherID=teacherID;
yourVMObject.CreateAndAssign=createdAndAssing;
return View(createdAndAssing);
}
and in your strongly typed view,
#model YourViewModel
#using (Html.BeginForm())
{
//Other form elements also
#Html.HiddenFor(x=>x.TeacherID)
#Html.HiddenFor(x=>x.CreateAndAssign)
<input type="submit" />
}
And in your POST action
[HttpPost]
public ActionResult Create(YourViewModel model)
{
//look for model.TeacherID
//Save and Redirect
}
you can get the value from the query string or as params of the controller like
var x =Request.QueryString["createAndAssign"];
or
public ActionResult Create(bool createAndAssign, string teacherID){
return View();
}

ASP.NET MVC3: can Action accept mulitple model parameters?

For example:
class MyContoller
{
[MyCustomAttribute]
public ActionResult MyAction(ModelX fromRequest, ModelY fromSession, ModelZ fromCookie)
{
string productId = fromRequest.ProductId;
string userId = fromSession.UserId;
string cultureName = fromCookie.CultureName;
}
}
Reason:
I don't want to visit Request, Session and HttpContext in the controllers, and the default idea of MVC3 which passing models to actions is very great.
I want the number of parameters of MyAction is easy to change. For example, if I add a new parameter, the system will try to look for values in Request, Session or Cookies by the name or type of the parameter (I think custom ModelBinders may be required for cookie values) and pass the filled model to my action. I don't have to write extra code.
Can the custom attribute (MyCustomAttribute in the example) accomplish this idea?
I am not sure I follow you about the custom attribute. What are you expecting the custom attribute to do?
Yes, an action method can take as many model parameters as you want. Obviously, only one can be bound in any given request (because a view can only have one model). Whichever one is found first will be bound, and the others will be null.
So let's say you have the following:
public class ModelX {
public string X {get;set;}
}
public class ModelY {
public string Y {get;set;}
}
public class ModelZ {
public string Z {get;set;}
}
And you have an action method like this:
public ActionResult DoIt(ModelX x, ModelY y, ModelZ z)
{
return View();
}
And in your DoIt.cshtml you have the following:
#model ModelZ
#using(Html.BeginForm()) {
#Html.TextBoxFor(x => x.Z)
<input type="submit"/>
}
If you type something into the textbox and submit, then the model binder will bind a ModelZ with the value you entered and ModelX and ModelY will be null.
If you mean can an action method bind multiple models simultaneously, then I would have to ask you.. How exactly do you plan to have a view have more than one model? You can certainly create a wrapper model to contain the multiple models, but a view can only have one.
Create a composite ViewModel class that incorporates ModelX, ModelY and ModelZ. You can then populate an instance of your new ViewModel class, and pass that to your controller method.
public class XYZViewModel
{
public ModelX fromRequest { get; set; }
public ModelY fromSession { get; set; }
public ModelZ fromCookie { get; set; }
}
public class MyController
{
[MyCustomAttribute]
public ActionResult MyAction(XYZViewModel myModel)
{
string productId = myModel.fromRequest.ProductId;
string userId = myModel.fromSession.UserId;
string cultureName = myModel.fromCookie.CultureName;
}
}
You can always pass multiple parameters to your controller action, yes. The key is to make sure they are properly serialized in the request. If you're using a form, that means using the Html helper methods.
For example, let's say you want an action like this:
public ActionResult Multiple(ModelA a, ModelB b)
{
// ...
}
You could create simple partial view for each model:
#model MyProject.Models.ModelA
#Html.EditorForModel()
Then in your Multiple view, render the partial views like so:
#{ using (Html.BeginForm("Multiple", "MyController", FormMethod.Get))
{
#Html.Partial("A", new MyProject.Models.ModelA())
#Html.Partial("B", new MyProject.Models.ModelB())
<input type='submit' value='submit' />
}
I set the method to GET here so that you can easily see how MVC passes the parameters. If you submit the form, you'll see that MVC successfully deserializes each object.

Resources