Передача данных в макет, которые являются общими для всех страниц

У меня есть сайт, на котором есть страница макета. Однако на этой странице макета есть данные, которые все модели страниц должны предоставлять такой заголовок страницы, название страницы и место, где мы на самом деле предназначены для помощника HTML, который я выполнял, которые выполняют некоторые действия. Также каждая страница имеет свои собственные свойства моделей просмотра.

Как я могу это сделать? Кажется, что его плохая идея набирать макет, но как я могу передать тезисы?

Ответ 1

Если вам необходимо передать те же свойства на каждую страницу, то создание базовой модели просмотра, используемой всеми вашими моделями просмотров, будет разумным. Затем ваша страница макета возьмет эту базовую модель.

Если за этими данными требуется логика, тогда это должно быть помещено в базовый контроллер, который используется всеми вашими контроллерами.

Есть много вещей, которые вы могли бы сделать, важный подход - не повторять один и тот же код в нескольких местах.

Изменить: обновить комментарии ниже

Вот простой пример, демонстрирующий концепцию.

Создайте модель базового представления, которую будут наследовать все модели просмотра.

public abstract class ViewModelBase
{
    public string Name { get; set; }
}

public class HomeViewModel : ViewModelBase
{
}

Ваша страница макета может принять это как модель.

@model ViewModelBase
<!DOCTYPE html>
<html>
    <head>
        <meta name="viewport" content="width=device-width" />
        <title>Test</title>
    </head>
    <body>
        <header>
            Hello @Model.Name
        </header>
        <div>
            @this.RenderBody()
        </div>
    </body>
</html>

Наконец, установите данные в методе действий.

public class HomeController
{
    public ActionResult Index()
    {
        return this.View(new HomeViewModel { Name = "Bacon" });
    }
}

Ответ 2

Я использовал хелпер RenderAction html для бритвы в макете.

@{
   Html.RenderAction("Action", "Controller");
 }

Мне нужно было это для простой строки. Поэтому мое действие возвращает строку и записывает ее легко. Но если вам нужны сложные данные, вы можете вернуть PartialViewResult и модель.

 public PartialViewResult Action()
    {
        var model = someList;
        return PartialView("~/Views/Shared/_maPartialView.cshtml", model);
    }

Вам просто нужно поставить свою модель на начальное частичное представление "_maPartialView.cshtml", которое вы создали

@model List<WhatEverYourObjeIs>

Затем вы можете использовать данные в модели в этом частичном представлении с помощью html.

Ответ 3

Другой вариант - создать отдельный класс LayoutModel со всеми свойствами, которые вам понадобятся в макете, а затем заполнить экземпляр этого класса в ViewBag. Я использую метод Controller.OnActionExecuting для его заполнения. Затем, в начале компоновки, вы можете вытащить этот объект из ViewBag и продолжить доступ к этому строго типизированному объекту.

Ответ 4

Предположительно, основной пример использования - получить базовую модель для представления для всех (или большинства) действий контроллера.

Учитывая, что я использовал комбинацию из нескольких из этих ответов, ответ на основной ответ на ответ Колина Бэкона.

Правильно, что это все еще логика контроллера, потому что мы заполняем viewmodel, чтобы вернуться к представлению. Таким образом, правильное место, чтобы положить это, находится в контроллере.

Мы хотим, чтобы это произошло на всех контроллерах, потому что мы используем это для страницы макета. Я использую его для частичных представлений, которые отображаются на странице макета.

Мы также все еще нуждаемся в дополнительном преимуществе строго типизированного ViewModel

Таким образом, я создал BaseViewModel и BaseController. Все контроллеры ViewModels будут наследоваться от BaseViewModel и BaseController соответственно.

Код:

BaseController

public class BaseController : Controller
{
    protected override void OnActionExecuted(ActionExecutedContext filterContext)
    {
        base.OnActionExecuted(filterContext);

        var model = filterContext.Controller.ViewData.Model as BaseViewModel;

        model.AwesomeModelProperty = "Awesome Property Value";
        model.FooterModel = this.getFooterModel();
    }

    protected FooterModel getFooterModel()
    {
        FooterModel model = new FooterModel();
        model.FooterModelProperty = "OMG Becky!!! Another Awesome Property!";
    }
}

Обратите внимание на использование OnActionExecuted, взятого из этого сообщения SO

HomeController

