ASP.NET MVC Razor truyền mô hình vào bố cục


97

Những gì tôi thấy là một thuộc tính Bố cục chuỗi. Nhưng làm cách nào để chuyển một mô hình sang bố cục một cách rõ ràng?


Tôi có một số trang với mô hình khác nhau nhưng bố cục giống nhau
SiberianGuy

2
Câu hỏi stackoverflow này dường như để trả lời những gì bạn đang yêu cầu: stackoverflow.com/questions/13225315/...
Paul

Tôi không gặp vấn đề này. Modelcó sẵn trong _Layout. Tôi đang sử dụng MVC5.
toddmo

Câu trả lời:


66

Có vẻ như bạn đã lập mô hình các mô hình xem của mình hơi sai nếu bạn gặp sự cố này.

Cá nhân tôi sẽ không bao giờ gõ một trang bố cục. Nhưng nếu bạn muốn làm điều đó, bạn nên có một mô hình xem cơ sở mà các mô hình xem khác của bạn kế thừa và nhập bố cục của bạn vào mô hình xem cơ sở và bạn chuyển các trang cụ thể một lần.


11
"Cá nhân tôi sẽ không bao giờ gõ một trang bố cục." Tại sao? Ý tôi là, bạn xử lý nội dung động bên cạnh xuất hiện trong Tất cả các trang như thế nào? Bạn có bỏ qua bộ điều khiển khỏi chế độ xem không? / có thể ý bạn là sử dụng RenderAction từ bố cục? (Tôi chỉ đang xem nó ngay bây giờ)
eglasius

52
@eglasius, Giải pháp tôi sử dụng khác nhau tùy thuộc vào loại nội dung chúng ta nói về. Nhưng một giải pháp phổ biến là sử dụng RenderAction để kết xuất các phần cần dữ liệu riêng trong trang bố cục. Lý do tôi không thích nhập trang bố cục là nó sẽ buộc bạn phải luôn kế thừa một mô hình xem "cơ sở" trong tất cả các mô hình xem cụ thể của bạn. Theo kinh nghiệm của tôi, đây thường không phải là một ý tưởng hay và rất nhiều lúc bạn sẽ gặp vấn đề khi quá muộn để thay đổi thiết kế (hoặc sẽ mất nhiều thời gian).
Mattias Jakobsson

2
Điều gì sẽ xảy ra nếu tôi muốn bao gồm mô hình cơ sở theo tổng hợp, không phải theo kế thừa? Một cách hoàn toàn hợp pháp từ quan điểm thiết kế. Làm cách nào để xử lý bố cục sau đó?
Fyodor Soikin

4
Tôi có 2 giải pháp: một mô hình chung cho bố cục để tôi có thể sử dụng MyLayoutModel <MyViewModel> cho mô hình chế độ xem, chỉ sử dụng RenderPartial với MyViewModel trong bố cục. Hoặc kết xuất một phần các phần của trang bằng cách sử dụng RenderAction cho các phần được lưu trong bộ đệm tĩnh và lệnh gọi ajax cho các phần động. Nhưng tôi thích giải pháp đầu tiên hơn vì nó thân thiện với các công cụ tìm kiếm hơn và dễ dàng kết hợp với các bản cập nhật ajax.
Softlion

4
Làm việc trên mã kế thừa nơi chính xác điều này đã được thực hiện. Nó là một cơn ác mộng. Đừng gõ bố cục của bạn ... làm ơn!

79
  1. Thêm một thuộc tính vào bộ điều khiển của bạn (hoặc bộ điều khiển cơ sở) được gọi là MainLayoutViewModel (hoặc bất kỳ thứ gì) với bất kỳ loại nào bạn muốn sử dụng.
  2. Trong phương thức khởi tạo của bộ điều khiển của bạn (hoặc bộ điều khiển cơ sở), khởi tạo loại và đặt nó thành thuộc tính.
  3. Đặt nó thành trường ViewData (hoặc ViewBag)
  4. Trong trang Bố cục, truyền thuộc tính đó cho loại của bạn.

