Використання Razor в JavaScript


435

Чи можливо чи існує рішення використовувати синтаксис Razor у JavaScript, який знаходиться у view ( 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>

3
Вас може зацікавити моє оновлення щодо @:синтаксису.
StriplingWarrior

@anyone думає зробити це таким чином, принаймні помістіть дані в окремий тег скрипту, який просто визначає деякий JSON, який потім використовується в JS. 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більш правильним.

Для уточнення, @:примушує Бритву повернутись у текстовий режим, навіть якщо addMarkerвиклик дуже нагадує код C #. Потім Razor підбирає @item.Propertyсинтаксис, щоб сказати, що він повинен безпосередньо виводити вміст цих властивостей.

Оновлення 2

Варто відзначити, що код перегляду насправді не найкраще місце для розміщення коду 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);
});

3
Я не розумію вашого оновленого прикладу. Чи правильна функція додавання?
NVM

2
@NVM: Замість того, щоб виводити один і той же код JavaScript кілька разів, я пропоную створити єдину функцію javascript (яка може зберігатися у кешованому .js-файлі) та вивести кілька викликів до цієї функції. Я поняття не маю, чи правильна функція: я лише будував її на коді ОП.
StriplingWarrior

1
Чому "@ Model.Latitude" в передбаченні. Чому б не item.Latitude?
НВМ

5
Ваші змінні C # потрібно уникати. Якщо @item.Titleміститься одна цитата, цей код вибухне.
30

7
@Mark: Гарне спостереження. Насправді я, як правило, не поєдную JavaScript і Razor взагалі у власному коді: я вважаю за краще використовувати Razor для створення HTML з data-атрибутами, а потім використовувати статичний, ненав’язливий javascript для отримання цієї інформації з DOM. Але вся ця дискусія була якось поза рамками питання.
Стриптинг-воїн

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

Зверніть увагу, як я не ставлю цитати навколо цього. Якщо заголовок вже містить лапки, він не вибухне. Здається, теж добре поводиться зі словниками та анонімними об’єктами!


19
Якщо ви намагаєтеся кодувати об'єкт у вікні перегляду, немає необхідності створювати допоміжний код, такий вже існує. Цим ми користуємось весь час@Html.Raw(Json.Encode(Model))
PJH

2
Чи можете ви розширити свою відповідь PJH? Як вказати заголовок, якщо ви просто кодуєте "Модель"?
obesechicken13

2
Крім того, коли я спробував такий підхід із Model.Title, я отримав кілька додаткових цитат навколо закодованого javascript. Я не можу позбутися цитат, навіть якщо приєднаю їх до чогось іншого. Ці котирування стають частиною вашого js.
obesechicken13

1
Коментар PJH чудовий. У такий спосіб ви дезаріалізуєте серверні моделі в блок javascript.
netfed

24

Ви намагаєтеся забити квадратний кілочок у круглий отвір.

Бритва задумана як мова, що генерує HTML. Ви можете дуже добре отримати його для створення коду JavaScript, але він не був розроблений для цього.

Наприклад: Що робити, якщо Model.Titleміститься апостроф? Це порушить ваш код JavaScript, і Razor за умовчанням не вийде з нього правильно.

Можливо, було б доречніше використовувати генератор струн у функції помічника. Ймовірно, буде менше ненавмисних наслідків такого підходу.


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 повинен перейти в режим розмітки.


1
ітераційна модель (від foreach) та позначена @ Model.Latidue? яка функція ітерації? я думаю, що щось пропустило. це могло бути @ item.Latitude тощо
Нурі YILMAZ

12

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

Двигун шаблону Razor не байдуже, що він виводить, і не розрізняє <script>чи інші теги.

Однак вам потрібно кодувати рядки, щоб запобігти атакам XSS .


2
Я оновив своє запитання, для мене це не працює - багато ідей, що не так? дякую
raklos

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.Ви впевнені, що це aobut? stackoverflow.com/questions/33666065/…
HMR

2
@HMR: Ця функція не існувала, коли я писав цю відповідь.
СЛАкс

11

Я віддаю перевагу "<! -" "->" як "текст>"

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

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

</script>

це дивно одне рішення, яке працювало для мене, тому що в тексті, який мені потрібно було включити, було кілька роздільників, які не сподобалось @:<text>
бритві

8

Ще одне, що потрібно додати - я виявив, що синтаксис hilighter (і, можливо, компілятор) 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

Простий і хороший прямий приклад:

<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(змінна на стороні сервера) і помістив його в код, який він пише в тег сценарію.


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.


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>

4

Нарешті я знайшов рішення (* .vbhtml):

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

    /* String */
    var str = '@stringvariable';
    alert(str);
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.