public class HomeController : BaseController
{
    public ActionResult Index(string id)
    {
        HomeIndexModel model = new HomeIndexModel();

        // populate HomeIndexModel ...

        return View(model);
    }
}

BaseViewModel

public class BaseViewModel
{
    public string AwesomeModelProperty { get; set; }
    public FooterModel FooterModel { get; set; }
}

HomeViewModel

public class HomeIndexModel : BaseViewModel
{

    public string FirstName { get; set; }

    // other awesome properties
}

FooterModel

public class FooterModel
{
    public string FooterModelProperty { get; set; }
}

Layout.cshtml

@model WebSite.Models.BaseViewModel
<!DOCTYPE html>
<html>
<head>
    < ... meta tags and styles and whatnot ... >
</head>
<body>
    <header>
        @{ Html.RenderPartial("_Nav", Model.FooterModel.FooterModelProperty);}
    </header>

    <main>
        <div class="container">
            @RenderBody()
        </div>

        @{ Html.RenderPartial("_AnotherPartial", Model); }
        @{ Html.RenderPartial("_Contact"); }
    </main>

    <footer>
        @{ Html.RenderPartial("_Footer", Model.FooterModel); }
    </footer>

    < ... render scripts ... >

    @RenderSection("scripts", required: false)
</body>
</html>

_Nav.cshtml

@model string
<nav>
    <ul>
        <li>
            <a href="@Model" target="_blank">Mind Blown!</a>
        </li>
    </ul>
</nav>

Надеюсь, это поможет.

Ответ 5

Вам не нужно связываться с действиями или менять модель, просто используйте базовый контроллер и приведите существующий контроллер из контекста макета.

Создайте базовый контроллер с желаемыми общими данными (заголовок/страница/местоположение и т.д.) И инициализацией действия...

public abstract class _BaseController:Controller {
    public Int32 MyCommonValue { get; private set; }

    protected override void OnActionExecuting(ActionExecutingContext filterContext) {

        MyCommonValue = 12345;

        base.OnActionExecuting(filterContext);
    }
}

Убедитесь, что каждый контроллер использует базовый контроллер...

public class UserController:_BaseController {...

Приведите существующий базовый контроллер из контекста представления на вашей странице _Layout.cshml...

@{
    var myController = (_BaseController)ViewContext.Controller;
}

Теперь вы можете ссылаться на значения в базовом контроллере со страницы макета.

@myController.MyCommonValue

UPDATE

Вы также можете создать расширение страницы, которое позволит вам использовать this.

//Allows typed "this.Controller()." in cshtml files
public static class MyPageExtensions {
    public static _BaseController Controller(this WebViewPage page) => Controller<_BaseController>(page);
    public static T Controller<T>(this WebViewPage page) where T : _BaseController => (T)page.ViewContext.Controller;
}

Тогда вам нужно только помнить, чтобы использовать this.Controller(), когда вам нужен контроллер.

@{
    var myController = this.Controller(); //_BaseController
}

или конкретный контроллер, который наследуется от _BaseController...

@{
    var myController = this.Controller<MyControllerType>();
}

Ответ 6

если вы хотите передать всю модель, пойдите так в макете:

@model ViewAsModelBase
<!DOCTYPE html>
<html>
<head>
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta charset="utf-8"/>
    <link href="/img/phytech_icon.ico" rel="shortcut icon" type="image/x-icon" />
    <title>@ViewBag.Title</title>
    @RenderSection("styles", required: false)    
    <script type="text/javascript" src="http://code.jquery.com/jquery-1.8.3.min.js"></script>
    @RenderSection("scripts", required: false)
    @RenderSection("head", required: false)
</head>
<body>
    @Html.Action("_Header","Controller", new {model = Model})
    <section id="content">
        @RenderBody()
    </section>      
    @RenderSection("footer", required: false)
</body>
</html>

и добавьте это в контроллер:

public ActionResult _Header(ViewAsModelBase model)

Ответ 7

Создание базового представления, представляющего модель представления макета, является ужасным подходом. Представьте, что вы хотите иметь модель, которая представляет навигацию, определенную в макете. Вы бы сделали CustomersViewModel : LayoutNavigationViewModel? Зачем? Почему вы должны передавать данные модели навигации через каждую модель представления, которую вы имеете в решении?

Модель представления макета должна быть посвящена сама по себе и не должна заставлять остальные модели обзора зависеть от нее.

Вместо этого вы можете сделать это в своем _Layout.cshtml файле:

@{ var model = DependencyResolver.Current.GetService<MyNamespace.LayoutViewModel>(); }

Самое главное, нам не нужно new LayoutViewModel(), и мы получим все зависимости, которые LayoutViewModel имеет для нас.

например.

public class LayoutViewModel
{
    private readonly DataContext dataContext;
    private readonly ApplicationUserManager userManager;