Ví dụ: Bộ điều khiển:

public class MyController : Controller
{
    public MainLayoutViewModel MainLayoutViewModel { get; set; }

    public MyController()
    {
        this.MainLayoutViewModel = new MainLayoutViewModel();//has property PageTitle
        this.MainLayoutViewModel.PageTitle = "my title";

        this.ViewData["MainLayoutViewModel"] = this.MainLayoutViewModel;
    }

}

Ví dụ trên cùng của Trang bố cục

@{
var viewModel = (MainLayoutViewModel)ViewBag.MainLayoutViewModel;
}

Bây giờ bạn có thể tham chiếu đến biến 'viewModel' trong trang bố cục của mình với toàn quyền truy cập vào đối tượng đã nhập.

Tôi thích cách tiếp cận này vì nó là bộ điều khiển kiểm soát bố cục, trong khi các mô hình xem trang riêng lẻ vẫn là bố cục bất khả tri.

Ghi chú cho MVC Core


Mvc Core dường như thổi bay nội dung của ViewData / ViewBag khi gọi mỗi hành động lần đầu tiên. Điều này có nghĩa là việc gán ViewData trong hàm tạo không hoạt động. Những gì hoạt động, tuy nhiên, đang sử dụng IActionFiltervà thực hiện cùng một công việc trong OnActionExecuting. Mặc MyActionFiltervào của bạn MyController.

public class MyActionFilter: Attribute, IActionFilter
    {
        public void OnActionExecuted(ActionExecutedContext context)
        {
        }

        public void OnActionExecuting(ActionExecutingContext context)
        {
            var myController= context.Controller as MyController;

            if (myController!= null)
            {
                myController.Layout = new MainLayoutViewModel
                {

                };

                myController.ViewBag.MainLayoutViewModel= myController.Layout;
            }
        }
    }

1
Tôi hiểu rồi ... nhưng động lực / phôi khá trung tâm trong các trang dao cạo. Một điều bạn có thể làm là thêm một phương thức tĩnh vào MainLayoutViewModel, phương thức này thực hiện truyền cho bạn (ví dụ: MainLayoutViewModel.FromViewBag (this.ViewBag)) để ít nhất quá trình truyền diễn ra ở một nơi và bạn có thể xử lý các ngoại lệ ở đó tốt hơn.
BlackjacketMack

@BlackjacketMack Cách tiếp cận tốt và tôi đã đạt được nó bằng cách sử dụng cách trên và thực hiện một số sửa đổi Tôi đã có một yêu cầu khác biệt và điều này thực sự giúp tôi cảm ơn. Chúng ta có thể sử dụng TempData như vậy không nếu có thì làm thế nào và không thì làm ơn cho tôi biết tại sao nó không thể được sử dụng. Cảm ơn một lần nữa.
Zaker

2
@User - TempData sử dụng Session và tôi luôn cảm thấy hơi khó chịu. Sự hiểu biết của tôi là nó 'đọc một lần' để ngay sau khi bạn đọc nó, nó sẽ xóa nó khỏi phiên (hoặc có thể ngay sau khi yêu cầu kết thúc). Có thể bạn lưu trữ phiên trong Sql Server (hoặc Dynamo Db), vì vậy hãy xem xét thực tế là bạn phải tuần tự hóa MasterLayoutViewModel ... không phải là những gì bạn muốn. Vì vậy, về cơ bản, đặt nó thành ViewData sẽ lưu trữ nó trong bộ nhớ trong một từ điển linh hoạt nhỏ, phù hợp với hóa đơn.
BlackjacketMack

Đơn giản thôi, tôi đã sử dụng giải pháp của bạn nhưng, tôi mới sử dụng MVC nên tôi chỉ tự hỏi đây có được coi là một phương pháp hay không? hoặc ít nhất không phải là một trong những xấu?
Karim AG

