【问题标题】:MVC alternatives to UserControls用户控件的 MVC 替代品
【发布时间】:2012-05-30 20:14:54
【问题描述】:

我们正在将一个大型 WebForms 应用程序转移到 MVC。

在 WebForms 应用程序中,我们有一些可重复使用的控件 (.ascx)。例如,一个在您键入时显示用户名建议的文本框。该控件向页面添加了一些 JS/CSS,具有一些服务器端逻辑和自定义属性(如 SelectedUserID)。

在 MVC 应用程序中应该是什么?如何将这些东西封装到 MVC 应用程序中的一个可重用实体中?也许是部分观点?但是你不能从局部视图中将 JS/CSS 添加到页面的 <head> 中(最好检查它是否尚未添加)......另外,我如何返回类似于提到的 @987654324 @在局部视图中?

重新表述这个问题 - 您将如何在 MVC 应用程序中实现这样的控件?

PS。我知道您仍然可以在 MVC 应用程序中使用 .ascx 用户控件,但这似乎是一种“hacky/legacy”方式。什么是“合法”选项?

【问题讨论】:

  • 我能想到的就是写一个自定义的 jQuery 插件,然后做一些类似 $("input#myTextBox").makeSuggestable();

标签: asp.net-mvc


【解决方案1】:

您的问题非常广泛。我所有的 cmets/answer 都将在 Razor 中。在我看来,如果你要切换到 MVC,你不妨使用 Razor。有很多很好的理由来改变,但我想说,对于任何正在迁移的人来说,我的首选,最好的理由是两个; first razor 让我戒掉了一些关于如何使用 webform 进行编程的坏习惯,并且在同一个庄园里,它迫使我重新编写我的代码,而不是尝试复制、粘贴和更改原始代码以在 MVC 中工作。

该控件向页面添加了一些 JS/CSS,具有一些服务器端逻辑和自定义属性。 在 MVC 应用程序中应该是什么?

这是一个漂亮的religious software 问题。有很多方法可以做到这一点。

关于如何在 MVC 中添加 JS/CSS 的宗教观点。包含服务器端的一种方法是在 Layout/Master 模板中创建一个区域。

视图/_ViewStart.cshtml

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

Views/Shared/Layout.cshtml

<!doctype html>
  <head>
    <link href="/Styles/Global.css" rel="stylesheet" type="text/css" />
    @RenderSection("Styles", required: false)
    <script src="/Scripts/Global.js" type="text/javascript"></script>
    @RenderSection("Scritps", required: false)

这将允许每个视图选择性地(因为required=false)使用 RenderSeion 代码添加任何样式或脚本。

Views/Home/Index.cshtml

@section Styles {
    <link href="/Styles/Home/Index.css" rel="stylesheet" type="text/css" />
}

