Nhiều mô hình trong một khung nhìn


302

Tôi muốn có 2 mô hình trong một chế độ xem. Trang chứa cả LoginViewModelRegisterViewModel.

ví dụ

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; }
}

Tôi có cần tạo một ViewModel khác chứa 2 ViewModel này không?

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

Tôi cần các thuộc tính xác nhận để được đưa ra để xem. Đây là lý do tại sao tôi cần ViewModels.

Không có cách nào khác như (không có 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)
 }


1
@saeed serpooshan, cảm ơn bạn rất nhiều vì liên kết với các tùy chọn khác nhau, sau 4 năm bạn đã đăng một bình luận và nó giúp tôi, tôi chỉ sử dụng ViewBagcho mỗi người trong chế độ xem, hoạt động rất tốt
shaijut

@stom Chỉ là một FYI: tác giả bài viết luôn nhận được thông báo, nhưng nếu bạn muốn thông báo cho người khác, bạn cần đặt @trước tên của họ, như tôi đã làm ở đây.
jpaugh

Câu trả lời:


260

Có rất nhiều cách ...

  1. với BigViewModel của bạn, bạn làm:

    @model BigViewModel    
    @using(Html.BeginForm()) {
        @Html.EditorFor(o => o.LoginViewModel.Email)
        ...
    }
  2. bạn có thể tạo thêm 2 lượt xem

    Đăng nhập.cshtml

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

    và register.cshtml điều tương tự

    Sau khi tạo, bạn phải hiển thị chúng trong chế độ xem chính và chuyển cho chúng chế độ xem / chế độ xem

    vì vậy nó có thể như thế này:

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

    hoặc là

    @{Html.RenderPartial("login", Model.LoginViewModel)}
    @{Html.RenderPartial("register", Model.RegisterViewModel)}
  3. sử dụng các phần ajax của trang web của bạn trở nên độc lập hơn

  4. iframes, nhưng có lẽ đây không phải là trường hợp


2
Có vấn đề gì không nếu 2 hộp văn bản có cùng tên trên biểu mẫu do sử dụng một phần xem xét?
Shawn Mclean

2
Không, nên ổn khi nhấp vào chính phần tử bằng cách sử dụng một cái gì đó như fireorms (trên firefox) và bạn sẽ thấy một cái gì đó như id = "LoginViewModel_Email" name = "LoginViewModel.Email", vì vậy trên thực tế chúng là duy nhất! Một mô hình xem phải là những gì bạn cần, chỉ cần đăng mỗi trang lên một URL khác nhau và bạn sẽ ổn thôi
Haroon

Bộ mã hóa @Lol thực sự sẽ là 2 biểu mẫu, một biểu mẫu cho mỗi chế độ xem, nhưng dù sao nếu bạn có 2 hoặc 3 hoặc nhiều hơn cùng tên, bạn sẽ chỉ nhận được một mảng có tên đó ở phía máy chủ (nếu bạn đặt nó vào params của phương pháp hành động bài)
Omu

@Chuck Norris Tôi đang sử dụng asp.net mvc 4 và đã triển khai kỹ thuật partviewresult của bạn nhưng @Html.RenderActionđang báo cáo lỗi rằng Biểu thức phải trả về một giá trị
Deeptechtons

1
Bạn có thể giải thích làm thế nào điều này hoạt động? Tôi hiểu đây là một giải pháp cho vấn đề, nhưng tôi không thể hiểu ý nghĩa của nó. Tôi có một ví dụ tương tự (hơi khác) về vấn đề này và tôi không thể tìm ra cách khắc phục nó.
Andre

127

Tôi khuyên bạn nên sử dụng Html.RenderActionvà PartialViewResults để thực hiện điều này; nó sẽ cho phép bạn hiển thị cùng một dữ liệu, nhưng mỗi chế độ xem một phần vẫn sẽ có một mô hình chế độ xem duy nhất và loại bỏ nhu cầu về mộtBigViewModel

Vì vậy, chế độ xem của bạn chứa nội dung như sau:

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

Trường hợp Login& Registercả hai hành động trong bộ điều khiển của bạn được xác định như sau:

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

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

Các Login& Registersau đó sẽ điều khiển người dùng cư trú tại một trong hai Xem thư mục hiện tại, hoặc trong thư mục chia sẻ và muốn một cái gì đó như thế này:

/Views/Shared/Login.cshtml: (hoặc /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: (hoặc /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)
}

Và ở đó bạn có một hành động điều khiển duy nhất, xem và xem tệp cho mỗi hành động với mỗi hành động hoàn toàn khác biệt và không phụ thuộc vào nhau cho bất cứ điều gì.


4
Điều này có ý nghĩa rất lớn về mặt thiết kế, nhưng về hiệu quả, không phải trải qua 3 chu kỳ đầy đủ của chu trình mvc sao? stackoverflow.com/questions/719027/renderaction-renderpartial/ từ
Shawn Mclean