    public LayoutViewModel(DataContext dataContext, ApplicationUserManager userManager)
    {
    }
}

Ответ 8

Я не думаю, что любой из этих ответов достаточно гибкий для приложения большого уровня предприятия. Я не поклонник чрезмерного использования ViewBag, но в этом случае, для гибкости, я сделал бы исключение. Вот что я буду делать...

У вас должен быть базовый контроллер на всех ваших контроллерах. Добавьте данные макета OnActionExecuting в ваш базовый контроллер (или OnActionExecuted, если вы хотите отложить это)...

public class BaseController : Controller
{
    protected override void OnActionExecuting(ActionExecutingContext     
        filterContext)
    {
        ViewBag.LayoutViewModel = MyLayoutViewModel;
    }
}

public class HomeController : BaseController
{
    public ActionResult Index()
    {
        return View(homeModel);
    }
}

Затем в вашем _Layout.cshtml вытащите ViewModel из ViewBag...

@{
  LayoutViewModel model = (LayoutViewModel)ViewBag.LayoutViewModel;
}

<h1>@model.Title</h1>

Или...

<h1>@ViewBag.LayoutViewModel.Title</h1>

Выполнение этого не мешает кодированию ваших контроллеров страниц или моделей.

Ответ 9

В других ответах почти все описано, как мы можем передать модель на нашу страницу макета. Но я нашел способ, с помощью которого можно передавать переменные на страницу макета динамически без использования какой-либо модели или частичного представления в вашем макете. Скажем, у вас есть эта модель -

public class SubLocationsViewModel
{
    public string city { get; set; }
    public string state { get; set; }
}

И вы хотите, чтобы город и государство были динамически. Например,

в вашем index.cshtml вы можете поместить эти две переменные в ViewBag

@model  MyProject.Models.ViewModel.SubLocationsViewModel
@{
    ViewBag.City = Model.city;
    ViewBag.State = Model.state;
}

И затем в вашем layout.cshtml вы можете получить доступ к этим переменным viewbag

<div class="text-wrap">
    <div class="heading">@ViewBag.City @ViewBag.State</div>
</div>

Ответ 10

Вы также можете использовать RenderSection, он поможет вам внедрить данные вашей Model в представление _Layout.

Вы можете ввести View Model Data, Json, Script, CSS, HTML т.д.

В этом примере я Json из моего представления Index в представление Layout.

Index.chtml

@section commonLayoutData{

    <script>

        var products = @Html.Raw(Json.Encode(Model.ToList()));

    </script>

    }

_Layout.cshtml

@RenderSection("commonLayoutData", false)

Это устраняет необходимость создания отдельной View Model базового View Model.

Надежда помогает кому-то.

Ответ 11

https://www.youtube.com/watch?v=EbB92vXsE8M

Гюнеш ее akşam batıp ее gün doğuyorsa Çiçekler solup solup tekrar açıyorsa En Derin yaralar kapanıyorsa En büyük acılar unutuluyorsa Neden korkulur hayatta söyleyin бана Бен neden kalayım söyleyin в той же бана

Эльбетт Базен Чичек Ачип Базен Солаках Эльбет Базен Чичек Эльбет Базен Хызла Донуп Базен Дурачаг Эльбет Базен Сойлеип Базен Сусакайм

İnanmadım asla inanamam Her şeyin bir sonu olduğuna

Elbette bugün ağlıyorsam yarın güleceğim Elbette önce çekip gidip sonra döneceğim

Ответ 12

Есть еще один способ справиться с этим. Может быть, не самый чистый путь с архитектурной точки зрения, но он позволяет избежать большой боли, связанной с другими ответами. Просто добавьте сервис в макет Razor и затем вызовите метод, который получает необходимые данные:

@inject IService myService

Затем в представлении макета:

@if (await myService.GetBoolValue()) {
   // Good to go...
}

Опять же, не чисто с точки зрения архитектуры (очевидно, что сервис не должен быть внедрен непосредственно в представление), но он выполняет свою работу.

Ответ 13

Почему никто не предложил методы расширения для ViewData?

Опция 1

На мой взгляд, наименее навязчивое и простое решение проблемы. Нет жестко закодированных строк. Нет наложенных ограничений. Нет волшебного кодирования. Нет сложного кода.

public static class ViewDataExtensions
{
    private const string TitleData = "Title";
    public static void SetTitle<T>(this ViewDataDictionary<T> viewData, string value) => viewData[TitleData] = value;
    public static string GetTitle<T>(this ViewDataDictionary<T> viewData) => (string)viewData[TitleData] ?? "";
}

Установить данные на странице

ViewData.SetTitle("abc");

Вариант № 2

Другой вариант, облегчающий объявление поля.

public static class ViewDataExtensions
{
    public static ViewDataField<string, V> Title<V>(this ViewDataDictionary<V> viewData) => new ViewDataField<string, V>(viewData, "Title", "");
}

public class ViewDataField<T,V>
{
    private readonly ViewDataDictionary<V> _viewData;
    private readonly string _field;
    private readonly T _defaultValue;

