Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Same view for both create and edit in MVC4

Can we have a single razor view for both Create and Edit operations?

If yes, how do we achieve this?

like image 316
mmssaann Avatar asked Jun 14 '13 11:06

mmssaann


People also ask

What is the purpose of using bind in Edit post method?

The [Bind] attribute will let you specify the exact properties of a model should include or exclude in binding. In the following example, the Edit() action method will only bind StudentId and StudentName properties of the Student model class.


2 Answers

I don't recommend it.

This should be a rather long answer, because there's a lot of things involved in the process, request and workflow of a normal MVC GET/POST workflow. I will try to answer your question with the minimum information required and why I do not recommend the use of the same view.

First, why?

  1. You don't have control over the views, which may have over-posting;
  2. No flexibility;
  3. Not reusable views or parts;
  4. Hard to maintain the views (one change in the view must be tested on both actions).

My suggested approach would be to have different actions/views but share common code:

Create both views as normal.

You will have duplicated code, but not all code is the same, for example, you may not want to send an ID on the create action, this is not directly related to your question, but using the same view implies you are also sending the same data, and this is not recommended, especially for over-posting or mass assignment. More info about mass assignment here (an Architectural Approach is what I'm using here).

So let's start from what are you going to receive in your controllers. In this case I used inheritance but it's not the only strategy.

Binding models

public class UpdateBindingModel : CreateBindingModel {     // since we are not using the same binding model,      // we can have a "real" validation rules on our update binding and view.     [Required]     public int? Id {get;set;} }  public class CreateBindingModel {     // no id here prevent overposting.     [Required]     public string Name {get;set;}     [Required]     public int? CountryId {get;set;} } 

That will make sure the data you send to your Create and Edit is the minimum needed and nothing else.

Let's then see the View Models that will be sent to the View, for this example I will include a List that will be used to select some value but should not be posted (the list) to the controller, only the selected value.

View models

public class CreateViewModel : CreateBindingModel {     public IEnumerable<SelectListItem> CountryList {get;set;} }  public class UpdateViewModel : UpdateBindingModel {     public IEnumerable<SelectListItem> CountryList {get;set;} } 

As you can see, this gives you lot of flexibility but still have some duplicated code (the extra information needed on view model for both views) which can be mitigated in several ways (depending the needs/context):

  1. Have an action to retrieve the common data and using @Html.Action("GetCountryList");
  2. Use the same View Model aka CreateUpdateViewModel and discarding extra UpdateBindingModel properties in the view but still posting the corresponding model on POST.
  3. Having your binding models as properties and select one or the other in the specific view. (better use @Html.EditorFor instead of partials so Model Binder will work with no additional change on code)

The controller actions will look like:

Controller

[HttpGet] public ActionResult Create(){     ViewData.Model = new CreateViewModel();     return View(); }  [HttpPost] public RedirectToRouteResult Create(CreateBindingModel binding) {     // check valid model state and create data     return RedirectToAction("Index"); }  [HttpGet] public ActionResult Update(int id) {     var objectToEdit = service.GetObjectToEdit(id);     ViewData.Model = new UpdateViewModel(objectToEdit);     return View(); }  [HttpPost] public RedirectToRouteResult Update(UpdateBindingModel binding) {     // check valid model state and update data     return RedirectToAction("Index"); } 

And your views:

Views

Update.cshtml <form action="Update">     @Html.HiddenFor(Model.Id);     @Html.Partial("EditFieldsPartial")     <button>delete</button> // no delete button on create.     <button>create new</button> // you can have a create new instead of update. </form>  Create.cshtml <form action="Create">     @Html.Partial("EditFieldsPartial") </form> 

Note: code is incomplete and didn't use helpers in most cases for brevity and clarity. Do NOT copy paste :D

like image 66
Bart Calixto Avatar answered Sep 25 '22 13:09

Bart Calixto


Sure you can.

On post, check in your controller whether the primary key has value 0 then Insert, otherwise Update.

View should be the same for Create and Edit.

Just remember to include:

@Html.HiddenFor(model=>model.ID) 

In your view

For example:

Model:

public class DescriptionModel {     [Key]     public int ID { get; set; }      public string Description { get; set; } } 

CreateEdit.cshtml:

@model DescriptionModel  @using (Html.BeginForm("CreateEdit")) {     @Html.HiddenFor(model=> model.ID)     @Html.EditorFor(model=> model.Description)     <input type="submit" value='Submit' /> } 

DescriptionModel controller:

public ActionResult Create() {     return View("CreateEdit", new DescriptionModel()); } public ActionResult Edit(int id) {     return View("CreateEdit", db.DescriptionModels.Find(id)); }  // Submit and add or update database [HttpPost] public ActionResult CreateEdit(DescriptionModel model) {     if (ModelState.IsValid)     {        // No id so we add it to database        if (model.ID <= 0)        {            db.DescriptionModels.Add(model);        }        // Has Id, therefore it's in database so we update        else        {            db.Entry(model).State = EntityState.Modified;        }        db.SaveChanges();        return RedirectToAction("Index");     }      return View(model); } 
like image 33
Tomi Lammi Avatar answered Sep 24 '22 13:09

Tomi Lammi