web-dev-qa-db-fra.com

Qu'est-ce que ViewModel dans MVC?

Je suis nouveau sur ASP.NET MVC. J'ai du mal à comprendre l'objectif d'un ViewModel. 

Qu'est-ce qu'un ViewModel et pourquoi avons-nous besoin d'un ViewModel pour une application ASP.NET MVC? 

C'est mieux si je peux avoir un exemple simple.

376
unique

Un view model représente les données que vous souhaitez afficher sur votre vue/page, qu'elles soient utilisées pour du texte statique ou pour des valeurs d'entrée (comme des zones de texte et des listes déroulantes) pouvant être ajoutées à la base de données (ou modifiées). C'est quelque chose de différent de votre domain model. C'est un modèle pour la vue.

Supposons que vous ayez une classe Employee qui représente le modèle de domaine de votre employé et qui contient les propriétés suivantes (identificateur unique, prénom, nom et date de création):

public class Employee : IEntity
{
     public int Id { get; set; }

     public string FirstName { get; set; }

     public string LastName { get; set; }

     public DateTime DateCreated { get; set; }
}

Les modèles de vue diffèrent des modèles de domaine en ce qu'ils ne contiennent que les données (représentées par des propriétés) que vous souhaitez utiliser sur votre vue. Par exemple, supposons que vous souhaitiez ajouter un nouvel enregistrement d'employé, votre modèle de vue pourrait ressembler à ceci:

public class CreateEmployeeViewModel
{
     public string FirstName { get; set; }

     public string LastName { get; set; }
}

Comme vous pouvez le constater, il ne contient que deux propriétés. Ces deux propriétés sont également dans le modèle de domaine employé. Pourquoi est-ce que vous pouvez demander? Id n'est peut-être pas défini dans la vue, il peut être généré automatiquement par la table Employee. Et DateCreated peut également être défini dans la procédure stockée ou dans la couche service de votre application. Ainsi, Id et DateCreated ne sont pas nécessaires dans le modèle de vue. Vous souhaiterez peut-être afficher ces deux propriétés lorsque vous afficherez les détails d'un employé (un employé déjà saisi) sous forme de texte statique.

Lors du chargement de la vue/page, la méthode d'action de création dans votre contrôleur d'employés crée une instance de ce modèle de vue, renseigne les champs si nécessaire, puis transmet ce modèle de vue à la vue/page:

public class EmployeeController : Controller
{
     private readonly IEmployeeService employeeService;

     public EmployeeController(IEmployeeService employeeService)
     {
          this.employeeService = employeeService;
     }

     public ActionResult Create()
     {
          CreateEmployeeViewModel model = new CreateEmployeeViewModel();

          return View(model);
     }

     public ActionResult Create(CreateEmployeeViewModel model)
     {
          // Do what ever needs to be done before adding the employee to the database
     }
}

Votre vue/page pourrait ressembler à ceci (en supposant que vous utilisez ASP.NET MVC et le moteur de vue Razor):

@model MyProject.Web.ViewModels.CreateEmployeeViewModel

<table>
     <tr>
          <td><b>First Name:</b></td>
          <td>@Html.TextBoxFor(m => m.FirstName, new { maxlength = "50", size = "50" })
              @Html.ValidationMessageFor(m => m.FirstName)
          </td>
     </tr>
     <tr>
          <td><b>Last Name:</b></td>
          <td>@Html.TextBoxFor(m => m.LastName, new { maxlength = "50", size = "50" })
              @Html.ValidationMessageFor(m => m.LastName)
          </td>
     </tr>
</table>

La validation ne se ferait donc que sur FirstName et LastName. En utilisant Validation Fluent vous pouvez avoir une validation comme celle-ci:

public class CreateEmployeeViewModelValidator : AbstractValidator<CreateEmployeeViewModel>
{
     public CreateEmployeeViewModelValidator()
     {
          RuleFor(m => m.FirstName)
               .NotEmpty()
               .WithMessage("First name required")
               .Length(1, 50)
               .WithMessage("First name must not be greater than 50 characters");

          RuleFor(m => m.LastName)
               .NotEmpty()
               .WithMessage("Last name required")
               .Length(1, 50)
               .WithMessage("Last name must not be greater than 50 characters");
     }
}