6
Có bạn đúng: nó gây ra một chu kỳ MVC đầy đủ bổ sung cho mỗi chu kỳ RenderAction. Tôi luôn quên một phần của gói tương lai vì dự án của tôi luôn bao gồm dll đó theo mặc định. Nó thực sự tùy thuộc vào các yêu cầu ứng dụng và ưu tiên như nếu các chu kỳ mvc bổ sung có giá trị tách biệt mà nó mang lại cho phía thiết kế. Rất nhiều lần bạn có thể lưu trữ các RenderActionkết quả, do đó, lần duy nhất bạn thực hiện là xử lý thêm một chút thông qua nhà máy điều khiển.
TheRightChoyce

Tôi đã thực hiện những điều trên .. tôi còn thiếu gì? Xin hãy giúp đỡ: stackoverflow.com/questions/9677818/ Lời
diegohb

Chúa ơi! Điều này làm việc hoàn hảo cho tôi ra khỏi cổng. Tôi đang xây dựng một trang web nội bộ chỉ có một vài người dùng ... vì vậy hiệu quả không phải là mối quan tâm thực sự của tôi. CẢM ƠN BẠN!
Derek Evermore

1
Phải sử dụng dấu ngoặc nhọn để làm cho PartialView hoạt động.
null

113

Một cách khác là sử dụng:

@model Tuple<LoginViewModel,RegisterViewModel>

Tôi đã giải thích cách sử dụng phương thức này cả trong khung nhìn và bộ điều khiển cho một ví dụ khác: Hai mô hình trong một khung nhìn trong ASP MVC 3

Trong trường hợp của bạn, bạn có thể thực hiện nó bằng cách sử dụng mã sau đây:

Theo quan điểm:

@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"})
}

Lưu ý rằng tôi đã thay đổi thủ công các thuộc tính Tên cho từng thuộc tính khi xây dựng biểu mẫu. Điều này cần phải được thực hiện, nếu không nó sẽ không được ánh xạ chính xác đến tham số của mô hình kiểu phương thức khi các giá trị được gửi đến phương thức liên quan để xử lý. Tôi sẽ đề nghị sử dụng các phương thức riêng biệt để xử lý các biểu mẫu này một cách riêng biệt, ví dụ này tôi đã sử dụng các phương thức Login1 và Login2. Phương thức Login1 yêu cầu phải có một tham số kiểu RegisterViewModel và Login2 yêu cầu một tham số của kiểu LoginViewModel.

nếu cần một liên kết hành động, bạn có thể sử dụng:

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

trong phương thức của bộ điều khiển cho chế độ xem, một biến loại Tuple cần được tạo và sau đó được chuyển đến dạng xem.

Thí dụ:

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

hoặc bạn có thể điền vào hai trường hợp của LoginViewModel và RegisterViewModel với các giá trị và sau đó chuyển nó vào dạng xem.


Đây là một cách tuyệt vời để xử lý nó, cảm ơn! Đã làm những gì tôi cần.
Todd Davis

Tôi đã thử điều này, nhưng nếu tôi sử dụng EditorForhoặc HiddenFor(lý tưởng nhất là những gì tôi muốn sử dụng) thì các thuộc tính mô hình không được đặt khi các phương thức Login1/ Login2bộ điều khiển được gọi. Có lẽ @Name=bản đồ đang bị bỏ qua. Có HiddenForyêu cầu một số mẹo khác cho tình huống này?
Gary Chapman

1
Điều này sẽ không làm việc ở tất cả - bạn không thể liên kết với các mô hình khi biểu mẫu được gửi

@ Hamid Cảm ơn Hamid, đối với một người mới chơi MVC, đây là câu trả lời đơn giản nhất đối với tôi. Cảm ơn bạn.
Harold_Finch

Làm thế nào bạn liên kết mô hình khi gửi biểu mẫu?
Mohammed Noureldin

28

Sử dụng mô hình khung nhìn chứa nhiều mô hình khung nhìn:

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

Theo quan điểm của bạn:

  @model MyProject.Web.ViewModels.UserViewModel

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

1
Đây là một giải pháp tuyệt vời và xác nhận mô hình vẫn hoạt động không có chất lượng. Cảm ơn!
AFM-Horizon

11

Tôi có cần thực hiện một chế độ xem khác giữ 2 chế độ xem này không?

Trả lời: Không

Không có cách nào khác như (không có BigViewModel):

, bạn có thể sử dụng Tuple (mang lại phép thuật trong chế độ xem có nhiều mô hình).

Mã số:

 @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)
     }

Điều này sẽ không ánh xạ đúng đến bộ điều khiển đã chấp nhận biểu mẫu? Tôi nghĩ rằng nó sẽ tìm "Mục" trong trường hợp của bạn trước khi tìm "tên".
SolidSnake4444

7

Thêm ModelCollection.cs này vào Mô hình của bạn

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)];
   }
 }
}

Điều khiển:

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);
  }
}

Chế độ xem:

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

Tôi thích cách tiếp cận này vì nó cho phép tôi sử dụng các mô hình khác nhau trong cùng một chế độ xem mà không cần phải có chúng giao nhau.
Matt nhỏ

