ASP.NET MVC Razor modeli düzene geçirme


97

Gördüğüm şey bir string Layout özelliği. Ancak bir modeli düzene nasıl aktarabilirim?


Farklı modelde ancak aynı düzende birkaç sayfam var
SiberianGuy

2
Bu
Paul

Bu sorunu yaşamıyorum. Modelmevcuttur _Layout. MVC5 kullanıyorum.
Toddmo

Yanıtlar:


66

Bu problemi yaşıyorsanız, görünüm modellerinizi biraz yanlış modellediniz gibi görünüyor.

Şahsen ben asla bir düzen sayfası yazmam. Ancak bunu yapmak istiyorsanız, diğer görünüm modellerinin miras aldığı bir temel görünüm modeline sahip olmanız ve mizanpajınızı temel görünüm modeline yazmanız ve belirli bir kez sayfalamanız gerekir.


11
"Şahsen ben asla bir düzen sayfası yazmam." Neden? Demek istediğim, Tüm sayfalarda görünen yan dinamik içeriği nasıl ele alıyorsunuz? Denetleyicileri görünümden atlıyor musunuz? / belki de düzende RenderAction'ı kullanmak istiyorsunuz? (Şu anda sadece bakıyorum)
eglasius

52
@eglasius, Ne tür içerikten bahsettiğimize bağlı olarak kullandığım çözüm farklı. Ancak genel bir çözüm, düzen sayfasında kendi verilerine ihtiyaç duyan parçaları oluşturmak için RenderAction'ı kullanmaktır. Düzen sayfasını yazmaktan hoşlanmamamın nedeni, tüm belirli görünüm modellerinde sizi her zaman "temel" görünüm modelini devralmaya zorlamasıdır. Tecrübelerime göre, bu genellikle çok iyi bir fikir değildir ve çoğu zaman tasarımı değiştirmek için geç olduğunda sorunlar yaşarsınız (veya uzun sürer).
Mattias Jakobsson

2
Temel modeli kalıtım yoluyla değil, toplama yoluyla dahil etmek istersem ne olur? Tasarım açısından tamamen meşru bir yol. O zaman düzeni nasıl idare ederim?
Fyodor Soikin

4
2 çözümüm var: mizanpaj için genel bir model, böylece görünüm modeli için MyLayoutModel <MyViewModel> 'i sadece mizanpajda MyViewModel ile RenderPartial kullanarak kullanabilirim. Veya statik önbelleğe alınmış parçalar için RenderAction kullanarak sayfanın bölümlerini kısmen işleyin ve dinamik parçalar için ajax çağırın. Ancak daha çok arama motoru dostu olduğu ve ajax güncellemeleriyle kolayca birleştirilebileceği için ilk çözümü tercih ediyorum.
Softlion

4
Tam olarak bunun yapıldığı eski kod üzerinde çalışmak. Bu bir kabus. Düzenlerinizi yazmayın ... lütfen!

79
  1. Denetleyicinize (veya temel denetleyicinize) MainLayoutViewModel (veya her neyse) adında, kullanmak istediğiniz türden bir özellik ekleyin.
  2. Denetleyicinizin (veya temel denetleyicinizin) yapıcısında, türü somutlaştırın ve özelliğe ayarlayın.
  3. ViewData alanına (veya ViewBag) ayarlayın
  4. Düzen sayfasında, bu özelliği kendi türünüze çevirin.

Örnek: Denetleyici:

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

}

Düzen Sayfasının Başına Örnek

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

Artık, yazılan nesneye tam erişimle düzen sayfanızdaki 'viewModel' değişkenine başvurabilirsiniz.

Bu yaklaşımı seviyorum çünkü mizanpajı kontrol eden kontrolcüdür, oysa münferit sayfa görünüm modelleri mizanpajdan bağımsız kalır.

MVC Core için notlar


Mvc Core, her eylemi ilk kez çağırdığında ViewData / ViewBag içeriğini havaya uçuruyor gibi görünüyor. Bunun anlamı, yapıcıda ViewData'nın atanmasının çalışmamasıdır. Bununla birlikte, işe yarayan şey, bir kullanmak IActionFilterve aynı işi yapmaktır OnActionExecuting. Put MyActionFilterAşağıdaki yerlerde de 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
Anlıyorum ... ama dinamikler / yayınlar jilet sayfalarının oldukça merkezi. Yapabileceğiniz bir şey, sizin için döküm yapan MainLayoutViewModel'e statik bir yöntem eklemektir (örneğin, MainLayoutViewModel.FromViewBag (this.ViewBag)), böylece en azından döküm tek bir yerde gerçekleşiyor ve burada istisnaları daha iyi halledebilirsiniz.
BlackjacketMack

@BlackjacketMack İyi bir yaklaşım ve yukarıdakileri kullanarak ve bazı değişiklikler yaparak başardım bcoz bir fark ihtiyacım vardı ve bu gerçekten bana yardımcı oldu. TempData kullanarak aynısını başarabilir miyiz evet ise, nasıl ve hayır sonra plz bana neden kullanılamayacağını söyleyin. Tekrar teşekkürler.
Zaker

