I have a main Contact and ContactViewModel . How do I get contact model and update it to the database ?
[HttpPost]
public ActionResult EditContact(ContactFormViewModel contactView) {
}
I was doing like this before I needed ViewModel
[HttpPost]
public ActionResult EditContact(int id, FormCollection collection) {
Contact contact = repository.GetContactById(id);
if (TryUpdateModel(contact, "Contact")) {
repository.Save();
return RedirectToAction("Index");
return View(new ContactFormView Model(contact));
}
}
It's a bit easier when you have a view model (you can forget about FormCollection and TryUpdateModel):
[HttpPost]
public ActionResult EditContact(ContactViewModel contact)
{
if (ModelState.IsValid)
{
// the model is valid => we can save it to the database
Contact contact = mapper.Map<ContactViewModel, Contact>(contact);
repository.Save(contact);
return RedirectToAction("Index");
}
// redisplay the form to fix validation errors
return View(contact);
}
where mapper converts between the view model and models. AutoMapper is a great choice for this task.
Related
I have this mvc controller that add a customer to the database called CustomerController. This Controller has one ActionResult called Add. It works as it is but I want to display a status message after the user hit submit, and I want all information added to the model be kept on the page as is. How can I keep the all the entered text in the form fields and also show a status message after the form has been posted?
public ActionResult Add()
{
// This is the empty view the user see when he is about to add new form data
return View(new CreateSupplierViewModel());
}
public ActionResult AddNew(CreateSupplierViewModel model)
{
// I post to this and need to display the status of this on the view with the entered text fields as is
return RedirectToAction("Add", "Supplier");
}
You need to refactor your code as below :
The CustomerController :
public ActionResult Add()
{
return View(new CreateSupplierViewModel());
}
public ActionResult Add(CreateSupplierViewModel model)
{
return View(model);
}
public ActionResult AddNew(CreateSupplierViewModel model)
{
return RedirectToAction("Add", "Supplier", model);
}
Your SupplierController
public ActionResult Add(CreateSupplierViewModel model)
{
//save the entity
Viewbag.Message ="submit result";
return RedirectToAction("Add", "Customer", model);
}
The Customer/Add.cshtml (show the submit result in view)
#if( Viewbag.Message != null)
{
<p> #Viewbag.Message </p>
}
In the Action method code below, all the data from the form is included as parameters, but what if I also would like to add to the table something more like the users Entity ID
var userID = User.Identity.GetUserId();
How can I add that to the Customers object before it's written to the table? One way could perhaps be to add it into the HTML and the form as hidden, but I guess it would be unsecure to have that visible in the code?
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = "ID,FirtsName,LastName,PhoneNumber")] Customers customers)
{
var userID = User.Identity.GetUserId();
if (ModelState.IsValid)
{
db.Customers.Add(customers);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(customers);
}
You can just set some property before save.
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = "ID,FirtsName,LastName,PhoneNumber")] Customers customers)
{
if (ModelState.IsValid)
{
customers.UserID = User.Identity.GetUserId();
db.Customers.Add(customers);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(customers);
}
I'd like show a form with some field (one in the example), submit it, save and display the same page with a reset of all fields. The probelm when I submit, I go the "Save" action but when I display the view the form is still filled in.
The model :
public class TestingModel
{
public string FirstName { get; set; }
}
The controller :
public class ChildController : Controller
{
public ActionResult Index()
{
TestingModel model = new TestingModel();
return View(model);
}
public ActionResult Save(TestingModel model)
{
Console.WriteLine(model.FirstName); //OK
//Save data to DB here ...
TestingModel testingModel = new TestingModel() { FirstName = string.Empty };
return View("Index", testingModel);
}
}
The view :
#using (Html.BeginForm("Save", "Child",FormMethod.Post))
{
#Html.TextBoxFor( m => m.FirstName)
<input type="submit" id="btSave" />
}
When Id debug to the view, in "Immediat window" Model.FirstName = "" but when the page is show I still have the value posted. I tried a ReditrectionToAction("Index") at the end of the Save method but same result.
Do you have an idea ?
Thanks,
If you want to do this you need to clear everything that's in the ModelState. Otherwise HTML helpers will completely ignore your model and use data from ModelState when binding their values.
Like this:
[HttpPost]
public ActionResult Save(TestingModel model)
{
//Save data to DB here ...
ModelState.Clear();
TestingModel testingModel = new TestingModel() { FirstName = string.Empty };
return View("Index", testingModel);
}
or simply redirect to the Index GET action in case of success:
[HttpPost]
public ActionResult Save(TestingModel model)
{
//Save data to DB here ...
return RedirectToAction("Index");
}
Try to return Index view without any model
return View("Index");
You should be posting your form back to the same ActionResult
public ActionResult Index()
{
TestingModel model = new TestingModel();
return View(model);
}
[HttpPost]
public ActionResult Index(TestingModel model)
{
Console.WriteLine(model.FirstName); //OK
//Save data to DB here ...
return RedirectToAction("Index");
}
You would be able to use the parameterless overload for BeginForm too
#using(Html.BeginForm())
{
//form
}
I am redirecting the view from [HttpPost] method to [HttpGet] method. I have gotten it to work, but want to know if this is the best way to do this.
Here is my code:
[HttpPost]
public ActionResult SubmitStudent()
{
StudentViewModel model = TempData["model"] as StudentResponseViewModel;
TempData["id"] = model.Id;
TempData["name"] = model.Name;
return RedirectToAction("DisplayStudent");
}
[HttpGet]
public ActionResult DisplayStudent()
{
ViewData["id"] = TempData["id"];
ViewData["name"] = TempData["name"];
return View();
}
View:
<%# Page
Language="C#"
Inherits="System.Web.Mvc.ViewPage"
%>
<html>
<head runat="server">
<title>DisplayStudent</title>
</head>
<body>
<div>
<%= ViewData["id"]%> <br />
<%= ViewData["name"]%>
</div>
</body>
</html>
There are basically 3 techniques in ASP.NET MVC to implement the PRG pattern.
TempData
Using TempData is indeed one way of passing information for a single redirect. The drawback I see with this approach is that if the user hits F5 on the final redirected page he will no longer be able to fetch the data as it will be removed from TempData for subsequent requests:
[HttpPost]
public ActionResult SubmitStudent(StudentResponseViewModel model)
{
if (!ModelState.IsValid)
{
// The user did some mistakes when filling the form => redisplay it
return View(model);
}
// TODO: the model is valid => do some processing on it
TempData["model"] = model;
return RedirectToAction("DisplayStudent");
}
[HttpGet]
public ActionResult DisplayStudent()
{
var model = TempData["model"] as StudentResponseViewModel;
return View(model);
}
Query string parameters
Another approach if you don't have many data to send is to send them as query string parameters, like this:
[HttpPost]
public ActionResult SubmitStudent(StudentResponseViewModel model)
{
if (!ModelState.IsValid)
{
// The user did some mistakes when filling the form => redisplay it
return View(model);
}
// TODO: the model is valid => do some processing on it
// redirect by passing the properties of the model as query string parameters
return RedirectToAction("DisplayStudent", new
{
Id = model.Id,
Name = model.Name
});
}
[HttpGet]
public ActionResult DisplayStudent(StudentResponseViewModel model)
{
return View(model);
}
Persistence
Yet another approach and IMHO the best consists into persisting this model into some data store (like a database or something and then when you want to redirect to the GET action send only an id allowing for it to fetch the model from wherever you persisted it). Here's the pattern:
[HttpPost]
public ActionResult SubmitStudent(StudentResponseViewModel model)
{
if (!ModelState.IsValid)
{
// The user did some mistakes when filling the form => redisplay it
return View(model);
}
// TODO: the model is valid => do some processing on it
// persist the model
int id = PersistTheModel(model);
// redirect by passing the properties of the model as query string parameters
return RedirectToAction("DisplayStudent", new { Id = id });
}
[HttpGet]
public ActionResult DisplayStudent(int id)
{
StudentResponseViewModel model = FetchTheModelFromSomewhere(id);
return View(model);
}
Each method has its pros and cons. Up to you to choose which one suits best to your scenario.
If you are inserting this data into a database then you should redirect them to a controller action that has this data in the route:
/Students/View/1
You can then write code in the controller to retrieve the data back from the database for display:
public ActionResult View(int id) {
// retrieve from the database
// create your view model
return View(model);
}
One of the overrides of RedirectToAction() looks like that:
RedirectToAction(string actionName, object routeValues)
You can use this one as:
[HttpPost]
public ActionResult SubmitStudent()
{
StudentViewModel model = TempData["model"] as StudentResponseViewModel;
return RedirectToAction("DisplayStudent", new {id = model.ID, name = model.Name});
}
[HttpGet]
public ActionResult DisplayStudent(string id, string name)
{
ViewData["id"] = TempData["id"];
ViewData["name"] = TempData["name"];
return View();
}
Hope that works.
This is the classic Post-Redirect-Get pattern (PRG) and it looks fine but I would add one bit of code. In the DisplayStudent method check if your TempData variables are not null otherwise do a redirect to some default Index action. This is in case a user presses F5 to refresh the page.
public ActionResult DisplayStudent()
{
if(TempData["model"] == null)
{
return RedirectToAction("Index");
}
var model = (StudentResponseViewModel)TempData["model"];
return View(model);
}
public ViewResult Index()
{
IEnumerable<StudentResponseViewModel> students = GetAllStudents();
return View(students);
}
I've this controller's method for create
[HttpPost]
public ActionResult Create(Topic topic)
{
if (ModelState.IsValid)
{
topicRepo.Add(topic);
topicRepo.Save();
return RedirectToAction("Details", new { id = topic.ID });
}
return View(topic);
}
and this for edit
[HttpPost]
public ActionResult Edit(int id, FormCollection formCollection)
{
Topic topic = topicRepo.getTopic(id);
if (ModelState.IsValid)
{
UpdateModel<Topic>(topic);
topicRepo.Save();
return RedirectToAction("Details", new { id = topic.ID });
}
return View(topic);
}
Both of these methods use common partial page (.ascx).
Validation works when I try to create topic but doesn't work when I try to edit it
That's normal. In the first example you are using a model as action parameter. When the default model binder tries to bind this model from the request it will automatically invoke validation and when you enter the action the ModelState.IsValid is already assigned.
In the second example your action takes no model, only a key/value collection and without a model validation makes no sense. Validation is triggered by the UpdateModel<TModel> method which in your example is invoked after the ModelState.IsValid call.
So you could try this:
[HttpPost]
public ActionResult Edit(int id)
{
Topic topic = topicRepo.getTopic(id);
UpdateModel<Topic>(topic);
if (ModelState.IsValid)
{
topicRepo.Save();
return RedirectToAction("Details", new { id = topic.ID });
}
return View(topic);
}