Et avec les annotations de données, cela pourrait ressembler à ceci:

public class CreateEmployeeViewModel : ViewModelBase
{
    [Display(Name = "First Name")]
    [Required(ErrorMessage = "First name required")]
    public string FirstName { get; set; }

    [Display(Name = "Last Name")]
    [Required(ErrorMessage = "Last name required")]
    public string LastName { get; set; }
}

L'important, c'est que le modèle de vue ne représente que les données que vous souhaitez utiliser, rien d'autre. Vous pouvez imaginer tout le code et la validation inutiles si vous avez un modèle de domaine avec 30 propriétés et que vous souhaitez uniquement mettre à jour une valeur. Dans ce scénario, vous ne disposez que de cette valeur/propriété dans le modèle de vue, et non de toutes les propriétés de l'objet de domaine.

Un modèle de vue peut ne pas contenir uniquement des données d'une table de base de données. Il peut combiner des données d'une autre table. Prenons l'exemple ci-dessus concernant l'ajout d'un nouveau dossier d'employé. Outre l'ajout des noms et prénoms, vous pouvez également ajouter le département de l'employé. Cette liste de départements proviendra de votre table Departments. Alors maintenant, vous avez les données des tables Employees et Departments dans un modèle de vue. Il vous suffira ensuite d'ajouter les deux propriétés suivantes à votre modèle de vue et de le renseigner avec des données:

public int DepartmentId { get; set; }

public IEnumerable<Department> Departments { get; set; }

Lors de la modification de données d'employé (un employé déjà ajouté à la base de données), cela ne diffère pas beaucoup de mon exemple ci-dessus. Créez un modèle de vue, appelez-le par exemple EditEmployeeViewModel. Ne possédez que les données que vous souhaitez modifier dans ce modèle de vue, telles que le prénom et le nom. Modifiez les données et cliquez sur le bouton d'envoi. Je ne m'inquiéterais pas trop du champ Id car la valeur Id sera probablement dans l'URL, par exemple: 

http://www.yourwebsite.com/Employee/Edit/3

Prenez cette Id et transmettez-la à votre couche de référentiel, ainsi que vos valeurs de prénom et de nom.

Lors de la suppression d'un enregistrement, je suis normalement le même chemin que pour le modèle de vue de modification. J'aurais aussi une URL, par exemple:

http://www.yourwebsite.com/Employee/Delete/3

Lorsque la vue se charge pour la première fois, les données de l’employé sont extraites de la base de données à l’aide de la variable Id de 3. Je n’affiche alors que du texte statique sur ma vue/page afin que l’utilisateur puisse voir quel employé est supprimé. Lorsque l'utilisateur clique sur le bouton Supprimer, il me suffit d'utiliser la valeur Id de 3 et de la transmettre à ma couche de référentiel. Vous avez uniquement besoin de la variable Id pour supprimer un enregistrement de la table.

Autre point, vous n’avez pas vraiment besoin d’un modèle de vue pour chaque action. S'il s'agit de données simples, il serait préférable d'utiliser uniquement EmployeeViewModel. S'il s'agit de vues/pages complexes et qu'elles diffèrent les unes des autres, je vous suggère d'utiliser des modèles de vues distincts pour chacune.

J'espère que cela dissipe toute confusion entre vos modèles de vue et de domaine.

548
Brendan Vogt

Modèle de vue est une classe qui représente le modèle de données utilisé dans une vue spécifique. Nous pourrions utiliser cette classe comme modèle pour une page de connexion:

public class LoginPageVM
{
    [Required(ErrorMessage = "Are you really trying to login without entering username?")]
    [DisplayName("Username/e-mail")]
    public string UserName { get; set; }
    [Required(ErrorMessage = "Please enter password:)")]
    [DisplayName("Password")]
    public string Password { get; set; }
    [DisplayName("Stay logged in when browser is closed")]
    public bool RememberMe { get; set; }
}

En utilisant ce modèle de vue, vous pouvez définir la vue (moteur de vue Razor):

@model CamelTrap.Models.ViewModels.LoginPageVM

@using (Html.BeginForm()) {
    @Html.EditorFor(m => m);
    <input type="submit" value="Save" class="submit" />
}

