324 votes

Plusieurs modèles dans une vue

Je veux avoir deux modèles dans une seule vue. La page contient les deux LoginViewModel et RegisterViewModel .

par exemple

public class LoginViewModel
{
    public string Email { get; set; }
    public string Password { get; set; }
}

public class RegisterViewModel
{
    public string Name { get; set; }
    public string Email { get; set; }
    public string Password { get; set; }
}

Dois-je créer un autre ViewModel qui contient ces deux ViewModels ?

public BigViewModel
{
    public LoginViewModel LoginViewModel{get; set;}
    public RegisterViewModel RegisterViewModel {get; set;}
}

J'ai besoin que les attributs de validation soient reportés dans la vue. C'est pourquoi j'ai besoin des ViewModels.

N'y a-t-il pas une autre façon de procéder (sans l'option BigViewModel ) :

 @model ViewModel.RegisterViewModel
 @using (Html.BeginForm("Login", "Auth", FormMethod.Post))
 {
        @Html.TextBoxFor(model => model.Name)
        @Html.TextBoxFor(model => model.Email)
        @Html.PasswordFor(model => model.Password)
 }

 @model ViewModel.LoginViewModel
 @using (Html.BeginForm("Login", "Auth", FormMethod.Post))
 {
        @Html.TextBoxFor(model => model.Email)
        @Html.PasswordFor(model => model.Password)
 }

4 votes

1 votes

@saeed serpooshan, merci beaucoup pour le lien avec les différentes options, après 4 ans vous avez posté un commentaire et il m'a aidé, je viens d'utiliser ViewBag avec for each dans la vue, cela fonctionne très bien

0 votes

@stom Juste pour info : l'auteur du message reçoit toujours une notification, mais si vous voulez notifier quelqu'un d'autre, vous devez mettre @ devant leur nom, comme je l'ai fait ici.

268voto

Omu Points 17372

Il y a beaucoup de façons...

  1. avec votre BigViewModel vous le faites :

    @model BigViewModel    
    @using(Html.BeginForm()) {
        @Html.EditorFor(o => o.LoginViewModel.Email)
        ...
    }
  2. vous pouvez créer 2 vues supplémentaires

    Connexion.cshtml

    @model ViewModel.LoginViewModel
    @using (Html.BeginForm("Login", "Auth", FormMethod.Post))
    {
        @Html.TextBoxFor(model => model.Email)
        @Html.PasswordFor(model => model.Password)
    }

    et register.cshtml même chose

    après la création, vous devez les rendre dans la vue principale et leur passer le modèle de vue/les données de vue.

    donc ça pourrait être comme ça :

    @{Html.RenderPartial("login", ViewBag.Login);}
    @{Html.RenderPartial("register", ViewBag.Register);}

    ou

    @{Html.RenderPartial("login", Model.LoginViewModel)}
    @{Html.RenderPartial("register", Model.RegisterViewModel)}
  3. en utilisant ajax, les parties de votre site web deviennent plus indépendantes

  4. iframes mais ce n'est probablement pas le cas

2 votes

Est-ce un problème si deux zones de texte ont le même nom sur le formulaire en raison de l'utilisation de vues partielles ?

2 votes

Non, ça devrait aller - cliquez sur l'élément lui-même en utilisant quelque chose comme firebug (sur firefox) et vous verrez quelque chose comme id="LoginViewModel_Email" name = "LoginViewModel.Email", donc en fait ils sont uniques ! Un modèle de vue devrait être ce dont vous avez besoin, il suffit de poster chaque page à une URL différente et tout devrait bien se passer.

0 votes

@Lol coder en fait ce serait 2 formulaires, un pour chaque viewmodel, mais de toute façon si vous aviez 2 ou 3 ou plus avec le même nom vous obtiendriez juste un tableau avec ce nom du côté du serveur (si vous le mettez dans les paramètres de la méthode post action)

130voto

Thechoyce Points 1805

Je recommande d'utiliser Html.RenderAction et PartialViewResults pour y parvenir ; cela vous permettra d'afficher les mêmes données, mais chaque vue partielle aura toujours un modèle de vue unique et éliminera la nécessité d'un fichier BigViewModel

Votre vue contient donc quelque chose comme ce qui suit :

@Html.RenderAction("Login")
@Html.RenderAction("Register")

Login & Register sont deux actions dans votre contrôleur définies comme suit :

public PartialViewResult Login( )
{
    return PartialView( "Login", new LoginViewModel() );
}

public PartialViewResult Register( )
{
    return PartialView( "Register", new RegisterViewModel() );
}

Le site Login & Register seraient alors des contrôles utilisateur résidant soit dans le dossier de vue actuel, soit dans le dossier partagé, et ressembleraient à quelque chose comme ceci :

/Views/Shared/Login.cshtml : (ou /Views/MyView/Login.cshtml)

@model LoginViewModel
@using (Html.BeginForm("Login", "Auth", FormMethod.Post))
{
    @Html.TextBoxFor(model => model.Email)
    @Html.PasswordFor(model => model.Password)
}

/Vues/Partagées/Enregistrement.cshtml : (ou /Views/MyView/Register.cshtml)

@model ViewModel.RegisterViewModel
@using (Html.BeginForm("Login", "Auth", FormMethod.Post))
{
    @Html.TextBoxFor(model => model.Name)
    @Html.TextBoxFor(model => model.Email)
    @Html.PasswordFor(model => model.Password)
}

