Использование Razor в JavaScript

435

Возможно ли или есть обходной путь для использования синтаксиса Razor в JavaScript, который находится в представлении ( cshtml)?

Я пытаюсь добавить маркеры на карту Google ... Например, я пробовал это, но я получаю кучу ошибок компиляции:

<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>
raklos
источник
3
Вас может заинтересовать мое обновление относительно @:синтаксиса.
StriplingWarrior
@ Любой, кто рассматривает возможность сделать это таким образом, по крайней мере поместит данные в отдельный тег сценария, который просто определяет некоторый JSON, который затем используется в JS. JS с внутренним соединением на переднем конце был унаследованным PITA для меня в ряде случаев. Не пишите код с кодом, если вам не нужно. Вместо этого передайте данные.
Эрик Реппен

Ответы:

637

Используйте <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>

Обновить:

Скотт Гатри недавно написал о @:синтаксисе в Razor, который немного менее неуклюж, чем <text>тег, если вам нужно добавить одну или две строки кода JavaScript. Следующий подход, вероятно, будет предпочтительным, поскольку он уменьшает размер сгенерированного 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 вернуться в текстовый режим, хотя addMarkercall выглядит во многом как код C #. Затем Razor выбирает @item.Propertyсинтаксис, чтобы сказать, что он должен напрямую выводить содержимое этих свойств.

Обновление 2

Стоит отметить, что код View действительно не подходит для размещения кода JavaScript. Код JavaScript должен быть помещен в статический .jsфайл, а затем он должен получить необходимые данные либо из вызова Ajax, либо путем сканирования data-атрибутов из HTML. Помимо возможности кэширования вашего кода 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);
});
StriplingWarrior
источник
3
Я не понимаю ваш обновленный пример. Правильно ли работает функция addmarker?
NVM
2
@NVM: Вместо того, чтобы выводить один и тот же код javascript несколько раз, я предлагаю создать одну функцию javascript (которую можно сохранить в кэшированном файле .js) и вывести несколько вызовов этой функции. Я понятия не имею, правильная ли функция: я просто основывал ее на коде OP.
StriplingWarrior
1
Почему '@ Model.Latitude' в foreach. Почему бы не пункт.
NVM
5
Ваши переменные C # должны быть экранированы. Если @item.Titleсодержит одну кавычку, этот код взорвется.
mpen
7
@ Марк: Хорошее наблюдение. На самом деле, я , как правило , не объединить JavaScript и Бритва вообще в моем собственном коде: Я предпочитаю использовать бритву для генерации HTML с data-атрибутами, а затем использовать статические, ненавязчивый JavaScript , чтобы почерпнуть эту информацию из DOM. Но вся эта дискуссия выходила за рамки вопроса.
StriplingWarrior
39

Я только что написал эту вспомогательную функцию. Поместите это в 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);

Обратите внимание, как я не ставлю кавычки вокруг этого. Если заголовок уже содержит кавычки, он не взорвется. Кажется, хорошо справляются со словарями и анонимными объектами!

mpen
источник
19
Если вы пытаетесь закодировать объект в представлении, нет необходимости создавать вспомогательный код, он уже существует. Мы используем это все время@Html.Raw(Json.Encode(Model))
PJH
2
Можете ли вы расширить свой ответ PJH? Как указать заголовок, если вы просто кодируете «Модель»?
obesechicken13
2
Кроме того, когда я попробовал этот подход с Model.Title, я получил несколько дополнительных цитат вокруг закодированного JavaScript. Я не могу избавиться от кавычек, даже если соединю это с чем-то другим. Эти цитаты становятся частью вашего JS.
obesechicken13
1
Комментарий PJH превосходен. Таким образом, вы десериализуете модели на стороне сервера в блок javascript.
сети
24

Вы пытаетесь засунуть квадратный колышек в круглое отверстие.

Razor был задуман как язык шаблонов, генерирующий HTML. Вы можете получить его для генерации кода JavaScript, но он не предназначен для этого.

Например: что, если Model.Titleсодержит апостроф? Это нарушит ваш код JavaScript, и Razor по умолчанию не сможет его избежать.

Вероятно, было бы более целесообразно использовать генератор String в вспомогательной функции. Вероятно, будет меньше непредвиденных последствий такого подхода.

Адам Лассек
источник
17