2
@User - TempData, Session kullanıyor ve bana her zaman biraz garip geliyor. Anladığım kadarıyla 'bir kez oku', böylece onu okur okumaz oturumdan çıkarır (veya belki istek biter bitmez). Oturumu Sql Sunucusunda (veya Dynamo Db) depolamanız mümkündür, bu nedenle MasterLayoutViewModel'i seri hale getirmeniz gerektiği gerçeğini düşünün ... büyük olasılıkla istediğinizi değil. Yani temel olarak, ViewData olarak ayarlandığında, faturaya uyan küçük esnek bir sözlükte bellekte depolanır.
BlackjacketMack

Yeterince basit, çözümünüzü kullandım ama MVC'de yeniyim, bu yüzden bunun iyi bir uygulama olarak kabul edildiğini merak ediyorum. ya da en azından kötü değil mi?
Karim AG

1
Merhaba Karim AG, sanırım ikisinden de biraz. ViewData'da bir şeyler saklamayı kötü bir uygulama olarak görüyorum (izlemesi zor, sözlüğe dayalı, gerçekten yazılmamış) ... AMA ... tüm düzen özelliklerinizi güçlü bir şekilde yazılmış bir nesnede yazmak harika bir uygulama. Bu yüzden, tamam, orada bir şey saklayalım, ancak geri kalanını güçlü bir şekilde yazılmış bir ViewModel'e kilitleyelim diyerek uzlaşıyorum.
BlackjacketMack

30

bu oldukça basit şeyler, tek yapmanız gereken bir temel görünüm modeli oluşturmak ve TÜMÜNÜ sağlamaktır! ve TÜM demek istedim! Bu düzeni kullanacak olan görünümlerinizden tanesi o temel modeli kullanan görünümler alacak!

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

public class EmptyViewModel : ViewModelBase
{
}

public abstract class ViewModelBase
{
}

_Layout.cshtml’de:

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

ev denetleyicisindeki Dizin (örneğin) yönteminde:

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

_layout'a bir model iletmenin bir hata olduğuna katılmıyorum, bazı kullanıcı bilgileri aktarılabilir ve veriler denetleyicilerin devralma zincirinde doldurulabilir, bu nedenle yalnızca bir uygulamaya ihtiyaç vardır.

Açıkçası daha gelişmiş amaçlar için, enjeksiyon kullanarak özel statik içerik oluşturmayı düşünmeli ve bu model ad alanını _Layout.cshtml'ye dahil etmelisiniz.

ancak temel kullanıcılar için bu hile yapacak


Size katılıyorum. Teşekkürler.
Sebastián Guerrero

1
Yukarı ve sadece temel sınıf yerine bir arayüzle çalıştığını belirtmek istiyorum
VladL

29

Yaygın bir çözüm, yerleşim dosyasında kullanılan özellikleri içeren bir temel görünüm modeli yapmak ve ardından temel modelden ilgili sayfalarda kullanılan modellere devralmaktır.

Bu yaklaşımla ilgili sorun, artık kendinizi bir model problemine kilitlemiş olmanızdır, ancak başka bir sınıftan miras alabilir ve belki de çözümünüz, amaçladığınız model üzerinde kalıtımı kullanamayacağınız şekildedir.

Çözümüm ayrıca bir temel görünüm modeliyle başlıyor:

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

    public string Title { get;}
}

Daha sonra kullandığım şey, LayoutModel'den miras alan LayoutModel'in genel bir sürümüdür, örneğin:

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

    public T PageModel { get; }
}

Bu çözümle, yerleşim modeli ile model arasında miras alma ihtiyacını ortadan kaldırdım.

Şimdi devam edip Layout.cshtml'deki LayoutModel'i şu şekilde kullanabilirim:

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

Ve bir sayfada genel LayoutModel'i şu şekilde kullanabilirsiniz:

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

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

Denetleyicinizden LayoutModel türünde bir model döndürmeniz yeterlidir:

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

1
Çoklu miras sorununa ve bununla nasıl başa çıkılacağına işaret ettiğiniz için bonus! Bu, ölçeklenebilirlik için daha iyi bir cevaptır.
Brett Spencer

1
Bence en iyi çözüm. Mimari açıdan ölçeklenebilir ve bakımı yapılabilir. Bunu yapmanın doğru yolu budur. ViewBag'i veya ViewData'yı hiç sevmedim ..... Her ikisi de bana huysuz görünüyor.
Jonathan Alfaro

10

Neden gerekli modeli kısmi görünüme geçiren kendi özel denetleyicisine sahip yeni bir Kısmi Görünüm eklemiyorsunuz ve son olarak, söz konusu kısmi görünümü RenderPartial veya RenderAction kullanarak Layout.cshtml'nizde Render yapmıyorsunuz?

Bu yöntemi oturum açmış kullanıcının ad, profil resmi vb. Bilgilerini göstermek için kullanıyorum.


