Menggunakan Razor dalam JavaScript

435

Apakah mungkin atau ada solusi untuk menggunakan sintaks Razor dalam JavaScript yang ada dalam view ( cshtml)?

Saya mencoba menambahkan spidol ke peta Google ... Misalnya, saya mencoba ini, tetapi saya mendapatkan banyak kesalahan kompilasi:

<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
sumber
3
Anda mungkin tertarik dengan pembaruan saya tentang @:sintaksis.
StriplingWarrior
@ siapa pun mempertimbangkan melakukannya dengan cara ini, setidaknya memasukkan data dalam tag skrip terpisah yang hanya mendefinisikan beberapa JSON yang kemudian digunakan dalam JS. JS back-end-coupled di ujung depan telah menjadi warisan PITA bagi saya pada beberapa kesempatan. Jangan menulis kode dengan kode jika Anda tidak perlu. Sebaliknya, serahkan data.
Erik Reppen

Jawaban:

637

Gunakan <text>elemen pseudo, seperti dijelaskan di sini , untuk memaksa kompiler Razor kembali ke mode konten:

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

Memperbarui:

Scott Guthrie baru-baru ini memposting tentang @:sintaks di Razor, yang sedikit kurang kikuk daripada <text>tag jika Anda hanya memiliki satu atau dua baris kode JavaScript untuk ditambahkan. Pendekatan berikut mungkin lebih disukai, karena mengurangi ukuran HTML yang dihasilkan. (Anda bahkan dapat memindahkan fungsi addMarker ke file JavaScript cache yang statis untuk mengurangi ukurannya):

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

Diperbarui kode di atas untuk membuat panggilan menjadi addMarkerlebih benar.

Untuk memperjelas, @:kekuatan Razor kembali ke mode teks, meskipun addMarkerpanggilan sangat mirip dengan kode C #. Razor kemudian mengambil @item.Propertysintaks untuk mengatakan bahwa itu harus langsung mengeluarkan konten dari properti itu.

Perbarui 2

Perlu dicatat bahwa kode Tampilan sebenarnya bukan tempat yang baik untuk meletakkan kode JavaScript. Kode JavaScript harus ditempatkan dalam .jsfile statis , dan kemudian harus mendapatkan data yang dibutuhkan baik dari panggilan Ajax atau dengan memindai data-atribut dari HTML. Selain memungkinkan untuk me-cache kode JavaScript Anda, ini juga menghindari masalah dengan pengkodean, karena Razor dirancang untuk menyandikan untuk HTML, tetapi bukan JavaScript.

Lihat Kode

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

Kode JavaScript

$('[data-marker]').each(function() {
    var markerData = $(this).data('marker');
    addMarker(markerData.Latitude, markerData.Longitude,
              markerData.Description, markerData.Title);
});
StriplingWarrior
sumber
3
Saya tidak mengerti contoh Anda yang diperbarui. Apakah fungsi addmarker benar?
NVM
2
@ NVM: Daripada mengeluarkan kode javascript yang sama beberapa kali, saya sarankan membuat fungsi javascript tunggal (yang dapat disimpan dalam file .js yang di-cache), dan mengeluarkan beberapa panggilan ke fungsi itu. Saya tidak tahu apakah fungsinya benar: Saya hanya mendasarkannya pada kode OP.
StriplingWarrior
1
Mengapa '@ Model.Latitude' di foreach. Mengapa tidak barang. Banyak?
NVM
5
Variabel C # Anda harus diloloskan. Jika @item.Titleberisi satu kutipan, kode ini akan meledak.
buka
7
@ Mark: Pengamatan yang baik. Sebenarnya, saya biasanya tidak menggabungkan javascript dan Razor sama sekali dalam kode saya sendiri: Saya lebih suka menggunakan Razor untuk menghasilkan HTML dengan data-atribut, dan kemudian menggunakan javascript statis, tidak mencolok untuk mengumpulkan informasi ini dari DOM. Tetapi seluruh diskusi itu agak di luar cakupan pertanyaan.
StriplingWarrior
39

Saya baru saja menulis fungsi pembantu ini. Masukkan App_Code/JS.cshtml:

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

Kemudian dalam contoh Anda, Anda dapat melakukan sesuatu seperti ini:

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

Perhatikan bagaimana saya tidak menaruh tanda kutip di sekitarnya. Jika judul sudah berisi tanda kutip, itu tidak akan meledak. Tampaknya juga menangani kamus dan benda anonim!

Mpen
sumber
19
Jika Anda mencoba untuk menyandikan objek dalam tampilan tidak perlu membuat kode pembantu, sudah ada. Kami menggunakan ini sepanjang waktu@Html.Raw(Json.Encode(Model))
PJH
2
Bisakah Anda memperluas jawaban Anda PJH? Bagaimana Anda menentukan judul jika Anda hanya menyandikan "Model"?
obesechicken13
2
Juga, ketika saya mencoba pendekatan ini dengan Model.Title saya mendapatkan beberapa kutipan tambahan di sekitar javascript yang disandikan. Saya tidak bisa menghilangkan kutipan bahkan jika saya menggabungkannya dengan sesuatu yang lain. Kutipan itu menjadi bagian dari js Anda.
obesechicken13
1
Komentar PJH luar biasa. Dengan cara Anda deserialize model sisi server ke dalam blok javascript.
netfed
24

