Как абстрагировать общие фрагменты разметки с помощью ASP.NET MVC - PullRequest
4 голосов
/ 03 ноября 2010

На моем сайте ASP.NET MVC 2 много просмотров с большим содержанием.Они содержат несколько повторяющихся шаблонов HTML.При использовании ASP.NET Webforms класс, производный от WebControl, может инкапсулировать эти шаблоны.Мне бы хотелось несколько советов о правильном подходе к этой проблеме с MVC.

Подробное объяснение

Шаблоны, не похожие на следующую HTML-разметку, продолжают появляться во всех этих представлениях.Разметка представляет собой изолированную коробку содержимого:

<div class="top container">
    <div class="header">
       <p>The title</p>
       <em>(and a small note)</em>
    </div>
    <div class="simpleBox rounded">
       <p>This is content.</p>
       <p><strong>Some more content</strong></p>
    </div>
</div>

Это тривиальный пример, но существуют более сложные повторяющиеся шаблоны.В ASP.NET Webforms я бы абстрагировал такой код в WebControl (скажем, я бы назвал его BoxControl), добавив его на страницу, подобную этой:

<foo:BoxControl runat="server">
  <Header>The title</Header>
  <Note>(and a small note)</Note>
  <Content>
     <p>This is content.</p>
     <p><strong>Some more content</strong></p>
  </Content>
</foo:BoxControl>

Эта абстракция облегчает адаптациюспособ построения бокса по всему сайту, просто изменив исходный код BoxControl.Он также аккуратно сохраняет статический HTML-контент на странице просмотра, даже при объединении нескольких элементов BoxControl на странице.Еще одним преимуществом является то, что HTML, используемый в качестве контента, распознается в среде IDE, что обеспечивает подсветку / проверку синтаксиса.

Насколько я понимаю, WebControls не рекомендуется использовать в ASP.NET MVC.Вместо WebControl я мог бы выполнить абстракцию с частичным представлением.Такое представление затем будет включено в страницу просмотра следующим образом:

<%= Html.Partial("BoxControl", new {
  Header="The Title", 
  Note="(and a small note)", 
  Content="<p>This is content.</p><p><strong>Some more content</strong></p>"});
%>

Это не идеальный вариант, поскольку параметр «Content» может стать очень длинным, а IDE при его передаче не обрабатывает его как HTML.сюда.

Рассмотренные решения Сильно типизированные ViewModels могут быть переданы в вызов Html.Partial вместо длинных параметров, показанных выше.Но тогда мне пришлось бы извлекать контент откуда-то еще (CMS или файл ресурсов).Я бы хотел, чтобы содержимое содержалось на странице просмотра.

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

Разве я не хочу абстрагироваться от разметки?Или есть подход, подходящий для MVC, который я здесь пропускаю?В чем недостаток «греха» при использовании WebControl?

Ответы [ 4 ]

4 голосов
/ 22 января 2013

Существует решение этой проблемы, хотя способ получить его немного хитрее, чем другие фреймворки, такие как Ruby on Rails.

Я использовал этот метод для создания разметки для контрольной группы Twitter Bootstrapсинтаксис, который выглядит следующим образом:

<div class="control-group">
  <label class="control-label">[Label text here]</label>
  <div class="controls">
    [Arbitrary markup here]
  </div>
</div>

Вот как:

1) Создайте модель для общего фрагмента разметки.Модель должна написать разметку при построении и снова при утилизации:

using System;
using System.Web.Mvc;

namespace My.Name.Space
{
  public class ControlGroup : IDisposable
  {
    private readonly ViewContext m_viewContext;
    private readonly TagBuilder m_controlGroup;
    private readonly TagBuilder m_controlsDiv;

