在MVC Razor中分享C#和Javascript之间的枚举

时间:2011-12-01 17:06:07

标签: javascript asp.net-mvc-3 razor

我已经看到了将常量添加到javascript文件中的问题的有用答案,因此它可以与剃刀视图一起使用:Share constants between C# and Javascript in MVC Razor

我希望能够做同样的事情,除了定义枚举,但我不知道如何将C#Enum转换为javascript中的常量。

关闭GetType(),似乎没有办法实际获得常量值。

3 个答案:

答案 0 :(得分:11)

我从几个人的答案中得到了一个混合物并编写了这个HtmlHelper扩展方法:

    public static HtmlString GetEnums<T>(this HtmlHelper helper) where T : struct
    {
        System.Text.StringBuilder sb = new System.Text.StringBuilder();
        sb.AppendLine("<script type=\"text/javascript\">");
        sb.AppendLine("if(!window.Enum) Enum = {};");
        var enumeration = Activator.CreateInstance(typeof(T));
        var enums = typeof(T).GetFields().ToDictionary(x => x.Name, x => x.GetValue(enumeration));
        sb.AppendLine("Enum." + typeof(T).Name + " = " + System.Web.Helpers.Json.Encode(enums) + " ;");
        sb.AppendLine("</script>");
        return new HtmlString(sb.ToString());
    }

然后您可以使用Razor语法调用该方法,如下所示: @(Html.GetEnums<Common.Enums.DecisionStatusEnum>())

然后会吐出这样的javascript:

<script type="text/javascript">
    if(!window.Enum) Enum = {};
    Enum.WorkflowStatus = {"value__":0,"DataEntry":1,"SystemDecisionMade":2,"FinalDecisionMade":3,"ContractCreated":4,"Complete":5} ;
</script>


然后你可以在javascript中使用它,如:

if(value == Enum.DecisionStatusEnum.Complete)

由于检查了顶部的属性(if(!window.Enum)),这允许您为多个枚举调用它,它不会覆盖全局 Enum变量,只是追加它。

答案 1 :(得分:7)

Dictionary的{​​{1}}转换略有不同。

enum

您可以更进一步,传递public ActionResult DayOfWeekEnum() { var enumType = typeof(DayOfWeek); var enumDictionary = enumType .GetFields() .Where(x => x.IsLiteral) .ToDictionary(x => x.Name, x => (int)Enum.Parse(enumType, x.Name)); var json = new JavaScriptSerializer().Serialize(enumDictionary); return JavaScript("var DayOfWeek = " + json + ";"); } 的命名空间并使用反射来查找类型。这将允许更通用的行动方法。

enum

答案 2 :(得分:6)

我之前使用的另一种方法是使用t4模板来完成这种工作。与t4mvc的工作方式类似,如果您知道如何使用它,这可能是一个非常强大的工具。

这个想法是在t4模板中,你在项目中搜索并查找枚举,当它找到一个枚举时,你需要模板来转换它并根据C#代码吐出一些javascript。我第一次使用T4Templates,它让我大吃一惊,并没有很多很好的资源,但它们可以是特殊的(参见t4mvc)

在你链接到的另一个问题中使用模板而不是控制器动作的优点是,t4模板引擎生成的文件是输出是常规js文件,可以像任何其他JavaScript文件一样提供/缩小,而不是要求MVC堆栈的开销来满足请求。

如果你有兴趣,我可能会挖掘一个例子。我可能没有在存储库中存放过。

修改

所以我挖了一个例子,我从它的原始形式编辑了一下,并没有测试它,但你应该得到这个想法。它有点长,所以我把它作为github gist。但我会在这里强调一些重要的块。

首先,T4 templates是Visual Studio中内置的模板引擎,控件块是用C#编写的(如果需要,可以用VB编写)。我不是想象中的专家,也不是自称是一个人,但我会分享我能做到的。因此,这些文件在visual studio项目中显示类似于其他“代码隐藏”类型的项目,您可以在其中展开.tt项目并查看其后面生成的模板文件。

