I created my first MVC application in ASP.NET today. I have a datetime column "CreatedAt" which should be filled by current date without being visible in the input form. But the generated code has this code:
<div class="editor-field">
#Html.EditorFor(model => model.CreatedAt)
#Html.ValidationMessageFor(model => model.CreatedAt)
</div>
It displays a textbox in input form. I don't want to display it, instead it should be set in code behind. How can I do that?
ASP.NET MVC doesn't have a concept of a 'code-behind'. Quite simply, you send data from your View, and it's processed in your Controller.
So if this is an action that POSTs, then we can send data back to the controller, and even better, we can keep that data 'hidden' from the textbox view.
In your view, you should replace that with the following line:
#Html.HiddenFor(m => m.CreatedAt, DateTime.Now);
Then when the model is POSTed to the controller, the CreatedAt property will have the DateTime.Now filled in.
When you POST something, it has to go to an Action Method:
public class MyController : Controller
{
//other stuff
[HttpPost]
public ActionResult Edit(Product product)
{
product.CreatedAt // should equal the DateTime.Now set when you created the View
}
}
or you could set it in the controller after it POSTs:
public class MyController : Controller
{
//other stuff
[HttpPost]
public ActionResult Edit(Product product)
{
product.CreatedAt = DateTime.Now;
}
}
You may run into issues with Html.Hidden in this context, if you do, make sure to use the work around in place.
Related
First, sorry for my bad English. I am from Brazil. And I am a beginner in .NET MVC5.
I had a model class Task with ID plus 4 editable columns. When creating a new Task, the user must fill out only 2 of them (TaskType and Subject). The remaining two columns (UserID and CreationDate) must be populated with information obtained from the system: user ID and current date.
Then, in the Create Get, I put this two information in the ViewBag:
public ActionResult Create()
{
ViewBag.TaskTypeID = new DAO.TaskTypesDAO().ListOfTypes();
ViewBag.ApplicationUserId = User.Identity.GetUserId();
ViewBag.CreationDate = DateTime.Now;
return View();
}
And in View Create I tried to include this information first, without showing it:
First, I tried HiddenFor() and after with DisplayFor()
#Html.LabelFor(model => model.CreationDate, "Creation Date")
#Html.DisplayFor(model => model.CreationDate)
#Html.HiddenFor(model => model.CreationDate)
#Html.LabelFor(model => model.ApplicationUserId, "Creator")
#Html.DisplayFor(model => model.User.Id)
#Html.HiddenFor(model => model.ApplicationUserId)
DisplayFor() doesn't show anything.
And when I submit the Post Create method
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = "ID,TaskTypeID,Subject,CreationDate,ApplicationUserId")] Task task)
{
if (ModelState.IsValid)
{
db.Tasks.Add(task);
db.SaveChanges();
return RedirectToAction("Index");
}
ViewBag.TaskTypeID = new DAO.TaskTypesDAO().ListOfTypes();
ViewBag.ApplicationUserId = User.Identity.GetUserId();
ViewBag.CreationDate = DateTime.Now;
return View(tarefa);
}
The ModelState is invalid and task.CreationDate and task.ApplicationUserId are empty.
How can include this two information before the create post bind????
or else, How can include after and revalidate ModelState??
or .....
I don't understand why are you sending CreationDate and ApplicationUserId to the view through ViewBag and posting it back, this doesn't make any sense. Someone could easily edit the html and forge the ApplicationUserId or CreationDate.
I think your life would be much easier if you just use a strong typed view for this. My 2 cents for you would be:
Put everything you want to display inside a view model (dropdowns, lists, etc.).
Here you could propably use fill a SelectListItem to show your tasks types on a dropdown (or a enum if it's simple enough).
[HttpGet]
public ActionResult Create()
{
var viewModel = new CreateTaskViewModel
{
// [...] Put info that you need to display on the view here
}
return View(viewModel);
}
Get current date and user id when creating the task, that way the user can't forge this information. After creating the task, if you want to show it to the user, just use a Show view.
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(CreateTaskViewModel createTaskViewModel)
{
// If validation fails return to the view showing the errors
if (!ModelState.IsValid) { return View(createTaskViewModel); }
// [...] Create task object using info from viewModel
// and what else is necessary
db.Tasks.Add(task);
db.SaveChanges();
// [...] Fills view model to show info and redirect
return RedirectToAction("Show", showViewModel);
}
Use a model on the view and don't put sensitive information hidden on your html.
#model CreateTaskViewModel
#Html.LabelFor(model => model.TaskType)
// Or a dropdown list
#Html.TextBoxFor(model => model.TaskType)
#Html.LabelFor(model => model.Subject)
#Html.TextBoxFor(model => model.Subject)
Another thing that can be really usefull for you to study is the PRG (Post Redirect Get) pattern. It comes in handy when you have more complex action/ views.
I have a entity called WorkOrder which gets assigned to an Employee.
I want send an email notification when the workorder has been asigned. This can happen on my MVC Create or Edit Action (POST).
The problem i have is i have to do checks to see if the value has changed in the Edit to determine if i should send an email.
Is there a better place to call the SendEmail Function, like in the Entity Model itself?
If you are talking about posting from a view, you could create and bind the existing value to a hidden field in your form when loading the view. Then, on the POST to your action you can check to see if the value from the field is different from the one that is on the hidden field.
Example of View:
#using (Html.BeginForm("MyAction", "MyController")
{
#Html.HiddenFor(m => m.CurrentValue)
#Html.TextBoxFor(m => m.Value)
<input type="submit" value="submit" />
}
Example of Action GET
public ActionResult MyAction()
{
var viewModel = GetModelFromSomeWhere();
viewModel.CurrentValue = viewModel.Value;
return this.View(viewModel);
}
Example of Action POST
[HttpPost]
public ActionResult MyAction(ViewModel model)
{
if (model.Value != model.CurrentValue)
{
// It has changed! Send that email!
}
}
I need to pass multiple data ( probably 2 Html.DropDownList's selected values ) to MVC controller action method from MVC View ( .aspx). I think it would be from somehow Html.Hidden form , but how?
I am unable to get the selected value from Html.DropDownList and pass it as Html.Hidden("paramName", MvcStringSelectedValue) to controller's action.
My Code is :
based on<br />
<%: Html.DropDownList("Semester")%>
<%= Html.Hidden("strSemesterToBaseOn",returnedValueFromAbove)%>
<%: Html.ValidationSummary(true) %>
<input type="submit" value="Clone" />
<% } %>
<br/><br/>
Do I need to write the input tag of "submitt" 2 times or just only once?
Edit ( EXTRA CODE )
Controller's action method :
[HttpPost]
public ActionResult CloneSemesterData(string strSemesterToOrganize, string strSemesterToBaseOn)
{
.............................................................
..............................
}
HERE ( another Controller's method ) IS THE DROP DOWN LIST Filled with Semester values
public ActionResult DepartmentAdministration()
{
// Get list of semesters
var lr = new ListRepository();
ViewData["Semester"] = new SelectList(lr.ListSemester(3)); //this ListSemester(3) will generate the list with 3 strings ( e.g "WS 2012", "SS2010")
return View();
}
My View code in .aspx file is :
//this executes when radioButton ="Clone" is selected
<% using (Html.BeginForm("CloneSemesterData", "CourseNeededHours"))
{%>
<%= Html.DropDownList("Semester")%> // this is First drop down list box , from which selected value , I want to transfer as 1st parameter of controller's action method
<%: Html.ValidationSummary(true) %>
based On
<%= Html.DropDownList("Semester")%> //this is Second drop down list box, from which selected value, I want to transfer as 2nd parameter of controller's action method.
<input type="submit" value="Clone" />
<% } %>
ERROR:
Now, after fixing using Edit 2 : it is giving red lines under
as it is somehow not recognizing the ViewData["SemesterList"]...
"System.Web.Mvc.HtmlHelper does not contain a definition for 'DropDownList' and the best extension method overloaded 'System.Web.Mvc.Html.SelectExtensions.DropDownList(System.Web.Mvc.HtmlHelper, string,System.Collections.Generic.IEnumerable') has some invalid arguments".
Hope now it will clear, still ambiguity , do let me know then.
Regards
Usman
I am not really sure what you're asking here. You don't need any kind of hidden field to post the selected values of a dropdown. Your Dropdownlist code is invalid to begin with.
Typically you have something like this:
<%= Html.DropDownList("SemesterToOrganize", GetSemesterToOrganize()) %>
<%= Html.DropDownList("SemesterToBaseOn", GetSemesterToBaseOn()) %>
And in your controller:
[HttpPost]
public ActionResult MyAction(string SemesterToOrganize, string SemesterToBaseOn) {
// your code.
}
EDIT:
Based on what you've told us. You are relying on the behavior of MVC of populating the DropDownList because you are adding your list to the ViewData with the same name as your dropdownlist. This won't work for you. You will have to populate each dropdown list seperately.
In your controller, do something like this:
public ActionResult MyAction ()
{
ViewData["SemesterList"] = // list of semesters
return View();
}
Then, in your view you have:
<%= Html.DropDownList("SemesterToOrganize", ViewData["SemesterList"]) %>
<%= Html.DropDownList("SemesterToBaseOn", ViewData["SemesterList"]) %>
then your post method
[HttpPost]
public ActionResult MyAction(string SemesterToOrganize, string SemesterToBaseOn) {
// your code.
}
If you want to continue to argue that you can do it your way, then you won't solve your problem. Each dropdown must have it's own unique id, otherwise it will not post correctly. The only way to solve this problem is to give each it's own unique id. That breaks the behavior of the drop down automatically getting the data, so you MUST specify the list of data explicitly.
So stop arguing that this is an unimportant part of the problem. It's not. It's key to the problem.
EDIT2:
Based on your code above:
<%= Html.DropDownList("strSemesterToOrganize", (SelectList)ViewData["Semester"]) %>
<%= Html.DropDownList("strSemesterToBaseOn", (SelectList)ViewData["Semester"]) %>
That's all you need
If you had just given us this, and didn't argue, this would been solved a lot easier.
// Try this. Change names and put in the appropriate namespace.
//Your view
#model MvcApplication2.Models.CloneSemesterDataViewModel
#Html.LabelFor(x => x.SemesterToOrganize)
#Html.DropDownListFor(x => x.SemesterToOrganize, Model.ListofSemestersToOrganize)
--------
#Html.LabelFor(x => x.SemesterToBaseOn)
#Html.DropDownListFor(x => x.SemesterToBaseOn, Model.ListofSemestersToBaseOn)
//view model
namespace MvcApplication2.Models
{
public class CloneSemesterDataViewModel
{
public string SemesterToOrganize { get; set; }
public IEnumerable<SelectListItem> ListofSemestersToOrganize
{
get
{
return new List<SelectListItem> { new SelectListItem { Text = "SS2012" , Value = "SS2012"} };
}
}
public string SemesterToBaseOn { get; set; }
public IEnumerable<SelectListItem> ListofSemestersToBaseOn
{
get
{
return new List<SelectListItem> { new SelectListItem { Text = "SS2012", Value = "SS2012" } };
}
}
}
}
----------
Controller.
[HttpPost]
public ActionResult CloneSemesterData(CloneSemesterDataViewModel viewModel)
{
//viewModel.SemesterToBaseOn
//viewModel.SemesterToOrganize
}
// This should do the trick.
When passing a view model (as below) to a view, how can I ensure that the checkboxes I'm creating (mapped to item "Product" in here) get passed back to the controller?
I've included my view model and "post" product controller below.
Unfortunately, when posted back to the controller, "Products" is null.
namespace MyProject.Models
{
public class ChartViewModel
{
public Chart ChartItem { get; set; }
public IEnumerable<Product> Products { get; set; }
}
}
Controller:
[Authorize]
[ValidateInput(false)]
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Edit(ChartViewModel objChartViewModel)
{
if (!TryUpdateModel(objChartViewModel))
{
return View(objChartViewModel);
}
else
{
} return View("Details", objChartViewModel);
}
How the checkboxes are added to my view, mapped to the "Product" object within my view model:
#{
foreach (MyProject.Models.Product objProduct in Model.Products)
{
#Html.CheckBox("product" + objProduct.Id, Model.ChartItem.ChartProducts.Select(t => t.ProductId).Contains(objProduct.Id));
#String.Format("{0} {1}", objProduct.Manufacturer.Name, objProduct.Name);<br />
}
}
You can send your lists (IEnumerable<T>) down to the view but when they don't come back up to the controller. The only properties of your ViewModel that have values are those with exactly matching items in the forms collection. So add a properties to your ViewModel like SelectedProductID.
This sets up a drop down list that sends its selected value back to the controller:
<div class="editor-field">
#Html.DropDownListFor(m => m.SelectedEmployeeID,
new SelectList(Model.EmployeeList, "EmployeeID", "EmployeeName"), "--Please select an Employee--")
#Html.ValidationMessageFor(model => model.SelectedEmployeeID)
</div>
Notice: the property being set is SelectedEmployeeID but this comes from the EmployeeList.
In your case the values many be in the collection (only "checked" checkboxes get sent in a post) so you could do:
string value = collection["myProductID"];
if its there its checked.
sorry for the messiness, this was in a bit of a rush. See this for more info:
MVC 3 form post and persisting model data
Once again I'm confronted with a "This shouldn't be this ?*!# hard" situation.
Problem: I want to use a form in MVC for creation of an object. One of the elements of the object is a set of limited choices - a perfect candidate for a drop down list.
But if I use a SelectList in my model, and a drop down list in my View, and then try to post the Model back to my Create method, I get the error "Missing Method Exception:No Parameterless constructor for this object". Exploring the MVC source code, it appears that in order to bind to a model, the Binder has to be able to create it first, and it can't create a SelectList because there is no default constructor for it.
Here's the simplified code:
For the model:
public class DemoCreateViewModel
{
public SelectList Choice { get; set; }
}
For the controller:
//
// GET: /Demo/Create
public ActionResult Create()
{
DemoCreateViewModel data = new DemoCreateViewModel();
data.Choice = new SelectList(new string[] { "Choice1", "Choice2", "Choice3" });
ViewData.Model = data;
return View();
}
//
// POST: /Demo/Create
[HttpPost]
public ActionResult Create(DemoCreateViewModel form)
{
try
{
// TODO: Add insert logic here
return RedirectToAction("Index");
}
catch
{
return View();
}
}
And for the View:
<fieldset>
<legend>Fields</legend>
<%= Html.LabelFor(model => model.Choice) %>
<%= Html.DropDownListFor(model => model.Choice, Model.Choice) %>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
Now, I know I can MAKE this work by dropping back 10 yards and punting: bypass model binding and drop back to the FormCollection and validate and bind all the fields myself, but there's got to be a simpler way. I mean, this is about as simple a requirement as it gets. Is there a way to make this work within the MVC ModelBinding architecture? If so, what is it? And if not, how come?
Edit: Well, I have egg on my face, but maybe this will help someone else. I did some more experimenting and found a simple solution that seems to work.
Provide a simple value (string or integer, depending on what your select list value type is), and name that as the model element that you bind to. Then provide a second element as the select list of choices, and name it something else. So my model became:
public class DemoCreateViewModel
{
public string Choice { get; set; }
public SelectList Choices { get; set; }
}
And then the DropDownListFor statement in the View becomes:
<%= Html.DropDownListFor(model => model.Choice, Model.Choices) %>
When I do this, the submit button correctly binds the choice made in the form to the string Choice, and submits the model back to the second Create method.
Here is one approach:
#Html.DropDownListFor(model => model.Choice,
ViewBag.Choices as SelectList,
"-- Select an option--",
new { #class = "editor-textbox" })
Notice that I use ViewBag to contain my SelectList. This way when you post back, the client doesn't send the entire select list up to the server as part of the model.
In your controller code, you just need to set the view bag:
ViewBag.Choices = new SelectList(....
Consider creating a different view model for your post action without the SelectList property:
public class DemoCreateViewModelForUpdate
{
public string Choice { get; set; }
}
Then you can always map from the DemoCreateViewModelPost instance to an DemoCreateViewModel instance if the model state is invalid and you want to re-show the view. I tend to prefer everything needed by the view to be in my display view model class, so using a separate update only view model let's me keep things slim and trim for the trip back to the server.
In your view, you'd do:
#Html.DropDownListFor(m => m.Choice, Model.Choices)
as in the previous answer, so no unnecessary data would round trip.