Je veux avoir 2 modèles dans une vue. La page contient à la fois 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 contenant ces 2 ViewModels?
public BigViewModel
{
public LoginViewModel LoginViewModel{get; set;}
public RegisterViewModel RegisterViewModel {get; set;}
}
J'ai besoin que les attributs de validation soient mis en avant dans la vue. C'est pourquoi j'ai besoin des ViewModels.
N'y a-t-il pas un autre moyen tel que (sans la 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)
}
avec votre BigViewModel vous faites:
@model BigViewModel
@using(Html.BeginForm()) {
@Html.EditorFor(o => o.LoginViewModel.Email)
...
}
vous pouvez créer 2 vues supplémentaires
Login.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 transmettre le viewmodel/viewdata
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)}
utiliser des parties ajax de votre site Web pour devenir plus indépendant
iframes
, mais ce n'est probablement pas le cas
Je recommanderais d'utiliser Html.RenderAction
et PartialViewResults pour accomplir cela; cela vous permettra d’afficher les mêmes données, mais chaque vue partielle aurait toujours un modèle de vue unique et ne nécessiterait plus de BigViewModel
Donc, votre vue contient quelque chose comme ce qui suit:
@Html.RenderAction("Login")
@Html.RenderAction("Register")
Où Login
& Register
sont les 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() );
}
Les Login
& Register
seraient alors des contrôles utilisateur résidant soit dans le dossier View actuel, soit dans le dossier Shared, et voudraient 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)
}
/Views/Shared/Register.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)
}
Et là, vous avez une seule action de contrôleur, vue et fichier de vue pour chaque action avec chaque totalement distinct et ne dépend pas les uns des autres pour rien.
Une autre façon consiste à 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 dans ASP MVC
Dans votre cas, vous pouvez 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"})
}
Notez que j'ai modifié manuellement les attributs de nom pour chaque propriété lors de la création du formulaire. Cela doit être fait, sinon, il ne serait pas correctement mappé sur le paramètre de type de la méthode 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 un paramètre de type RegisterViewModel et Login2 nécessite un paramètre de type LoginViewModel.
si un lien action est requis, 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);
}
vous pouvez également renseigner les valeurs de deux instances de LoginViewModel et de RegisterViewModel, puis les transmettre à la vue.
Utilisez un modèle de vue contenant 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)
Dois-je créer une autre vue contenant ces 2 vues?
N'y a-t-il pas un autre moyen tel que (sans BigViewModel):
Oui, vous pouvez utiliser Tuple (apporte de la magie dans la vue ayant plusieurs modèles).
Code:
@model Tuple<LoginViewModel, RegisterViewModel>
@using (Html.BeginForm("Login", "Auth", FormMethod.Post))
{
@Html.TextBoxFor(Tuple=> Tuple.Item.Name)
@Html.TextBoxFor(Tuple=> Tuple.Item.Email)
@Html.PasswordFor(Tuple=> Tuple.Item.Password)
}
@using (Html.BeginForm("Login", "Auth", FormMethod.Post))
{
@Html.TextBoxFor(Tuple=> Tuple.Item1.Email)
@Html.PasswordFor(Tuple=> Tuple.Item1.Password)
}
Ajouter ce ModelCollection.cs à vos modèles
using System;
using System.Collections.Generic;
namespace ModelContainer
{
public class ModelCollection
{
private Dictionary<Type, object> models = new Dictionary<Type, object>();
public void AddModel<T>(T t)
{
models.Add(t.GetType(), t);
}
public T GetModel<T>()
{
return (T)models[typeof(T)];
}
}
}
Manette:
public class SampleController : Controller
{
public ActionResult Index()
{
var model1 = new Model1();
var model2 = new Model2();
var model3 = new Model3();
// Do something
var modelCollection = new ModelCollection();
modelCollection.AddModel(model1);
modelCollection.AddModel(model2);
modelCollection.AddModel(model3);
return View(modelCollection);
}
}
La vue:
enter code here
@using Models
@model ModelCollection
@{
ViewBag.Title = "Model1: " + ((Model.GetModel<Model1>()).Name);
}
<h2>Model2: @((Model.GetModel<Model2>()).Number</h2>
@((Model.GetModel<Model3>()).SomeProperty
un moyen simple de faire ça
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 ;
puis utilisez facilement ceci comme modèle
Je tiens à dire que ma solution ressemblait à la réponse fournie sur cette page de stackoverflow: ASP.NET MVC 4, plusieurs modèles dans une vue?
Cependant, dans mon cas, la requête linq utilisée dans leur contrôleur ne fonctionnait pas pour moi.
Ceci est dit requête:
var viewModels =
(from e in db.Engineers
select new MyViewModel
{
Engineer = e,
Elements = e.Elements,
})
.ToList();
Par conséquent, "dans votre vue, spécifiez simplement que vous utilisez une collection de modèles de vues" ne m'a pas fonctionné non plus.
Cependant, une légère variation de cette solution a fonctionné pour moi. Voici ma solution au cas où cela aiderait quelqu'un.
Voici mon modèle de vue dans lequel je sais que je n'aurai qu'une seule équipe, mais que cette équipe peut avoir plusieurs conseils (et que j'ai un dossier ViewModels dans mon dossier Modèles btw, d'où l'espace de noms):
namespace TaskBoard.Models.ViewModels
{
public class TeamBoards
{
public Team Team { get; set; }
public List<Board> Boards { get; set; }
}
}
Maintenant c'est mon contrôleur. C'est la différence la plus significative de la solution dans le lien référencé ci-dessus. Je construis le ViewModel pour envoyer à la vue différemment.
public ActionResult Details(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
TeamBoards teamBoards = new TeamBoards();
teamBoards.Boards = (from b in db.Boards
where b.TeamId == id
select b).ToList();
teamBoards.Team = (from t in db.Teams
where t.TeamId == id
select t).FirstOrDefault();
if (teamBoards == null)
{
return HttpNotFound();
}
return View(teamBoards);
}
Alors, à mon avis, je ne le spécifie pas sous forme de liste. Je viens de faire "@model TaskBoard.Models.ViewModels.TeamBoards" Ensuite, je n'ai besoin que d'un pour chaque lorsque j'itère sur le tableau de bord de l'équipe. Voici mon avis:
@model TaskBoard.Models.ViewModels.TeamBoards
@{
ViewBag.Title = "Details";
}
<h2>Details</h2>
<div>
<h4>Team</h4>
<hr />
@Html.ActionLink("Create New Board", "Create", "Board", new { TeamId = @Model.Team.TeamId}, null)
<dl class="dl-horizontal">
<dt>
@Html.DisplayNameFor(model => Model.Team.Name)
</dt>
<dd>
@Html.DisplayFor(model => Model.Team.Name)
<ul>
@foreach(var board in Model.Boards)
{
<li>@Html.DisplayFor(model => board.BoardName)</li>
}
</ul>
</dd>
</dl>
</div>
<p>
@Html.ActionLink("Edit", "Edit", new { id = Model.Team.TeamId }) |
@Html.ActionLink("Back to List", "Index")
</p>
ASP.NET MVC étant relativement nouveau pour moi, il a fallu un peu de temps pour le comprendre. J'espère donc que ce message aidera quelqu'un à comprendre son projet dans un délai plus court. :-)
Mon conseil est de faire un grand modèle de vue:
public BigViewModel
{
public LoginViewModel LoginViewModel{get; set;}
public RegisterViewModel RegisterViewModel {get; set;}
}
Dans votre index.cshtml, si par exemple vous avez 2 partiels:
@addTagHelper *,Microsoft.AspNetCore.Mvc.TagHelpers
@model .BigViewModel
@await Html.PartialAsync("_LoginViewPartial", Model.LoginViewModel)
@await Html.PartialAsync("_RegisterViewPartial ", Model.RegisterViewModel )
et dans le contrôleur:
model=new BigViewModel();
model.LoginViewModel=new LoginViewModel();
model.RegisterViewModel=new RegisterViewModel();
vous pouvez toujours passer le deuxième objet dans un ViewBag ou View Data.
Créez une nouvelle classe dans votre modèle et les propriétés de LoginViewModel
et RegisterViewModel
:
public class UserDefinedModel()
{
property a1 as LoginViewModel
property a2 as RegisterViewModel
}
Ensuite, utilisez UserDefinedModel
dans votre vue.
Ceci est un exemple simplifié avec IEnumerable.
J'utilisais deux modèles dans la vue: un formulaire avec des critères de recherche (modèle SearchParams) et une grille de résultats. J'avais du mal à ajouter le modèle IEnumerable et l'autre modèle à la même vue. Voici ce que je suis venu avec, espérons que cela aide quelqu'un:
@using DelegatePortal.ViewModels;
@model SearchViewModel
@using (Html.BeginForm("Search", "Delegate", FormMethod.Post))
{
Employee First Name
@Html.EditorFor(model => model.SearchParams.FirstName,
new { htmlAttributes = new { @class = "form-control form-control-sm " } })
<input type="submit" id="getResults" value="SEARCH" class="btn btn-primary btn-lg btn-block" />
}
<br />
@(Html
.Grid(Model.Delegates)
.Build(columns =>
{
columns.Add(model => model.Id).Titled("Id").Css("collapse");
columns.Add(model => model.LastName).Titled("Last Name");
columns.Add(model => model.FirstName).Titled("First Name");
})
...)
SearchViewModel.cs:
namespace DelegatePortal.ViewModels
{
public class SearchViewModel
{
public IEnumerable<DelegatePortal.Models.DelegateView> Delegates { get; set; }
public SearchParamsViewModel SearchParams { get; set; }
....
DelegateController.cs:
// GET: /Delegate/Search
public ActionResult Search(String firstName)
{
SearchViewModel model = new SearchViewModel();
model.Delegates = db.Set<DelegateView>();
return View(model);
}
// POST: /Delegate/Search
[HttpPost]
public ActionResult Search(SearchParamsViewModel searchParams)
{
String firstName = searchParams.FirstName;
SearchViewModel model = new SearchViewModel();
if (firstName != null)
model.Delegates = db.Set<DelegateView>().Where(x => x.FirstName == firstName);
return View(model);
}
SearchParamsViewModel.cs:
namespace DelegatePortal.ViewModels
{
public class SearchParamsViewModel
{
public string FirstName { get; set; }
}
}