The M that represents the Model in MVC can be used in different ways:
As a model of persistence. It would be the class that is defined in your context with the aim of saving in the BD
As a business model classes with methods that can then be called on your controller
As a representation model, it is a class that you define with the objective of being represented in your view, used to represent complex forms like the one you need.
In practice, the representation model is declared with the name ViewModel and it is a class that incorporates the attributes that you want depending on how complex your form is, to this class the rules of validation like the others. Let's take a simple example
we have the Employee class
public class Empleado
{
public int Id { get; set; }
public string Nombre { get; set; }
public string Apellido { get; set; }
public int AreaId { get; set; }
public virtual Area Area { get; set; }
}
and an Area class
public class Area
{
public int Id { get; set; }
public string Nombre { get; set; }
public virtual ICollection<Empleado> Empleados { get; set; }
}
and we want through a form to create an employee, an area and assign the employee the id of the created area. For this type of cases it is necessary to help from a ViewModel. In the root directory we create a folder called ViewModels and inside we add the class.
public class EmpleadoAreaViewModel
{
public int Id { get; set; }
[Required(ErrorMessage = "El campo Nombre es obligatorio")]
public string Nombre { get; set; }
[Required(ErrorMessage = "El campo Apellido es obligatorio")]
public string Apellido { get; set; }
[Required(ErrorMessage = "El campo Area es obligatorio")]
public string NombreArea { get; set; }
}
If they are fixed to the employees and area classes, I did not apply them for validation because they are persistence classes I will only use them to save in the database, on the other hand, the model that I will use to build my form is EmployeeAreaViewModel which has validation.
my form would look like this:
@model WebApplication1.ViewModels.EmpleadoAreaViewModel
@{
ViewBag.Title = "Nuevo";
}
<h2>Nuevo</h2>
@using (Html.BeginForm())
{
@Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>EmpleadoAreaViewModel</h4>
<hr />
@Html.ValidationSummary(true)
<div class="form-group">
@Html.LabelFor(model => model.Nombre, new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.Nombre)
@Html.ValidationMessageFor(model => model.Nombre)
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.Apellido, new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.Apellido)
@Html.ValidationMessageFor(model => model.Apellido)
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.NombreArea, new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.NombreArea)
@Html.ValidationMessageFor(model => model.NombreArea)
</div>
</div>
<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>
}
As you see, EmpleadoAreaViewModel is used as a model for my form.
the interesting part comes in the controller, which would be passed the viewmodel as DataBinding example:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Nuevo([Bind(Include = "Id,Nombre,Apellido, NombreArea")] EmpleadoAreaViewModel empleadoArea)
{
if (ModelState.IsValid)
{
var area = new Area()
{
Nombre = empleadoArea.NombreArea
};
db.Areas.Add(area);
var empleado = new Empleado()
{
Nombre = empleadoArea.Nombre,
Apellido = empleadoArea.Apellido,
AreaId = area.Id
};
db.Empleados.Add(empleado);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(empleadoArea);
}
Since you collected all the data in a single form and these belong to 2 different classes, you must separate the data and create them independently as shown in the controller code. in this case I did something rudimentary because you can use AutoMapper for this. In this way, a worker and an area are created at the same time. I hope it helps you.
for the example that you ask me you can do it in this way:
in your model:
public class EmpleadoViewModel
{
public int Id { get; set; }
public string Nombre { get; set; }
public string Apellidos { get; set; }
}
in your view you use:
@model IList<WebApplication1.ViewModels.EmpleadoViewModel>
and you collect the data in this way:
@using (Html.BeginForm("Nuevo", "Empleado", FormMethod.Post))
{
@Html.AntiForgeryToken()
<div class="form-horizontal">
@Html.ValidationSummary(true)
<div class="form-group">
<label>Area</label>
<input type="text" id="area" name="area"/>
</div>
</div>
for (int i = 0; i < 5; i++)
{
<div class="form-group">
<label>Nombre </label>
<div class="col-md-10">
@Html.TextBoxFor(p => p[i].Nombre)
</div>
</div>
<div class="form-group">
<label>Apellidos </label>
<div class="col-md-10">
@Html.TextBoxFor(p => p[i].Apellidos)
</div>
</div>
}
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</div>
}
You define the shekel until you reach the amount you want to insert in my case I set it to 5.
in the controller:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Nuevo( string area, List<EmpleadoViewModel>Empleados )
{
if (ModelState.IsValid)
{
var Area = new Area()
{
Nombre = area
};
db.Areas.Add(Area);
foreach (var e in Empleados)
{
var empleado = new Empleado()
{
Nombre = e.Nombre,
Apellido = e.Apellidos,
AreaId = Area.Id,
};
db.Empleados.Add(empleado);
db.SaveChanges();
}
return RedirectToAction("Index");
}
return View( );
}
This way should work for you. since you collect the name of an area and a list of workers in the post. In the end this kind of thing solves but I recommend that you use other ways like Angular for example that are much more flexible on the client side. I hope I help you