在 JavaScript 中使用 Razor

470

有没有可能或者有没有解决方法能够在视图(cshtml)中的JavaScript中使用Razor语法?

我正在尝试向谷歌地图添加标记...例如,我尝试了这个代码,但是我得到了很多编译错误:

<script type="text/javascript">

    // Some JavaScript code here to display map, etc.

    // Now add markers
    @foreach (var item in Model) {

        var markerlatLng = new google.maps.LatLng(@(Model.Latitude), @(Model.Longitude));
        var title = '@(Model.Title)';
        var description = '@(Model.Description)';
        var contentString = '<h3>' + title + '</h3>' + '<p>' + description + '</p>'

        var infowindow = new google.maps.InfoWindow({
            content: contentString
        });

        var marker = new google.maps.Marker({
            position: latLng,
            title: title,
            map: map,
            draggable: false
        });

        google.maps.event.addListener(marker, 'click', function () {
            infowindow.open(map, marker);
        });
    }
</script>

3
关于 @: 语法,我有更新,你可能会感兴趣。 - StriplingWarrior
任何考虑这样做的人,至少要将数据放在单独的脚本标签中,该标签只定义一些JSON,然后在JS中使用。前端耦合的JS在我多次遇到的情况下都是麻烦的工作。如果不必要,请勿使用代码编写代码。相反,传递数据。 - Erik Reppen
13个回答

677

请使用伪元素<text>,如此处所述,将Razor编译器强制返回到内容模式:

<script type="text/javascript">

    // Some JavaScript code here to display map, etc.


    // Now add markers
    @foreach (var item in Model) {
        <text>
            var markerlatLng = new google.maps.LatLng(@(Model.Latitude), @(Model.Longitude));
            var title = '@(Model.Title)';
            var description = '@(Model.Description)';
            var contentString = '<h3>' + title + '</h3>' + '<p>' + description + '</p>'

            var infowindow = new google.maps.InfoWindow({
                content: contentString
            });

            var marker = new google.maps.Marker({
                position: latLng,
                title: title,
                map: map,
                draggable: false
            });

            google.maps.event.addListener(marker, 'click', function () {
                infowindow.open(map, marker);
            });
        </text>
    }
</script>

更新:

Scott Guthrie最近发表了一篇文章,介绍Razor中的@:语法,如果你只需要添加一两行JavaScript代码,则此语法比<text>标签更简洁。下面的方法可能更可取,因为它减少了生成的HTML大小(甚至可以将addMarker函数移动到静态缓存的JavaScript文件中以进一步减小文件大小):

<script type="text/javascript">

    // Some JavaScript code here to display map, etc.
    ...
    // Declare addMarker function
    function addMarker(latitude, longitude, title, description, map)
    {
        var latLng = new google.maps.LatLng(latitude, longitude);
        var contentString = '<h3>' + title + '</h3>' + '<p>' + description + '</p>';

        var infowindow = new google.maps.InfoWindow({
            content: contentString
        });

        var marker = new google.maps.Marker({
            position: latLng,
            title: title,
            map: map,
            draggable: false
        });

        google.maps.event.addListener(marker, 'click', function () {
            infowindow.open(map, marker);
        });
    }

    // Now add markers
    @foreach (var item in Model) {
        @:addMarker(@item.Latitude, @item.Longitude, '@item.Title', '@item.Description', map);
    }
</script>

已更新上面的代码,使调用addMarker更加正确。

需要说明的是,@:强制Razor返回到文本模式,即使addMarker调用看起来很像C#代码。然后,Razor使用@item.Property语法来直接输出这些属性的内容。

更新2

值得注意的是,视图代码并不是放置JavaScript代码的好地方。JavaScript代码应该放在静态的.js文件中,然后它应该通过Ajax调用或通过扫描HTML中的data-属性来获取所需的数据。除了使JavaScript代码可以缓存外,这还避免了编码问题,因为Razor被设计用于对HTML进行编码,但不是针对JavaScript。

视图代码

@foreach(var item in Model)
{
    <div data-marker="@Json.Encode(item)"></div>
}

JavaScript 代码

$('[data-marker]').each(function() {
    var markerData = $(this).data('marker');
    addMarker(markerData.Latitude, markerData.Longitude,
              markerData.Description, markerData.Title);
});

3
我不理解你更新后的示例。addmarker函数是否正确? - NVM
2
@NVM:与其多次输出相同的JavaScript代码,我建议创建一个单一的JavaScript函数(可以保存在缓存的.js文件中),并输出对该函数的多个调用。我不知道这个函数是否正确:我只是基于OP的代码进行推断。 - StriplingWarrior
1
为什么在foreach中使用'@Model.Latitude',而不是item.Latitude? - NVM
5
你的 C# 变量需要进行转义。如果 @item.Title 包含单引号,那么这段代码将会出错。 - mpen
8
@Mark: 观察得不错。事实上,在我的代码中,我通常根本不将JavaScript和Razor结合使用:我更喜欢使用Razor生成带有"data-"属性的HTML,然后使用静态的、不显眼的JavaScript从DOM中获取这些信息。但是整个讨论有点超出了问题的范围。 - StriplingWarrior
显示剩余10条评论

41

我刚刚写了这个辅助函数。将其放置在App_Code/JS.cshtml中:

@using System.Web.Script.Serialization
@helper Encode(object obj)
{
    @(new HtmlString(new JavaScriptSerializer().Serialize(obj)));
}

然后在你的示例中,你可以像这样做:

var title = @JS.Encode(Model.Title);

请注意,我没有将引号括在它周围。如果标题已经包含引号,它不会出错。似乎也很好地处理字典和匿名对象!


