代码之家  ›  专栏  ›  技术社区  ›  Joseph Tanenbaum

如何用ASP.NET MVC抽象标记的公共片段

  •  4
  • Joseph Tanenbaum  · 技术社区  · 14 年前

    我的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内容整齐地放在一起,即使在一个页面上组合多个框控件也是如此。另一个好处是用作内容的HTML被IDE识别,从而提供语法突出显示/检查。

    据我所知,在ASP.NET MVC中不鼓励使用WebControls。我可以用局部视图代替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.分部调用,而不是上面显示的冗长参数。但是我必须从其他地方(CMS或资源文件)提取内容。我希望在视图页面中包含内容。

    我也考虑过 the solution proposed by Jeffrey Palermo 但这意味着项目周围会有很多额外的文件。我希望任何视图的文本内容仅限于一个文件。

    我不应该抽象标记吗?或者,是否有一种适合MVC的方法,我可以在这里忽略?使用WebControl“sinning”有什么缺点?

    4 回复  |  直到 6 年前
        1
  •  4
  •   Lasse Skindstad Ebert    12 年前

    这个问题有一个解决方案,尽管实现这个问题的方法比其他框架(如RubyonRails)要复杂一些。

    我使用此方法为Twitter引导程序的控制组语法创建标记,如下所示:

    <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 方法

        2
  •  1
  •   RPM1984    14 年前

    好吧,您不会像那样呈现部分,将它传递给强类型的ViewModel,如下所示:

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

    contentModel 是视图模型(只是视图的一个类似POCO的存储机制),强类型的部分视图将绑定到它。

    因此,可以在局部视图中执行此操作:

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

        3
  •  1
  •   Joseph Tanenbaum    14 年前

    在考虑了答案并运行了一个实验之后,我倾向于坚持纯MVC方法,并在整个视图页面中复制一些表示代码。我想详细说明这个决定的理由。

    局部视图 当使用部分视图时,需要将框的内容作为视图模型传递,这使得视图页面的可读性比当场声明内容HTML差。记住,内容不是来自CMS,因此这意味着在控制器中用HTML填充视图模型,或者在视图页面中设置局部变量。这两种方法都无法利用IDE特性来处理HTML。

    基类 另一方面,WebControl派生类是不受欢迎的,而且也有一些实际问题。传统ASP.NET.ASPX页的声明性、层次性样式与MVC.NET视图页的过程性样式不匹配是一个主要问题。您必须选择一种完全成熟的传统方法,或者完全采用MVC。

    为了说明这一点,我的实验实现中最突出的问题是变量范围:当迭代产品列表时,MVC方法是使用 foreach 循环,但这会引入一个局部变量,该变量在WebControl范围内不可用。传统的ASP.NET方法是使用转发器而不是 前额 . 使用任何传统的ASP.NET控件似乎都是一个棘手的问题,因为我怀疑您很快就会发现需要结合越来越多的控件来完成工作。

    普通HTML 完全放弃抽象,只剩下重复的表示代码。这是对干,但产生非常可读的代码。

        4
  •  0
  •   Justin Soliz    14 年前

    在我看来,WebForms不像HTML那么少,它更像是一种横向移动。在MVC中使用分部可以使它更清晰,但是您需要的HTML标记仍然存在于那里,在一个地方或另一个地方。如果大部分额外的HTML让您感到不安,您可以查看nhaml视图引擎或查看haml。 the haml website .

    我绝对不是哈姆语专家,但HTML看起来确实更干净…

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