这非常简单,对于许多简单到中等复杂的网站来说可能是一个很好的解决方案。这对我来说还不够,因为我需要按照您的要求进行操作,并且仅在真正需要时才包含文件。此外,部分视图和模板无法渲染部分,我发现它是一个巨大的 PITA。所以我添加了一些 HtmlHelper 扩展方法。 (我只会展示 Javascript 的代码,因为样式表几乎是相同的代码。这些方法不允许重复的 url。

域/HtmlHelperExtensions.cshtml

public static class HtmlHelperExtensions
{
    private const string _JSViewDataName = "RenderJavaScript";
    private const string _StyleViewDataName = "RenderStyle";

    public static void AddJavaScript(this HtmlHelper HtmlHelper, string ScriptURL)
    {
        List<string> scriptList = HtmlHelper.ViewContext.HttpContext.Items[HtmlHelperExtensions._JSViewDataName] as List<string>;
        if (scriptList != null)
        {
            if (!scriptList.Contains(ScriptURL))
            {
                scriptList.Add(ScriptURL);
            }
        }
        else
        {
            scriptList = new List<string>();
            scriptList.Add(ScriptURL);
            HtmlHelper.ViewContext.HttpContext.Items.Add(HtmlHelperExtensions._JSViewDataName, scriptList);
        }
    }

    public static MvcHtmlString RenderJavaScripts(this HtmlHelper HtmlHelper)
    {
        StringBuilder result = new StringBuilder();

        List<string> scriptList = HtmlHelper.ViewContext.HttpContext.Items[HtmlHelperExtensions._JSViewDataName] as List<string>;
        if (scriptList != null)
        {
            foreach (string script in scriptList)
            {
                result.AppendLine(string.Format("<script type=\"text/javascript\" src=\"{0}\"></script>", script));
            }
        }

        return MvcHtmlString.Create(result.ToString());
    }
}

更新了 Views/Shared/Layout.cshtml

<!doctype html>
  <head>
    <link href="/Styles/Global.css" rel="stylesheet" type="text/css" />
    @Html.RenderStyleSheets()
    <script src="/Scripts/Global.js" type="text/javascript"></script>
    @Html.RenderJavascripts()

更新的视图/主页/Index.cshtml

@Html.AddStyleSheet("/Styles/Home/Index.css")

关于你的下一个问题...

如何将这些东西封装到 MVC 应用程序中的一个可重用实体中?也许是局部视图?

Razor 支持局部视图和模板。尽管从技术上讲,每种方法的功能有很大的重叠,但每种方法的设计方式都存在局限性,以便程序员在需要时可以利用它们。

部分视图需要模型/类才能被渲染。这是一个完全有效的局部视图:

/Views/Home/Index.cshtml

@Html.Partial("Partial-Menu")

/Views/Shared/Partial-Menu.cshtml

<div id="menu">
  <a href="/">Home</a>
  <a href="/Home/About">About Us</a>
</div>

另一个模板,需要一个模型才能被渲染。这是因为模板是一种将任何类或结构呈现为编辑模板或显示模板的方法。

/Models/Person.cs

public class Person
{
  public string FirstName { get; set; }
  public string LastName { get; set; }
}

/Controllers/HomeController.cs

public ActionResult Index()
{
  Person model = new Person();
  model.FirstName = "Jon";
  model.LastName = "Doe";

  return this.View(model);
}

/Views/Home/Index.cshtml

@model Project1.Models.Person

@Html.DisplayModel()

@Html.EditforModel()

/Views/Shared/DisplayTemplates/Person.cshtml

@model Project1.Models.Person

<div>@Html.LabelFor(x => x.FirstName) @Html.DisplayFor(x => x.FirstName)</div>
<div>@Html.LabelFor(x => x.LastName) @Html.DisplayFor(x => x.LastName)</div>

/Views/Shared/EditorTemplates/Person.cshtml

@model Project1.Models.Person

<div>@Html.LabelFor(x => x.FirstName) @Html.EditorFor(x => x.FirstName)</div>
<div>@Html.LabelFor(x => x.LastName) @Html.EditrorFor(x => x.LastName)</div>

在我看来,任何可能变成数据输入表单的模型都应该是模板。这是我更喜欢封装我的模型的方式。使用前面提供的扩展方法,我的局部视图和模板都可以根据需要加载包含(目前只有我的其中一个模型实际上需要使用它)。

我的偏好是每个页面呈现最多三个包含(每个 Javascript 和样式)。我基本上有一个 Global.css/js、一个控制器 Home.css/js 和一个页面 Index.css/js 作为可能的包括。我很少包含控制器。

【讨论】:

    【解决方案2】:

    到目前为止,我发现的最佳解决方案是 Razor 声明性助手。它们非常合身。

    @helper UserTextBox() {
        <input type="text" />
    
        <!--my own helper that registers scripts-->
        @Html.AddJS("../js/myscript.js") 
    }
    

    相关问题:Razor: Declarative HTML helpers

    相关说明:您不能在声明性帮助器中使用 @Html 帮助器,但有一个解决方法:https://stackoverflow.com/a/5557503/56621

    【讨论】:

      【解决方案3】:

      好吧,你不能拥有“自动”包含 css/js 的东西。这是用户必须添加到页面(主/布局或当前页面)的内容。但一般来说,人们会创建一个 Html Helper。不要指望这些是复杂的系统(如 asp.net 时代的巨大网格),但您可以用它们做很多事情。

      是的,部分页面可能更容易处理简单的事情。更简单的可能是编辑器或显示模板。

      然而,目前大多数 MVC 的“控件”都是基于 jQuery 的。

      这里有一段短视频:http://www.asp.net/mvc/videos/mvc-2/how-do-i/how-do-i-create-a-custom-html-helper-for-an-mvc-application

      不要试图将您的 javascript 包含在这些机制中。虽然它可能适用于单个控件,但如果您在一个页面上有多个控件,您将获得重复的 css/js。

      【讨论】:

      • +1 表示不以这种方式包含 javascript。其实我更喜欢让页面来决定哪些 javascript 不包括 PartialViews。
      【解决方案4】:

      我会将 js/css 添加到主布局中,然后编写一个 html 助手来呈现您的自定义控件。

      许多在 aspnet mvc 中使用 jquery 的控件都是这样工作的。

      看看这个控件here

      【讨论】:

        【解决方案5】:

        1) 对于自定义 js/css,您可以使用如下部分:

        //视图中的代码

        @section someName
        {
            ...
        }
        

        //布局页面中的代码

        @if (IsSectionDefined("someName"))
            {
                @RenderSection("someName")
            }
        

        2) 我最好做到以下几点:

        • 使用 Id 属性创建模型 SelectedUser
        • 为此模型创建模板
        • 将 SelectedUser 对象添加到需要它的任何模型中

        【讨论】:

        • @jitbit 如果你打算做 MVC,我强烈推荐使用 Razor。
        【解决方案6】:

        它们必须用空行与周围的文本隔开。 最外面的块元素的开始和结束标签不能缩进。 Markdown 不能在 HTML 块中使用。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2011-06-04
          • 2014-04-18
          • 1970-01-01
          • 2011-07-26
          • 2010-09-13
          • 1970-01-01
          • 2010-10-20
          • 2010-09-22
          相关资源
          最近更新 更多