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


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

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

8 104

8 ответов:

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

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

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

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

вот простой пример, чтобы продемонстрировать концепцию.

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

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

я использовал RenderAction html helper для razor в макете.

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

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

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

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

@model List<WhatEverYourObjeIs>

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

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

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

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

правильно, что это все еще логика контроллера, потому что мы заполняем 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 до это так пост

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

макет.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>

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

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

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

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

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

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

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

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

вместо этого, вы можете сделать это, в вашем _Layout.cshtml file:

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

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

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

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

в свой индекс.cshtml вы можете поместить эти две переменные в ViewBag

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

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

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