1
Xin chào Karim AG, tôi nghĩ rằng đó là một chút của cả hai. Tôi có xu hướng coi việc lưu trữ nội dung trong ViewData là một phương pháp không tốt (khó theo dõi, dựa trên từ điển, không thực sự được gõ) ... NHƯNG ... gõ tất cả các thuộc tính bố cục của bạn trong một đối tượng được gõ mạnh là một cách tuyệt vời. Vì vậy, tôi thỏa hiệp bằng cách nói, ok, hãy lưu trữ một thứ trong đó, nhưng hãy khóa phần còn lại của nó thành một ViewModel được gõ mạnh.
BlackjacketMack

30

đây là công cụ khá cơ bản, tất cả những gì bạn cần làm là tạo mô hình chế độ xem cơ sở và đảm bảo TẤT CẢ! và ý tôi là TẤT CẢ! trong số các chế độ xem của bạn sẽ sử dụng bố cục đó sẽ nhận được các chế độ xem sử dụng mô hình cơ sở đó!

public class SomeViewModel : ViewModelBase
{
    public bool ImNotEmpty = true;
}

public class EmptyViewModel : ViewModelBase
{
}

public abstract class ViewModelBase
{
}

trong _Layout.cshtml:

@model Models.ViewModelBase
<!DOCTYPE html>
  <html>
  and so on...

trong phương thức Chỉ mục (ví dụ) trong bộ điều khiển gia đình:

    public ActionResult Index()
    {
        var model = new SomeViewModel()
        {
        };
        return View(model);
    }

Index.cshtml:

@model Models.SomeViewModel

@{
  ViewBag.Title = "Title";
  Layout = "~/Views/Shared/_Layout.cshtml";
}

<div class="row">

tôi không đồng ý rằng việc chuyển một mô hình tới _layout là một lỗi, một số thông tin người dùng có thể được chuyển và dữ liệu có thể được đưa vào chuỗi kế thừa bộ điều khiển, vì vậy chỉ cần một lần triển khai.

rõ ràng cho mục đích nâng cao hơn, bạn nên xem xét việc tạo văn bản tĩnh tùy chỉnh bằng cách sử dụng tiêm và bao gồm không gian tên mô hình đó trong _Layout.cshtml.

nhưng đối với người dùng cơ bản, điều này sẽ thực hiện thủ thuật


Tôi đồng ý với bạn. Cám ơn.
Sebastián Guerrero

1
lên và chỉ muốn đề cập rằng nó cũng hoạt động với giao diện thay vì lớp cơ sở
VladL

27

Một giải pháp phổ biến là tạo mô hình dạng xem cơ sở chứa các thuộc tính được sử dụng trong tệp bố cục và sau đó kế thừa từ mô hình cơ sở sang các mô hình được sử dụng trên các trang tương ứng.

Vấn đề với cách tiếp cận này là bây giờ bạn đã tự nhốt mình vào vấn đề một mô hình chỉ có thể kế thừa từ một lớp khác, và có thể giải pháp của bạn là bạn không thể sử dụng kế thừa trên mô hình mà bạn dự định.

Giải pháp của tôi cũng bắt đầu với mô hình chế độ xem cơ sở:

public class LayoutModel
{
    public LayoutModel(string title)
    {
        Title = title;
    }

    public string Title { get;}
}

Những gì tôi sử dụng sau đó là phiên bản chung của LayoutModel kế thừa từ LayoutModel, như sau:

public class LayoutModel<T> : LayoutModel
{
    public LayoutModel(T pageModel, string title) : base(title)
    {
        PageModel = pageModel;
    }

    public T PageModel { get; }
}

Với giải pháp này, tôi đã ngắt kết nối nhu cầu thừa kế giữa mô hình bố trí và mô hình.

Vì vậy, bây giờ tôi có thể tiếp tục và sử dụng LayoutModel trong Layout.cshtml như sau:

@model LayoutModel
<!doctype html>
<html>
<head>
<title>@Model.Title</title>
</head>
<body>
@RenderBody()
</body>
</html>