    public ControlGroup(ViewContext viewContext, string labelText)
    {
      m_viewContext = viewContext;
      /*
       * <div class="control-group">
       *  <label class="control-label">Label</label>
       *  <div class="controls">
       *   input(s)
       *  </div>
       * </div>
       */

      m_controlGroup = new TagBuilder("div");
      m_controlGroup.AddCssClass("control-group");
      m_viewContext.Writer.Write(m_controlGroup.ToString(TagRenderMode.StartTag));

      if (labelText != null)
      {
        var label = new TagBuilder("label");
        label.AddCssClass("control-label");
        label.InnerHtml = labelText;
        m_viewContext.Writer.Write(label.ToString());
      }

      m_controlsDiv = new TagBuilder("div");
      m_controlsDiv.AddCssClass("controls");
      m_viewContext.Writer.Write(m_controlsDiv.ToString(TagRenderMode.StartTag));

    }

    public void Dispose()
    {
      m_viewContext.Writer.Write(m_controlsDiv.ToString(TagRenderMode.EndTag));
      m_viewContext.Writer.Write(m_controlGroup.ToString(TagRenderMode.EndTag));
    }
  }
}

2) Создать отличный помощник Html

using System.Collections.Generic;
using System.Linq;
using System.Web.Mvc;

using My.Name.Space

namespace Some.Name.Space
{
  public static class FormsHelper
  {
    public static ControlGroup ControlGroup(this HtmlHelper helper, string labelText)
    {
      return new ControlGroup(helper.ViewContext, labelText);
    }
  }
}

3) Использовать его в представлении (код Razor)

@using (Html.ControlGroup("My label"))
{
  <input type="text" />
  <p>Arbitrary markup</p>
  <input type="text" name="moreInputFields" />
}

Так же MVC-фреймворк отображает форму с помощью метода Html.BeginForm

1 голос
/ 15 ноября 2010

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

Частичное представление При использовании частичного представления содержимое поля должно передаваться как модель представления, делая представлениеСтраница менее читаема по сравнению с объявлением содержимого HTML на месте.Помните, что контент не поступает из CMS, так что это будет означать заполнение модели представления с помощью HTML в контроллере или установку локальной переменной на странице просмотра.Оба эти метода не в состоянии использовать преимущества возможностей IDE для работы с HTML.

WebControl С другой стороны, класс, производный от WebControl, не рекомендуется, а также имеет некоторые практические проблемы.,Основная проблема в том, что декларативный, иерархический стиль традиционных страниц ASP.NET .aspx просто не соответствует процедурному стилю MVC.NET View Pages.Вы должны выбрать либо полноценный традиционный подход, либо полностью перейти на MVC.

Чтобы проиллюстрировать это, наиболее заметной проблемой в моей экспериментальной реализации была проблема переменной области действия: при итерации списка продуктов MVC-way должен использовать цикл foreach, но он вводит локальную переменную, которая не будет доступна в области действия WebControl.Традиционный подход ASP.NET заключается в использовании Repeater вместо foreach.Кажется, что это скользкий путь для использования любых традиционных элементов управления ASP.NET, потому что я подозреваю, что вам скоро понадобится объединять их все больше и больше для выполнения работы.

Простой HTML Отказ от абстракции вообще, у вас остается дублирующий код презентации.Это против DRY, но дает очень читаемый код.

1 голос
/ 03 ноября 2010

Ну, вы бы не отрисовывали частичное подобным образом, передав его строго типизированной ViewModel, например:

<%= Html.RenderPartial("BoxControl", contentModel) %>

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

Таким образом, вы можете сделать это в своем частичном представлении:

<h1><%: Model.Header %></h1>
<p><%: Model.Content %></p>

и т. д.

0 голосов
/ 03 ноября 2010

Не похоже, что у вебформ гораздо меньше html для меня, это больше похоже на боковой ход. Использование частичного в MVC может сделать его чище, но нужная HTML-разметка все равно будет там, в том или ином месте. Если вас беспокоит в основном лишний html, вы можете взглянуть на движок представления NHaml или проверить haml. веб-сайт haml .

Я ни в коем случае не эксперт по Haml, но HTML выглядит намного чище ...

.top container
    .header
       %p 
         The title
       %em 
         (and a small note)
    .simpleBox rounded
       %p 
         This is content.
       %p 
         Some more content
...