20
如果您想在视图中对对象进行编码,无需创建帮助代码,因为已经存在。 我们一直使用 @Html.Raw(Json.Encode(Model)) - PJH
2
PJH,您能详细说明一下您的答案吗?如果只编码“Model”,那么如何指定标题? - obesechicken13
2
另外,当我尝试使用 Model.Title 这种方法时,我会在编码的 JavaScript 周围得到一些额外的引号。即使我将其与其他内容连接起来,也无法摆脱这些引号。这些引号成为您的 js 的一部分。 - obesechicken13
1
PJH的评论非常出色。你可以将服务器端模型反序列化为JavaScript块。 - netfed

24

你试图把方形钉子塞进圆形孔里。

Razor旨在作为生成HTML的模板语言。你也可以让它生成JavaScript代码,但这并不是它的设计目的。

例如:如果Model.Title包含一个撇号会怎样?这将破坏你的JavaScript代码,而且Razor默认情况下无法正确转义它。

更合适的方法可能是在帮助函数中使用字符串生成器。这样做可能会有更少的意外后果。


19

你看到的具体错误是什么?

这样做可能会更好:

<script type="text/javascript">

//now add markers
 @foreach (var item in Model) {
    <text>
      var markerlatLng = new google.maps.LatLng(@Model.Latitude, @Model.Longitude);
      var title = '@(Model.Title)';
      var description = '@(Model.Description)';
      var contentString = '<h3>' + title + '</h3>' + '<p>' + description + '</p>'
    </text>
}
</script>

请注意,在foreach之后需要神奇的<text>标签,以指示Razor应切换到标记模式。


1
通过foreach迭代Model并标记@Model.Latitude?迭代的功能是什么?我认为可能漏掉了一些东西。应该是@item.Latitude等。 - Nuri YILMAZ

13

只要在CSHTML页面中使用,而不是外部JavaScript文件中使用,那么就可以正常工作。

Razor模板引擎不关心它输出的内容,并且不区分<script>或其他标记。

但是,您需要对字符串进行编码以防止XSS攻击。


2
我已更新我的问题,但对我来说它不起作用 - 有什么想法是错的吗?谢谢 - raklos
3
你需要转义你的字符串。调用HTML.Raw(Server.JavaScriptStringEncode(...))来实现。 - SLaks
1
HTML.Raw(HttpUtility.JavaScriptStringEncode(...)) - Server 属性现在没有这个方法。HttpUtility 有。 - it3xl
1
Razor模板引擎并不关心它正在输出什么,也不区分<script>或其他标签。你确定吗?http://stackoverflow.com/questions/33666065/razor-template-containing-page-parts-that-mustache-will-render-on-client - HMR
2
@HMR:当我回答这个问题时,那个功能还不存在。 - SLaks

11

我更喜欢使用"<!--" "-->"作为"text>"

<script type="text/javascript">
//some javascript here     

@foreach (var item in itens)
{                 
<!--  
   var title = @(item.name)
    ...
-->

</script>

这是我找到的唯一解决方案,因为我需要包含一些分隔符,而Razor不支持@:<text>方法。 - Nando

8

需要补充的一点是,我发现Razor语法着色器(以及可能是编译器)对开括号的位置解释不同:

<script type="text/javascript">
    var somevar = new Array();

    @foreach (var item in items)
    {  // <----  placed on a separate line, NOT WORKING, HILIGHTS SYNTAX ERRORS
        <text>
        </text>
    }

    @foreach (var item in items) {  // <----  placed on the same line, WORKING !!!
        <text>
        </text>
    }
</script>

6
以下解决方案对我来说似乎比将JavaScript与Razor结合使用更准确。看看这个: https://github.com/brooklynDev/NGon 您可以将几乎任何复杂数据添加到ViewBag.Ngon中,并在JavaScript中访问它。 在控制器中:
public class HomeController : Controller
{
    public ActionResult Index()
    {
        var person = new Person { FirstName = "John", LastName = "Doe", Age = 30 };
        ViewBag.NGon.Person = person;
        return View();
    }
}

在JavaScript中:

<script type="text/javascript">
    $(function () {
        $("#button").click(function () {
            var person = ngon.Person;
            var div = $("#output");
            div.html('');
            div.append("FirstName: " + person.FirstName);
            div.append(", LastName: " + person.LastName);
            div.append(", Age: " + person.Age);
        });
    });
</script>

它允许使用默认的JavascriptSerializer序列化任何普通CLR对象(POCOs)。


6
一个简单而且好理解的例子:

一份简明扼要的示例:

<script>
    // This gets the username from the Razor engine and puts it
    // in JavaScript to create a variable I can access from the
    // client side.
    //
    // It's an odd workaraound, but it works.
    @{
        var outScript = "var razorUserName = " + "\"" + @User.Identity.Name + "\"";
    }
    @MvcHtmlString.Create(outScript);
</script>

这将在您放置上述代码的位置创建一个脚本,其外观如下所示:
<script>
    // This gets the username from the Razor engine and puts it
    // in JavaScript to create a variable I can access from
    // client side.
    //
    // It's an odd workaraound, but it works.

    var razorUserName = "daylight";
</script>

现在你有一个名为razorUserName的全局JavaScript变量,你可以在客户端访问和使用它。Razor引擎显然从@User.Identity.Name(服务器端变量)中提取了值,并将其放入写到script标记的代码中。


5

除了 @: 和 <text></text>,还有一种选项。

使用 <script> 块本身。

当您需要根据 Razor 代码执行大量 JavaScript 代码时,可以像这样执行:

@if(Utils.FeatureEnabled("Feature")) {
    <script>
        // If this feature is enabled
    </script>
}

<script>
    // Other JavaScript code
</script>

这种方式的优点在于它不会过多地混合JavaScript和Razor,因为过多地混合它们最终会导致可读性问题。而且大文本块也不太易读。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接