Và trên một trang, bạn có thể sử dụng LayoutModel chung như thế này:

@model LayoutModel<Customer>
@{
    var customer = Model.PageModel;
}

<p>Customer name: @customer.Name</p>

Từ bộ điều khiển của bạn, bạn chỉ cần trả về một mô hình kiểu LayoutModel:

public ActionResult Page()
{
    return View(new LayoutModel<Customer>(new Customer() { Name = "Test" }, "Title");
}

1
Phần thưởng cho việc chỉ ra vấn đề đa thừa kế và cách giải quyết! Đây là một câu trả lời tốt hơn cho khả năng mở rộng.
Brett Spencer

1
Giải pháp tốt nhất theo ý kiến ​​của tôi. Từ quan điểm kiến ​​trúc, nó có thể mở rộng và bảo trì được. Đây là cách chính xác để làm điều này. Tôi chưa bao giờ thích ViewBag hoặc ViewData ..... Cả hai đều có vẻ khó hiểu với tôi.
Jonathan Alfaro

10

Tại sao bạn không thêm Chế độ xem từng phần mới với bộ điều khiển cụ thể của riêng tôi chuyển mô hình được yêu cầu sang chế độ xem từng phần và cuối cùng Hiển thị chế độ xem từng phần được đề cập trên Layout.cshtml của bạn bằng cách sử dụng RenderPartial hoặc RenderAction?

Tôi sử dụng phương pháp này để hiển thị thông tin của người dùng đã đăng nhập như tên, ảnh hồ sơ, v.v.


2
Bạn có thể giải thích thêm về điều này được không? Tôi đánh giá cao một liên kết đến một số bài đăng trên blog trải qua kỹ thuật này
J86

Điều này có thể hoạt động nhưng tại sao lại mất hiệu suất? Bạn phải đợi tất cả các xử lý được thực hiện bởi bộ điều khiển, trả lại chế độ xem, chỉ để trình duyệt của người dùng thực hiện một yêu cầu KHÁC để lấy dữ liệu cần thiết. Và điều gì sẽ xảy ra nếu Bố cục của bạn phụ thuộc vào dữ liệu để kết xuất phù hợp. IMHO đây không phải là câu trả lời cho câu hỏi này.
Brett Spencer

3

câu hỏi cũ nhưng chỉ đề cập đến giải pháp cho các nhà phát triển MVC5, bạn có thể sử dụng thuộc Modeltính giống như trong chế độ xem.

Các Modelbất động sản trong cả hai quan điểm và bố trí được assosiated với cùng ViewDataDictionaryđối tượng, vì vậy bạn không cần phải làm bất cứ việc làm thêm để vượt qua mô hình của bạn để trang bố trí, và bạn không phải khai báo @model MyModelNametrong cách bố trí.

Nhưng lưu ý rằng khi bạn sử dụng @Model.XXXtrong layout, menu ngữ cảnh intelliSense sẽ không xuất hiện vì Modelở đây là một đối tượng động giống như ViewBag.


2

Có thể về mặt kỹ thuật không phải là cách thích hợp để xử lý nó, nhưng giải pháp đơn giản và hợp lý nhất đối với tôi là chỉ tạo một lớp và tạo nó trong bố cục. Nó là một ngoại lệ một lần đối với cách làm đúng khác. Nếu điều này được thực hiện nhiều hơn so với bố cục thì bạn cần phải nghiêm túc suy nghĩ lại những gì bạn đang làm và có thể đọc thêm một vài hướng dẫn trước khi tiếp tục thực hiện dự án của mình.

public class MyLayoutModel {
    public User CurrentUser {
        get {
            .. get the current user ..
        }
    }
}

sau đó trong quan điểm

@{
    // Or get if from your DI container
    var myLayoutModel = new MyLayoutModel();
}

trong lõi .net, bạn thậm chí có thể bỏ qua điều đó và sử dụng tính năng tiêm phụ thuộc.

@inject My.Namespace.IMyLayoutModel myLayoutModel

Đó là một trong những khu vực khá mờ ám. Nhưng với những lựa chọn thay thế cực kỳ phức tạp mà tôi đang thấy ở đây, tôi nghĩ rằng nó không phải là một ngoại lệ phù hợp để thực hiện nhân danh tính thực tế. Đặc biệt nếu bạn đảm bảo giữ cho nó đơn giản và đảm bảo rằng mọi logic nặng nề (tôi sẽ tranh luận rằng thực sự không nên có bất kỳ điều gì, nhưng các yêu cầu khác nhau) nằm trong một lớp / lớp khác nơi nó thuộc về. Nó chắc chắn tốt hơn là làm ô nhiễm TẤT CẢ các bộ điều khiển hoặc mô hình của bạn vì lợi ích về cơ bản chỉ là một chế độ xem ..


2

Có một cách khác để lưu trữ nó.

  1. Chỉ cần triển khai lớp BaseController cho tất cả các bộ điều khiển .

  2. Trong BaseControllerlớp, hãy tạo một phương thức trả về một lớp Model chẳng hạn.

public MenuPageModel GetTopMenu() 
{    

var m = new MenuPageModel();    
// populate your model here    
return m; 

}
  1. Và trong Layouttrang, bạn có thể gọi phương thức đóGetTopMenu()
@using GJob.Controllers

<header class="header-wrapper border-bottom border-secondary">
  <div class="sticky-header" id="appTopMenu">
    @{
       var menuPageModel = ((BaseController)this.ViewContext.Controller).GetTopMenu();
     }
     @Html.Partial("_TopMainMenu", menuPageModel)
  </div>
</header>

0

Giả sử mô hình của bạn là một tập hợp các đối tượng (hoặc có thể là một đối tượng duy nhất). Đối với mỗi đối tượng trong mô hình, hãy làm như sau.

1) Đặt đối tượng bạn muốn hiển thị trong ViewBag. Ví dụ:

  ViewBag.YourObject = yourObject;