让我们深入研究:

<#@ template language="C#v3.5" debug="true" hostspecific="true" #>

第一行设置控制块的语言。如你所见,我将使用C#。

<#@ output extension=".js" #>

接下来,我正在设置生成文件的扩展名。在这种情况下,我说我想生成一个.js文件。因此,当我将此模板放入解决方案时,让我们enum.tt,当我运行模板时,它将创建一个名为enum.js的文件。您可以控制生成的文件(或多个文件)。例如,t4mvc可以选择生成一堆不同的文件(每个控制器一个)或生成一个t4mvc.cs文件。

接下来,您将找到我需要使用的一堆程序集。一些更有趣的是:

<#@ assembly name="EnvDTE" #>
<#@ assembly name="EnvDTE80" #> 

同样,我不是专家,但您可以在msdn网站上找到这些文档。这些提供了一些核心功能,可以访问/操作visual studio解决方案。

然后有一些相当无趣的进口。你会注意到控制块是由<# .. #>分隔的(说实话,我真的不记得下一个字符的意义,它已经有一段时间了。)任何未包含在控制块中的内容都将直接写入到输出流。

这将我们带到将要编写的实际文件的开头:

window.Enum = function() {
    this.__descriptions = [];
    this.__ids = []
    this.__last_value = 0;
}

window.Enum.prototype.add = function(name, val) {
    if(val == undefined) val = ++this.__last_value;
    this[name] = val;
    this[val] = name;
    this.__ids[val] = name;
    this.__descriptions[val] = name.replace(/ShowWithEllipses$/,"...").replace(/([a-z])([A-Z])/g, "$1 $2").replace(/^\s+/,"");
    return this;
}

window.Enum.prototype.describe = function(val) { return this.__descriptions[val] };

这里我只是制作一个简单的JavaScript Enum实现。没有声称它是最好的。但是它就是这样啊。 :)

<#
Prepare(this);
foreach(ProjectItem pi in FindProjectItemsIn(CurrentProject.ProjectItems.Item("Models"))) {
    DumpEnumerationsFrom(pi);
}
#>

然后我们了解模板的内容。基本上它看起来在一个名为Models的文件夹中。并挖掘并试图找到它能找到的任何枚举。如果是这样,它会调用以下方法:

void DumpEnumerationsFrom(ProjectItem file) {
    var enumerations = new List<CodeEnum>();
    FindEnum(file.FileCodeModel.CodeElements, enumerations);

    if(enumerations.Count > 0) TT.WriteLine("// {0}",file.Name);

    foreach(CodeEnum enumeration in enumerations) {
        TT.Write("window.Enum.{0}=(new Enum())", enumeration.Name);
        foreach(CodeElement ce in enumeration.Children) {
            var cv = ce as CodeVariable;
            if(cv == null) continue;
            TT.Write("\r\n\t.add(\"{0}\", {1})", cv.Name, cv.InitExpression ?? "undefined");
        }
        TT.WriteLine(";\r\n");
    }
}

它将生成如下所示的内容:

window.Enum.TheNameOfTheEnum = (new Enum()).add("Value1",1).add("Value2",2);

因此生成的JS文件直接基于c#项目中的枚举。

但是有一些限制。一个枚举必须位于项目中的文件中(不在引用的库中),至少使用此实现可能有更聪明的方法来执行它。每次更改枚举时,都必须重新运行模板(右键单击它并选择“运行自定义工具”)。

但是有一些优点,就像我之前提到的那样,生成的文件只是一个简单的js文件,所以可以组合并运行缩小。因为它只是一个文件,它可以托管在CDN上,就像我之前提到的那样,不需要点击MVC堆栈来提供请求。

无论如何,我并不是出于各种目的而说它是最好的主意,但在我看来这是一种未充分利用的方法。希望这可能有助于提供一些启示,并给你一个调查方向。