    public ViewDataField(ViewDataDictionary<V> viewData, string field, T defaultValue)
    {
        _viewData = viewData;
        _field = field;
        _defaultValue = defaultValue;
    }

    public T Value {
        get => (T)(_viewData[_field] ?? _defaultValue);
        set => _viewData[_field] = value;
    }
}

Установить данные на странице. Объявление проще, чем первый вариант, но синтаксис использования немного длиннее.

ViewData.Title().Value = "abc";

Вариант № 3

Затем можно объединить это с возвратом одного объекта, содержащего все связанные с макетом поля со значениями по умолчанию.

public static class ViewDataExtensions
{
    private const string LayoutField = "Layout";
    public static LayoutData Layout<T>(this ViewDataDictionary<T> viewData) => 
        (LayoutData)(viewData[LayoutField] ?? (viewData[LayoutField] = new LayoutData()));
}

public class LayoutData
{
    public string Title { get; set; } = "";
}

Установить данные на странице

var layout = ViewData.Layout();
layout.Title = "abc";

Этот третий вариант имеет несколько преимуществ, и я думаю, что он является лучшим вариантом в большинстве случаев:

  • Простейшее объявление полей и значений по умолчанию.

  • Простейший синтаксис использования при настройке нескольких полей.

  • Позволяет устанавливать различные виды данных в ViewData (например, Layout, Header, Navigation).

  • Позволяет дополнительный код и логику в классе LayoutData.

PS Не забудьте добавить пространство имен ViewDataExtensions в _ViewImports.cshtml

Ответ 14

вместо этого вы всегда можете использовать другой подход, который также быстро

создайте новый частичный вид в общем каталоге и вызовите частичный вид в вашем макете как

@Html.Partial("MyPartialView")

в вашем частичном представлении вы можете вызвать свой db и выполнить то, что вы хотите сделать

@{
    IEnumerable<HOXAT.Models.CourseCategory> categories = new HOXAT.Models.HOXATEntities().CourseCategories;
}

<div>
//do what ever here
</div>

при условии, что вы добавили свою базу данных Entity Framework

Ответ 15

Это невероятно, что никто не сказал это здесь. Передача модели представления через базовый контроллер - беспорядок. Мы используем заявки пользователей для передачи информации на страницу макета (например, для отображения пользовательских данных на панели навигации). Есть еще одно преимущество. Данные хранятся с помощью файлов cookie, поэтому нет необходимости извлекать данные в каждом запросе с помощью частичных данных. Просто займись поиском "претензий по сети asp".

Ответ 16

Вы можете использовать следующее:

 @{ 
    ApplicationDbContext db = new ApplicationDbContext();
    IEnumerable<YourModel> bd_recent = db.YourModel.Where(m => m.Pin == true).OrderByDescending(m=>m.ID).Select(m => m);
}
<div class="col-md-12">
    <div class="panel panel-default">
        <div class="panel-body">
            <div class="baner1">
                <h3 class="bb-hred">Recent Posts</h3>
                @foreach(var item in bd_recent)
                {
                    <a href="/BaiDangs/BaiDangChiTiet/@item.ID">@item.Name</a>
                }
            </div>
        </div>
    </div>
</div>