2) Thêm câu lệnh using ở đầu _Layout.cshtml chứa định nghĩa lớp cho các đối tượng của bạn. Ví dụ:

@ sử dụng YourApplication.YourClasses;

3) Khi bạn tham chiếu yourObject trong _Layout truyền nó. Bạn có thể áp dụng diễn viên vì những gì bạn đã làm trong (2).


-2
public interface IContainsMyModel
{
    ViewModel Model { get; }
}

public class ViewModel : IContainsMyModel
{
    public string MyProperty { set; get; }
    public ViewModel Model { get { return this; } }
}

public class Composition : IContainsMyModel
{
    public ViewModel ViewModel { get; set; }
}

Sử dụng IContainsMyModel trong bố cục của bạn.

Đã giải quyết. Quy tắc giao diện.


1
không chắc tại sao bạn lại bị bỏ phiếu Sử dụng một giao diện, tương tự như những gì bạn đã làm ở đây, đã hoạt động trong bối cảnh của tôi.
costa

-6

Ví dụ

@model IList<Model.User>

@{
    Layout="~/Views/Shared/SiteLayout.cshtml";
}

Đọc thêm về chỉ thị @model mới


Nhưng nếu tôi muốn chuyển phần tử đầu tiên của bộ sưu tập sang mô hình Bố cục thì sao?
SiberianGuy

Bạn cần phải lấy phần tử đầu tiên trong điều khiển của bạn và các thiết lập mô hình để @model Model.User
Martin Fabik

Nhưng tôi muốn trang của mình có IList và Bố cục - chỉ là yếu tố đầu tiên
SiberianGuy

Nếu tôi hiểu bạn đúng, bạn muốn mô hình là một IList <SomeThing> và trong chế độ xem có phần tử đầu tiên của bộ sưu tập? Nếu vậy việc sử dụng @ Model.First ()
Martin Fabik

6
Người đăng đã hỏi về cách chuyển một mô hình đến trang _Layout.cshtml .. không phải chế độ xem chính sử dụng bố cục.
Pure.Krome
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.