Et des actions:

[HttpGet]
public ActionResult LoginPage()
{
    return View();
}

[HttpPost]
public ActionResult LoginPage(LoginPageVM model)
{
    ...code to login user to application...
    return View(model);
}

Ce qui produit ce résultat (l'écran est pris après la soumission du formulaire, avec des messages de validation):

Comme vous pouvez le constater, un modèle de vue a plusieurs rôles:

  • Voir les modèles de documents une vue en ne comprenant que des champs, qui sont représentés dans la vue.
  • Les modèles de vue peuvent contenir des règles de validation spécifiques utilisant des annotations de données ou IDataErrorInfo.
  • Le modèle de vue définit l'apparence d'une vue (pour LabelFor, EditorFor, DisplayFor assistants).
  • Les modèles de vue peuvent combiner des valeurs provenant de différentes entités de base de données.
  • Vous pouvez spécifier facilement des modèles d'affichage pour les modèles d'affichage et les réutiliser dans de nombreux endroits à l'aide des aides DisplayFor ou EditorFor.

Un autre exemple de modèle de vue et de sa récupération: nous voulons afficher les données utilisateur de base, ses privilèges et le nom de l'utilisateur. Nous créons un modèle de vue spécial, qui ne contient que les champs obligatoires. Nous récupérons des données de différentes entités à partir de la base de données, mais la vue ne connaît que la classe de modèle de vue:

public class UserVM {
    public int ID { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public bool IsAdministrator { get; set; }
    public string MothersName { get; set; }
}

Récupération:

var user = db.userRepository.GetUser(id);

var model = new UserVM() {
   ID = user.ID,
   FirstName = user.FirstName,
   LastName = user.LastName,
   IsAdministrator = user.Proviledges.IsAdministrator,
   MothersName = user.Mother.FirstName + " " + user.Mother.LastName
} 
126
LukLed

Si vous disposez de propriétés spécifiques à la vue et non liées au magasin de base de données/service/données, l'utilisation de ViewModels est recommandée. Supposons que vous souhaitiez laisser une case à cocher basée sur un champ de base de données (ou deux), mais que le champ de base de données lui-même n’est pas un booléen. Bien qu'il soit possible de créer ces propriétés dans le modèle lui-même et de le garder caché de la liaison aux données, vous pouvez ne pas vouloir encombrer le modèle en fonction de la quantité de ces champs et transactions.

S'il y a trop peu de données et/ou de transformations spécifiques à une vue, vous pouvez utiliser le modèle lui-même.

22
fozylet

Je n'ai pas lu tous les articles, mais chaque réponse semble manquer à un concept qui m'a vraiment aidé à "l'obtenir" ... 

Si un modèle s'apparente à une base de donnéesTable, un ViewModel s'apparente à une base de donnéesView- Une vue renvoie généralement de petites quantités de données d'une table ou , ensembles complexes de données provenant de plusieurs tables (jointures).

Je me trouve à utiliser ViewModels pour transmettre des informations dans une vue/un formulaire, puis à transférer ces données dans un modèle valide lorsque le formulaire est renvoyé au contrôleur, ce qui est également très pratique pour le stockage de listes (IEnumerable).

14
halfacreSal

Permettez-moi de vous expliquer de manière claire et croustillante de nombreux exemples.

ViewModel = Modèle créé pour servir la vue.

ASP.NET La vue MVC ne peut pas avoir plusieurs modèles. Par conséquent, si nous devons afficher les propriétés de plusieurs modèles dans la vue, cela n’est pas possible. ViewModel sert cet objectif.

View Model est une classe de modèle qui ne peut contenir que les propriétés requises pour une vue. Il peut également contenir des propriétés provenant de plusieurs entités (tables) de la base de données. Comme son nom l'indique, ce modèle est créé spécifiquement pour les exigences de vue. 

Quelques exemples de modèles de vue sont ci-dessous 

  • Pour répertorier des données provenant de plusieurs entités dans une page d'affichage, nous pouvons créer un modèle View et définir les propriétés de toutes les entités pour lesquelles nous souhaitons répertorier les données. Joignez-vous à ces entités de base de données et définissez View modelproperties, puis revenez à View pour afficher les données de différentes entités Dans un seul tableau.
  • Le modèle de vue peut définir uniquement des champs spécifiques d'une seule entité qui est requise pour la vue.

ViewModel peut également être utilisé pour insérer, mettre à jour des enregistrements dans plusieurs entités. Cependant, l’utilisation principale de ViewModel est d’afficher des colonnes de plusieurs entités (modèle) dans une seule vue.

La manière de créer ViewModel est identique à celle de Model, la manière de créer une vue pour le Viewmodel est identique à la création d’une vue pour Model.

Voici un petit exemple de Liste des données en utilisant ViewModel .

J'espère que cela vous sera utile.

10
Sheo Narayan

MVC n'a pas de modèle de vue: il a un modèle, une vue et un contrôleur. Un modèle de vue fait partie de MVVM (Model-View-Viewmodel). MVVM est dérivé du modèle de présentation et est popularisé dans WPF. Il devrait également y avoir un modèle dans MVVM, mais la plupart des gens passent complètement à côté de ce modèle et n’auront qu’une vue et un modèle de vue. Le modèle dans MVC est similaire au modèle dans MVVM.

Dans MVC, le processus est divisé en 3 responsabilités différentes:

  • View est responsable de la présentation des données à l'utilisateur
  • Un contrôleur est responsable du flux de page
  • Un modèle est responsable de la logique métier

MVC n'est pas très approprié pour les applications Web. C'est un modèle introduit par Smalltalk pour créer des applications de bureau. Un environnement Web se comporte complètement différemment. Il n’a pas de sens de copier un concept vieux de 40 ans à partir du développement de postes de travail et de le coller dans un environnement Web. Cependant, beaucoup de gens pensent que cela va, car leur application compile et renvoie les valeurs correctes. À mon avis, cela ne suffit pas pour déclarer un certain choix de conception correct.

Un exemple de modèle dans une application Web pourrait être:

public class LoginModel
{
    private readonly AuthenticationService authentication;

    public LoginModel(AuthenticationService authentication)
    {
        this.authentication = authentication;
    }

    public bool Login()
    {
        return authentication.Login(Username, Password);
    }

    public string Username { get; set; }
    public string Password { get; set; }
}

Le contrôleur peut l'utiliser comme ceci:

public class LoginController
{
    [HttpPost]
    public ActionResult Login(LoginModel model)
    {
        bool success = model.Login();

        if (success)
        {
            return new RedirectResult("/dashboard");
        }
        else
        {
            TempData["message"] = "Invalid username and/or password";
            return new RedirectResult("/login");
        }
    }
}

Vos méthodes de contrôleur et vos modèles seront petits, facilement testables et pertinents.

10
Jeroen

Le modèle de vue a est une classe simple pouvant contenir plusieurs propriétés de classe. Nous l'utilisons pour hériter de toutes les propriétés requises, par exemple. J'ai deux classes étudiant et sujet 

Public class Student
{
public int Id {get; set;}
public string Name {get; set;}
}  
Public class Subject
{
public int SubjectID {get; set;}
public string SubjectName {get; set;}
}

Nous voulons maintenant afficher les enregistrements Nom de l'étudiant et Nom du sujet dans la vue (dans MVC), mais il n'est pas possible d'ajouter plus d'une classe, telle que:

 @model ProjectName.Model.Student  
 @model ProjectName.Model.Subject

le code ci-dessus va jeter une erreur ...

Maintenant, nous créons une classe et pouvons lui donner n'importe quel nom, mais ce format "XyzViewModel" le rendra plus facile à comprendre. C'est un concept d'héritage . Maintenant, nous créons une troisième classe avec le nom suivant:

public class StudentViewModel:Subject
{
public int ID {get; set;}
public string Name {get; set;}
}

Maintenant, nous utilisons ce ViewModel dans View 

@model ProjectName.Model.StudentViewModel

Nous pouvons maintenant accéder à toutes les propriétés de StudentViewModel et à la classe héritée dans View.

10
Mayank

ViewModel est une solution qui corrige la maladresse conceptuelle du framework MVC. Il représente la 4ème couche de l’architecture Model-View-Controller à 3 couches. Lorsque Modèle (modèle de domaine) n'est pas approprié, trop grand (plus de 2 ou 3 champs) pour la vue, nous créons un ViewModel plus petit pour le transmettre à la vue.

6
gsivanov
  • ViewModel contient des champs représentés dans la vue (pour LabelFor, EditorFor, DisplayFor helpers)
  • ViewModel peut avoir des règles de validation spécifiques utilisant des annotations de données .__ ou IDataErrorInfo.
  • ViewModel peut avoir plusieurs entités ou objets provenant de données différentes Modèles ou sources de données.

Conception de ViewModel

public class UserLoginViewModel 
{ 
[Required(ErrorMessage = "Please enter your username")] 
[Display(Name = "User Name")]
[MaxLength(50)]
public string UserName { get; set; }
 [Required(ErrorMessage = "Please enter your password")]
 [Display(Name = "Password")]
 [MaxLength(50)]
 public string Password { get; set; } 
} 

Présentation du modèle de vue dans la vue

@model MyModels.UserLoginViewModel 
@{
 ViewBag.Title = "User Login";
 Layout = "~/Views/Shared/_Layout.cshtml";
}
@using (Html.BeginForm())
{
<div class="editor-label">
 @Html.LabelFor(m => m.UserName)
</div>
<div class="editor-field">
 @Html.TextBoxFor(m => m.UserName)
 @Html.ValidationMessageFor(m => m.UserName)
</div>
<div class="editor-label">
 @Html.LabelFor(m => m.Password)
</div>
<div class="editor-field">
 @Html.PasswordFor(m => m.Password)
 @Html.ValidationMessageFor(m => m.Password)
</div>
<p>
 <input type="submit" value="Log In" />
</p>
</div>
}

Travailler avec l'action

public ActionResult Login()
{ 
return View();
}
[HttpPost]
public ActionResult Login(UserLoginViewModel user)
{
// To acces data using LINQ
DataClassesDataContext mobjentity = new DataClassesDataContext();
 if (ModelState.IsValid) 
{ 
try
 {
 var q = mobjentity.tblUsers.Where(m => m.UserName == user.UserName && m.Password == user.Password).ToList(); 
 if (q.Count > 0) 
 { 
 return RedirectToAction("MyAccount");
 }
 else
 {
 ModelState.AddModelError("", "The user name or password provided is incorrect.");
 }
 }
 catch (Exception ex)
 {
 } 
 } 
 return View(user);
} 
  1. Dans ViewModel, ne placez que les champs/données que vous souhaitez afficher sur la vue/page
  2. Puisque view représente les propriétés du ViewModel, il est donc facile à utiliser pour le rendu et la maintenance.
  3. Utilisez un mappeur lorsque ViewModel devient plus complexe.
1
wild coder

Un modèle de vue est un modèle conceptuel de données. Son utilisation consiste par exemple à obtenir un sous-ensemble ou à combiner des données provenant de différentes tables. 

Vous voudrez peut-être uniquement des propriétés spécifiques, ce qui vous permet de ne charger que celles-ci et non des propriétés inutiles

1
user6685907

View Model est une classe que nous pouvons utiliser pour restituer des données dans View. Supposons que vous avez deux entités Place et PlaceCategory et que vous souhaitez accéder aux données des deux entités à l'aide d'un modèle unique, puis nous utilisons ViewModel.

  public class Place
    {
       public int PlaceId { get; set; }
        public string PlaceName { get; set; }
        public string Latitude { get; set; }
        public string Longitude { get; set; }
        public string BestTime { get; set; }
    }
    public class Category
    {
        public int ID { get; set; }
        public int? PlaceId { get; set; }
        public string PlaceCategoryName { get; set; }
        public string PlaceCategoryType { get; set; }
    }
    public class PlaceCategoryviewModel
    {
        public string PlaceName { get; set; }
        public string BestTime { get; set; }
        public string PlaceCategoryName { get; set; }
        public string PlaceCategoryType { get; set; }
    }

Ainsi, dans l'exemple ci-dessus, Place et Catégorie sont les deux entités différentes et PlaceCategory viewmodel est ViewModel que nous pouvons utiliser dans View.

0
Sagar Shinde