2
Bunu biraz daha açabilir misiniz lütfen? Bu teknikten geçen bazı blog gönderilerine bir bağlantı için minnettarım
J86

Bu işe yarayabilir ama neden performansa vurulsun? Denetleyici tarafından yapılan tüm işlemleri beklemeniz, görünüme geri dönmeniz, yalnızca kullanıcının tarayıcısının gereken verileri almak için BAŞKA bir istekte bulunmasını sağlamanız gerekir. Peki ya Düzeniniz uygun şekilde işlenecek verilere bağlıysa. IMHO bu, bu sorunun cevabı değil.
Brett Spencer

3

Eski bir soru, ancak MVC5 geliştiricileri için çözümden bahsetmek gerekirse, Modelözelliği görünümdeki ile aynı şekilde kullanabilirsiniz .

ModelGörünümü ve düzeni hem de özellik aynı sahip prosedürlerdir edilir ViewDataDictionaryEğer düzeni sayfasına modelinizi geçmesine herhangi bir ekstra çalışma yapmak zorunda kalmamak, nesne ve bir açıklamadan gerekmez@model MyModelName düzeninde.

Ancak @Model.XXX, mizanpajda kullandığınızda, intelliSense bağlam menüsünün görünmeyeceğine dikkat edin, çünkü Modelburası aynı dinamik bir nesne ViewBag.


2

Belki teknik olarak bunun üstesinden gelmenin doğru yolu değil, ama benim için en basit ve en makul çözüm, bir sınıf oluşturmak ve onu düzende somutlaştırmaktır. Aksi takdirde doğru şekilde yapmanın bir defalık istisnasıdır. Bu, düzende olduğundan daha fazla yapılırsa, ne yaptığınızı ciddi bir şekilde yeniden düşünmeniz ve projenizde ilerlemeden önce belki birkaç öğretici daha okumalısınız.

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

sonra görünümde

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

.net çekirdeğinde bunu atlayabilir ve bağımlılık enjeksiyonunu kullanabilirsiniz.

@inject My.Namespace.IMyLayoutModel myLayoutModel

Bu biraz gölgeli alanlardan biri. Ancak burada gördüğüm aşırı karmaşık alternatifler göz önüne alındığında, pratiklik adına yapmanın bir istisnadan daha fazlası olduğunu düşünüyorum. Özellikle basit tuttuğunuzdan ve herhangi bir ağır mantığın (gerçekten hiç olmaması gerektiğini, ancak gereksinimlerin farklı olduğunu) ait olduğu başka bir sınıfta / katmanda olduğundan emin olduğunuzdan emin olursanız. Temelde tek bir görünüm uğruna TÜM kontrol cihazlarınızı veya modellerinizi kirletmekten kesinlikle daha iyidir.


2

Arşivlemenin başka bir yolu var.

  1. Sadece tüm denetleyiciler için BaseController sınıfını uygulayın .

  2. Gelen BaseControllersınıfı, örneğin böyle bir model sınıf döndüren bir yöntem oluşturur.

public MenuPageModel GetTopMenu() 
{    

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

}
  1. Ve Layoutsayfada bu yöntemi çağırabilirsinizGetTopMenu()
@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

Modelinizin bir nesneler koleksiyonu (veya belki tek bir nesne) olduğunu varsayalım. Modeldeki her nesne için aşağıdakileri yapın.

1) Görüntülemek istediğiniz nesneyi ViewBag'e koyun. Örneğin:

  ViewBag.YourObject = yourObject;

2) _Layout.cshtml'nin üstüne nesnelerinizin sınıf tanımını içeren bir using ifadesi ekleyin. Örneğin:

@ YourApplication.YourClasses kullanarak;

3) _Layout'ta Nesnenize başvurduğunuzda onu yayınlayın. (2) 'de yaptığınız şeyden dolayı alçı uygulayabilirsiniz.


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

Düzeninizde IContainsMyModel kullanın.

Çözüldü. Arayüzler kuralı.


1
neden olumsuz oy aldığınızdan emin değilim. Burada yaptığınıza benzer bir arayüz kullanmak benim bağlamımda işe yaradı.
costa

-6

Örneğin

@model IList<Model.User>

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

Yeni @model yönergesi hakkında daha fazlasını okuyun


Peki ya koleksiyonun ilk öğesini Düzen modeline geçirmek istersem?
SiberianGuy

Denetleyicinizdeki ilk öğeyi getirmeli ve modeli @model Model olarak ayarlamalısınız Kullanıcı
Martin Fabik

Ancak sayfamın IList ve Layout'u almasını istiyorum - yalnızca ilk öğe
SiberianGuy

Sizi doğru anladıysam, modelin bir IList <SomeThing> olmasını ve görünümde koleksiyonun ilk öğesini almasını istersiniz? Öyleyse @ Model.First ()
Martin Fabik

6
Poster, bir modelin _Layout.cshtml sayfasına nasıl geçirileceğini soruyordu .. düzeni kullanan ana görünüm değil.
Pure.Krome
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.