6

một cách đơn giản để làm điều đó

chúng ta có thể gọi tất cả các mô hình đầu tiên

@using project.Models

sau đó gửi mô hình của bạn với viewbag

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

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

và trong tầm nhìn

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

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

sau đó dễ dàng sử dụng như mô hình


3

Lời khuyên của tôi là tạo ra một mô hình quan điểm lớn:

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

Trong Index.cshtml của bạn, ví dụ: nếu bạn có 2 phần:

@addTagHelper *,Microsoft.AspNetCore.Mvc.TagHelpers
@model .BigViewModel

@await Html.PartialAsync("_LoginViewPartial", Model.LoginViewModel)

@await Html.PartialAsync("_RegisterViewPartial ", Model.RegisterViewModel )

và trong bộ điều khiển:

model=new BigViewModel();
model.LoginViewModel=new LoginViewModel();
model.RegisterViewModel=new RegisterViewModel(); 

2

Tôi muốn nói rằng giải pháp của tôi giống như câu trả lời được cung cấp trên trang stackoverflow này: ASP.NET MVC 4, nhiều mô hình trong một khung nhìn?

Tuy nhiên, trong trường hợp của tôi, truy vấn linq họ sử dụng trong Trình điều khiển của họ không hoạt động với tôi.

Đây là truy vấn nói:

var viewModels = 
        (from e in db.Engineers
         select new MyViewModel
         {
             Engineer = e,
             Elements = e.Elements,
         })
        .ToList();

Do đó, "trong chế độ xem của bạn chỉ xác định rằng bạn đang sử dụng bộ sưu tập mô hình chế độ xem" cũng không hoạt động với tôi.

Tuy nhiên, một biến thể nhỏ trên giải pháp đó đã làm việc cho tôi. Đây là giải pháp của tôi trong trường hợp này giúp bất cứ ai.

Đây là mô hình xem của tôi, trong đó tôi biết tôi sẽ chỉ có một nhóm nhưng nhóm đó có thể có nhiều bảng (và tôi có thư mục ViewModels trong thư mục Mô hình của mình btw, do đó không gian tên):

namespace TaskBoard.Models.ViewModels
{
    public class TeamBoards
    {
        public Team Team { get; set; }
        public List<Board> Boards { get; set; }
    }
}

Bây giờ đây là bộ điều khiển của tôi. Đây là sự khác biệt đáng kể nhất từ ​​giải pháp trong liên kết được tham chiếu ở trên. Tôi xây dựng ViewModel để gửi đến chế độ xem khác nhau.

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);
        }

Sau đó, theo quan điểm của tôi, tôi không chỉ định nó là một danh sách. Tôi chỉ làm "@model TaskBoard.Models.ViewModels.TeamBoards" Sau đó, tôi chỉ cần một cái cho mỗi khi tôi lặp qua các bảng của Đội. Đây là quan điểm của tôi:

@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>

Tôi còn khá mới với ASP.NET MVC vì vậy tôi phải mất một chút thời gian để tìm ra điều này. Vì vậy, tôi hy vọng bài đăng này sẽ giúp ai đó tìm ra dự án của họ trong một khung thời gian ngắn hơn. :-)



1
  1. Tạo một lớp mới trong mô hình và các thuộc tính của bạn LoginViewModelRegisterViewModel:

    public class UserDefinedModel() 
    {
        property a1 as LoginViewModel 
        property a2 as RegisterViewModel 
    }
  2. Sau đó sử dụng UserDefinedModeltrong quan điểm của bạn.


1
vâng, điều đó làm việc cho tôi sau đó tôi đã tham chiếu nó như thế này: (mô hình được khai báo ở trên cùng của chế độ xem. Có 2 mô hình bên trong nó: hồ sơ và email. .... bộ điều khiển Tôi đã điền vào một trong các mô hình bằng cách sử dụng bài đăng @notso bên dưới. Tôi không cần phải điền vào becuase khác. Tôi chỉ sử dụng nó cho một đầu vào.
JustJohn

0

Đây là một ví dụ đơn giản với IEnumerable.

Tôi đã sử dụng hai mô hình trên chế độ xem: một biểu mẫu với tiêu chí tìm kiếm (mô hình SearchParams) và lưới cho kết quả và tôi đã đấu tranh với cách thêm mô hình IEnumerable và mô hình khác trên cùng một chế độ xem. Đây là những gì tôi nghĩ ra, hy vọng điều này sẽ giúp được ai đó:

@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");
        })

...)

Tìm kiếmViewModel.cs:

namespace DelegatePortal.ViewModels
{
    public class SearchViewModel
    {
        public IEnumerable<DelegatePortal.Models.DelegateView> Delegates { get; set; }

        public SearchParamsViewModel SearchParams { get; set; }
....

DelegateControll.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);
    }

Tìm kiếmParamsViewModel.cs:

namespace DelegatePortal.ViewModels
{
    public class SearchParamsViewModel
    {
        public string FirstName { get; set; }
    }
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.