Vous disposez ainsi d'un contrôleur, d'une vue et d'un fichier de vue uniques pour chaque action, chacun étant totalement distinct et ne dépendant en rien des autres.

4 votes

Cela a beaucoup de sens en termes de conception, mais en termes d'efficacité, ne doit-il pas passer par 3 cycles complets du cycle mvc ? stackoverflow.com/questions/719027/renderaction-renderpartial/

6 votes

Oui, vous avez raison : cela provoque un cycle MVC complet supplémentaire pour chaque RenderAction . J'oublie toujours qu'elle fait partie du pack Futures puisque mon projet inclut toujours cette dll par défaut. C'est vraiment selon les préférences et les exigences de l'application que l'on décide si les cycles mvc supplémentaires valent la séparation qu'ils procurent du point de vue de la conception. Souvent, vous pouvez mettre en cache le RenderAction de sorte que le seul impact que vous subissez est le léger traitement supplémentaire via l'usine du contrôleur.

0 votes

J'ai mis en œuvre ce qui précède. Qu'est-ce que j'ai manqué ? Merci de m'aider : stackoverflow.com/questions/9677818/

118voto

Hamid Points 921

Un autre moyen est d'utiliser :

@model Tuple<LoginViewModel,RegisterViewModel>

J'ai expliqué comment utiliser cette méthode à la fois dans la vue et dans le contrôleur pour un autre exemple : Deux modèles dans une vue en ASP MVC 3

Dans votre cas, vous pourriez l'implémenter en utilisant le code suivant :

Dans la vue :

@using YourProjectNamespace.Models;
@model Tuple<LoginViewModel,RegisterViewModel>

@using (Html.BeginForm("Login1", "Auth", FormMethod.Post))
{
        @Html.TextBoxFor(tuple => tuple.Item2.Name, new {@Name="Name"})
        @Html.TextBoxFor(tuple => tuple.Item2.Email, new {@Name="Email"})
        @Html.PasswordFor(tuple => tuple.Item2.Password, new {@Name="Password"})
}

@using (Html.BeginForm("Login2", "Auth", FormMethod.Post))
{
        @Html.TextBoxFor(tuple => tuple.Item1.Email, new {@Name="Email"})
        @Html.PasswordFor(tuple => tuple.Item1.Password, new {@Name="Password"})
}

Note que j'ai modifié manuellement les attributs Name de chaque propriété lors de la création du formulaire. Cela doit être fait, sinon il n'y aurait pas de correspondance correcte avec le paramètre de la méthode de type modèle lorsque les valeurs sont envoyées à la méthode associée pour traitement. Je suggère d'utiliser des méthodes distinctes pour traiter ces formulaires séparément. Pour cet exemple, j'ai utilisé les méthodes Login1 et Login2. La méthode Login1 nécessite d'avoir un paramètre de type RegisterViewModel et Login2 nécessite un paramètre de type LoginViewModel.

si un lien d'action est nécessaire, vous pouvez utiliser :

@Html.ActionLink("Edit", "Edit", new { id=Model.Item1.Id })

dans la méthode du contrôleur pour la vue, une variable de type Tuple doit être créée, puis transmise à la vue.

Exemple :

public ActionResult Details()
{
    var tuple = new Tuple<LoginViewModel, RegisterViewModel>(new LoginViewModel(),new RegisterViewModel());
    return View(tuple);
}

ou vous pouvez remplir les deux instances de LoginViewModel et RegisterViewModel avec des valeurs et les passer ensuite à la vue.

0 votes

C'est une excellente façon de procéder, merci ! Ça a fait ce dont j'avais besoin.

0 votes

J'ai essayé, mais si j'utilise EditorFor ou HiddenFor (ce qui est idéalement ce que je veux utiliser), les propriétés du modèle ne sont pas définies lorsque la fonction Login1 / Login2 les méthodes du contrôleur sont appelées. On peut supposer que le @Name= La cartographie est ignorée. Est-ce que HiddenFor avoir besoin d'une autre astuce pour cette situation ?

1 votes

Cela ne fonctionnera pas du tout - vous ne pouvez pas vous lier au modèle lorsque le formulaire est soumis.

30voto

Yini Yin Points 75

Utilisez un modèle de vue qui contient plusieurs modèles de vue :

   namespace MyProject.Web.ViewModels
   {
      public class UserViewModel
      {
          public UserDto User { get; set; }
          public ProductDto Product { get; set; }
          public AddressDto Address { get; set; }
      }
   }

A votre avis :

  @model MyProject.Web.ViewModels.UserViewModel

  @Html.LabelFor(model => model.User.UserName)
  @Html.LabelFor(model => model.Product.ProductName)
  @Html.LabelFor(model => model.Address.StreetName)

1 votes

C'est une excellente solution, et la validation du modèle fonctionne toujours sans problème. Merci !

6voto

Pnsadeghy Points 362

Un moyen simple de le faire

nous pouvons appeler tous les modèles en premier

@using project.Models

puis envoyez votre modèle avec viewbag

// for list
ViewBag.Name = db.YourModel.ToList();

// for one
ViewBag.Name = db.YourModel.Find(id);

et en vue

// for list
List<YourModel> Name = (List<YourModel>)ViewBag.Name ;

//for one
YourModel Name = (YourModel)ViewBag.Name ;

alors utilisez facilement ceci comme Modèle

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X