I am working on an MVC application, which has the stages D1, D2, D3 and D4 and we will load/edit the each stages from the application.
Right now, I have created separate models and separate views for each stage model like below and utilizing it. But for code reuse, I would like to use common view for all stages (D1, D2, D3 and D4) since all the stages contains same columns.
I have an idea to implement by creating a single view-model for all four stages and utilizing but we need to edit and save the values separately. So I am struck with that point. Is there any idea to use a common view for all four models by loading and saving the data for each stage.
Entities
public partial class D1Stage : EntityBase
{
[DisplayName("Status Indicator")]
public byte StatusIndicatorId { get; set; }
[DisplayName("Status Info")]
public string StatusInfo { get; set; }
[DisplayName("Completed %")]
[SCIRange(0, 100)]
public decimal StageCompletion { get; set; }
[DisplayName("Is Step Mandatory")]
public bool IsMandatory { get; set; }
}
And as I mentioned, D2Stage
, D3Stage
and D4Stage
have exactly the same properties, for example for D2Stage
:
public partial class D2Stage : EntityBase
{
... exact same properties as D1Stage
}
Views
@model Brain.DAL.Entities.D1Stage
@{
IEnumerable<SelectListItem> StatusIndicators = ViewBag.StatusIndicators;
}
@using (Html.BeginForm("", "", FormMethod.Post, new { enctype = "multipart/form-data", @class = "form form-horizontal" }))
{
<div class="form-body">
@Html.AntiForgeryToken()
@Html.HiddenFor(model => model.Id)
@Html.HiddenFor(model => model.Report.Id)
<div class="form-group">
@Html.SCILabelFor(model => model.StatusIndicatorId, new { @class = "col-md-1 control-label" })
<div class="input-group col-md-4">
@Html.DropDownListFor(model => model.StatusIndicatorId, new SelectList(StatusIndicators, "Value", "Text"), "None", new { @class = "form-control" })
@Html.ValidationMessageFor(model => model.StatusIndicatorId, "", new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
@Html.SCILabelFor(model => model.StatusInfo, new { @class = "col-md-1 control-label" })
<div class="input-group col-md-4">
@Html.TextAreaFor(model => model.StatusInfo, new { @class = "form-control" })
@Html.ValidationMessageFor(model => model.StatusInfo, "", new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
@Html.SCILabelFor(model => model.StageCompletion, new { @class = "col-md-1 control-label" })
<div class="input-group col-md-4">
@Html.SCINumericTextBoxFor(model => model.StageCompletion, new { @class = "form-control sliderrangemintext", @style = "width:100px" })
<div class="sliderrangemin" id="slider-range-min" style="margin-top:50px"></div>
</div>
</div>
<div class="form-group">
@Html.SCILabelFor(model => model.IsMandatory, new { @class = "col-md-1 control-label" })
<div class="input-group col-md-4">
<div class="input-group" style="width:100%">
@Html.DropDownListFor(model => model.IsMandatory, new List<SelectListItem>() { new SelectListItem { Text = "No", Value = "false" }, new SelectListItem { Text = "Yes", Value = "true", Selected = true } }, htmlAttributes: new { @class = "form-control" })
@Html.ValidationMessageFor(model => model.IsMandatory, "", new { @class = "text-danger" })
</div>
</div>
</div>
</div>
}
And as I mentioned above, all the other views have the same content with just having different corresponding model. For example for D2Stage
@model Brain.DAL.Entities.D2Stage
... The difference is just in model.
... Rest of the content is exactly the same as D1Stage view
All models have similar properties? Assuming you cannot create a single model. Then move the properties to base class and create the other models deriving from the base model. Then also create a single view having base model as model.
Models
public class MyBaseModel
{
public string Property1 { get; set; }
public string Property2 { get; set; }
}
public class ModelA: MyBaseModel{}
public class ModelB: MyBaseModel{}
View
Create a single view for the MyBaseModel
and Put the view in the Shared
folder and name it something like MySharedView
:
@model MyNamespace.MyBaseModel
@using (Html.BeginForm())
{
@Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>Title which you can decide based on model type or get from ViewBag</h4>
<hr />
@Html.ValidationSummary(true, "", new { @class = "text-danger" })
<div class="form-group">
@Html.LabelFor(model => model.Property1, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.Property1, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.Property1, "", new { @class = "text-danger" })
</div>
</div>
... Rest of properties ...
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</div>
</div>
}
Controller
Then in controllers, when you want to return view, specify the shared view name. For example for model A
and the same for model B
:
[HttpGet]
public ActionResult A()
{
var model = new ModelA() { Property1 = "A", Property2 = "A" };
return View("MySharedView", model);
}
[HttpPost]
public ActionResult A(ModelA model)
{
// SaveA(model)
// Redirect to index
}
Thanks for the immediate response. I will try this out. How do i save when i click save button, it should save on separate table?
You have different actions if you need different processing. Each action receives the model that it should process.
I can extend the answer to include more options, but to be honest, the whole point is having a base class for all the models and having a single view for the the base model, not easier or not more Initiative.
You can also redesign your models, to use a single model, having all the mentioned properties + another property
public int Stage { get ;set;}
and save all data in a single table. But I assume it's an obvious solution, and you don;t want to change your model and all the tables behind.@VigneshKumarA I believe the post answer the main concern of the question. Let me know if you have any question about it or if you found it useful.