Anda mencoba membuat pasak persegi dalam lubang bundar.

Razor dimaksudkan sebagai bahasa templat penghasil HTML. Anda mungkin mendapatkannya untuk menghasilkan kode JavaScript, tetapi itu tidak dirancang untuk itu.

Misalnya: Bagaimana jika Model.Titlemengandung tanda kutip? Itu akan merusak kode JavaScript Anda, dan Razor tidak akan menghindarinya dengan benar.

Mungkin akan lebih tepat untuk menggunakan generator String dalam fungsi pembantu. Kemungkinan akan ada lebih sedikit konsekuensi yang tidak diinginkan dari pendekatan itu.

Adam Lassek
sumber
17

Apa kesalahan spesifik yang Anda lihat?

Sesuatu seperti ini bisa bekerja lebih baik:

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

Perhatikan bahwa Anda memerlukan <text>tag ajaib setelah foreachuntuk menunjukkan bahwa Razor harus beralih ke mode markup.

marcind
sumber
1
iterate Model (by forach) dan bertanda @ Model.Latidue? apa fungsi iterasi? Saya pikir itu melewatkan sesuatu. bisa jadi @ item.Latitude dll
Nuri YILMAZ
12

Itu akan berfungsi dengan baik, selama itu ada di halaman CSHTML dan bukan file JavaScript eksternal.

Mesin template Razor tidak peduli apa itu keluaran dan tidak membedakan antara <script>tag lain.

Namun, Anda perlu menyandikan string Anda untuk mencegah serangan XSS .

Slaks
sumber
2
Saya telah memperbarui pertanyaan saya, Tidak berfungsi untuk saya - ada ide apa yang salah? terima kasih
raklos
3
@raklos: Anda harus melarikan diri. PanggilHTML.Raw(Server.JavaScriptStringEncode(...))
SLaks
1
HTML.Raw(HttpUtility.JavaScriptStringEncode(...))- Properti server tidak memiliki metode ini sekarang. HttpUtility tidak.
it3xl
1
The Razor template engine doesn't care what it's outputting and does not differentiate between <script> or other tags.Apakah Anda yakin itu? stackoverflow.com/questions/33666065/…
HMR
2
@HMR: Fitur itu tidak ada ketika saya menulis jawaban ini.
SLaks
11

Saya lebih suka "<! -" "->" seperti "teks>"

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

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

</script>
Fernando JS
sumber
ini anehnya satu solusi yang bekerja untuk saya, karena teks yang perlu saya sertakan memiliki beberapa pembatas yang Razor tidak suka dengan @:dan <text>metode
BuddyZ
8

Satu hal yang perlu ditambahkan - saya menemukan bahwa hilor sintaks Razor (dan mungkin kompiler) menginterpretasikan posisi braket pembuka berbeda:

<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>
Andy
sumber
6

Contoh sederhana dan mudah:

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

Ini membuat skrip di halaman Anda di lokasi Anda menempatkan kode di atas yang terlihat seperti berikut:

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

Sekarang Anda memiliki variabel JavaScript global bernama razorUserNameyang dapat Anda akses dan gunakan pada klien. Mesin Razor jelas telah mengekstraksi nilai dari @User.Identity.Name(variabel sisi server) dan memasukkannya ke dalam kode yang ditulisnya ke tag skrip Anda.

raddevus
sumber
6

Solusi berikut tampaknya lebih akurat bagi saya daripada menggabungkan JavaScript dengan Razor. Lihat ini: https://github.com/brooklynDev/NGon

Anda dapat menambahkan hampir semua data kompleks ke ViewBag.Ngon dan mengaksesnya dalam JavaScript

Di controller:

public class HomeController : Controller
{
    public ActionResult Index()
    {
        var person = new Person { FirstName = "John", LastName = "Doe", Age = 30 };
        ViewBag.NGon.Person = person;
        return View();
    }
}

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

Ini memungkinkan objek CLR tua biasa (POCOs) yang dapat diserialisasi menggunakan default JavascriptSerializer.

Ice2burn
sumber
5

Ada juga satu opsi lagi dari @: dan <text></text>.

Menggunakan <script>blok itu sendiri.

Saat Anda perlu melakukan JavaScript dalam jumlah besar tergantung pada kode Razor, Anda dapat melakukannya seperti ini:

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

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

Kelebihan dari cara ini adalah bahwa itu tidak mencampur JavaScript dan Razor terlalu banyak, karena mencampurkan mereka banyak akan menyebabkan masalah keterbacaan pada akhirnya. Blok teks besar juga tidak terlalu mudah dibaca.

Tuukka Lindroos
sumber
4

Tidak ada solusi sebelumnya yang berfungsi dengan benar ... Saya telah mencoba semua cara, tetapi tidak memberi saya hasil yang diharapkan ... Akhirnya saya menemukan bahwa ada beberapa kesalahan dalam kode ... Dan kode lengkap diberikan di bawah.

<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>
Atish Dipongkor - MVP
sumber
4

Saya akhirnya menemukan solusinya (* .vbhtml):

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

    /* String */
    var str = '@stringvariable';
    alert(str);
}
SZL
sumber