Какие конкретные ошибки вы видите?

Нечто подобное может работать лучше:

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

Обратите внимание, что вам нужен магический <text>тег после, foreachчтобы указать, что Razor должен переключиться в режим разметки.

marcind
источник
1
повторять модель (по foreach) и помечать @ Model.Latidue? что такое функция итерации? Я думаю, что что-то упустил. это может быть @ item.Latitude и т. д.
Nuri YILMAZ
12

Это будет хорошо работать, если оно находится на странице CSHTML, а не во внешнем файле JavaScript.

Движок шаблонов Razor не заботится о том, что он выводит, и не различает <script>другие теги.

Однако вам нужно кодировать свои строки, чтобы предотвратить атаки XSS .

SLaks
источник
2
Я обновил свой вопрос, он не работает для меня - какие-то идеи, что не так? спасибо
Раклос
3
@raklos: Тебе нужно убежать от струн. ЗвонитеHTML.Raw(Server.JavaScriptStringEncode(...))
SLaks
1
HTML.Raw(HttpUtility.JavaScriptStringEncode(...))- Свойство сервера не имеет этот метод сейчас. HttpUtility делает.
it3xl
1
The Razor template engine doesn't care what it's outputting and does not differentiate between <script> or other tags.Вы уверены, что аобуть это? stackoverflow.com/questions/33666065/…
HMR
2
@HMR: Когда я писал этот ответ, такой функции не было.
SLaks
11

Я предпочитаю "<! -" "->" как "текст>"

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

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

</script>
Фернандо Дж. С.
источник
это причудливо одно решение , которое работает для меня, потому что текст мне нужно включать были некоторые разделители, Razor не делал , как с @:и <text>методами
баддиз
8

Одна вещь, которую нужно добавить - я обнаружил, что синтаксис Razor hilighter (и, возможно, компилятор) по-разному интерпретирует положение открывающей скобки:

<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

Простой и хороший прямой пример:

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

Теперь у вас есть глобальная переменная JavaScript, razorUserNameкоторую вы можете использовать и использовать на клиенте. Ядро Razor, очевидно, извлекло значение из @User.Identity.Name(серверной переменной) и поместило его в код, который записывается в тэг скрипта.

raddevus
источник
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>

Это позволяет любые простые старые объекты CLR (POCO), которые могут быть сериализованы, используя значение по умолчанию JavascriptSerializer.

Ice2burn
источник
5

Существует также еще одна опция, чем @: и <text></text>.

Используя <script>сам блок.

Когда вам нужно сделать большие куски JavaScript в зависимости от кода Razor, вы можете сделать это следующим образом:

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

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

Плюсы этого способа в том, что он не слишком смешивает JavaScript и Razor, потому что их частое смешивание в конечном итоге вызовет проблемы с читабельностью. Также большие текстовые блоки не очень читабельны.

Туукка Линдроос
источник
4

Ни одно из предыдущих решений не работает правильно ... Я перепробовал все способы, но он не дал мне ожидаемого результата ... Наконец я обнаружил, что в коде есть некоторые ошибки ... И полный код дан ниже.

<script type="text/javascript">

    var map = new google.maps.Map(document.getElementById('map'), {
        zoom: 10,
        center: new google.maps.LatLng(23.00, 90.00),
        mapTypeId: google.maps.MapTypeId.ROADMAP
    });

    @foreach (var item in Model)
    {
        <text>
            var markerlatLng = new google.maps.LatLng(@(item.LATITUDE), @(item.LONGITUDE));
            var title = '@(item.EMP_ID)';
            var description = '@(item.TIME)';
            var contentString = '<h3>' + "Employee " +title+ " was here at "+description+ '</h3>' + '<p>'+" "+ '</p>'

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

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

            google.maps.event.addListener(marker, 'click', (function (marker) {
                return function () {
                    infowindow.setContent(marker.content);
                    infowindow.open(map, marker);
                }
            })(marker));
        </text>
    }
</script>
Атиш Дипонгкор - MVP
источник
4

Я наконец нашел решение (* .vbhtml):

function razorsyntax() {
    /* Double */
    @(MvcHtmlString.Create("var szam =" & mydoublevariable & ";"))
    alert(szam);

    /* String */
    var str = '@stringvariable';
    alert(str);
}
SZL
источник