Utilizzo di Razor in JavaScript


435

È possibile o esiste una soluzione alternativa per utilizzare la sintassi Razor in JavaScript che è in una vista ( cshtml)?

Sto cercando di aggiungere marcatori a una mappa di Google ... Ad esempio, ho provato questo, ma sto ricevendo un sacco di errori di compilazione:

<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
potresti essere interessato al mio aggiornamento per quanto riguarda la @:sintassi.
StriplingWarrior,

@anyone sta considerando di farlo in questo modo, almeno metti i dati in un tag script separato che definisce semplicemente alcuni JSON che vengono quindi utilizzati in JS. Il JS accoppiato back-end sul front-end è stato un PITA legacy per me in diverse occasioni. Non scrivere codice con codice se non è necessario. Consegnare invece i dati.
Erik Reppen,

Risposte:


637

Usa lo <text>pseudo-elemento, come descritto qui , per forzare il compilatore Razor nella modalità contenuto:

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

Aggiornare:

Scott Guthrie ha recentemente pubblicato un @:post sulla sintassi in Razor, che è leggermente meno goffo del <text>tag se hai solo una o due righe di codice JavaScript da aggiungere. Il seguente approccio sarebbe probabilmente preferibile, perché riduce la dimensione dell'HTML generato. (È anche possibile spostare la funzione addMarker in un file JavaScript statico memorizzato nella cache per ridurne ulteriormente le dimensioni):

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

Aggiornato il codice sopra per rendere la chiamata addMarkerpiù corretta.

Per chiarire, le @:forze Razor ritornano in modalità testo, anche se addMarkercall assomiglia molto al codice C #. Razor quindi rileva la @item.Propertysintassi per dire che dovrebbe produrre direttamente il contenuto di tali proprietà.

Aggiornamento 2

Vale la pena notare che il codice Visualizza non è davvero un buon posto per inserire il codice JavaScript. Il codice JavaScript deve essere inserito in un .jsfile statico e quindi dovrebbe ottenere i dati necessari da una chiamata Ajax o tramite la scansione degli data-attributi dall'HTML. Oltre a rendere possibile la memorizzazione nella cache del codice JavaScript, questo evita anche problemi con la codifica, poiché Razor è progettato per codificare per HTML, ma non JavaScript.

Visualizza codice

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

Codice JavaScript

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

3
Non capisco il tuo esempio aggiornato. La funzione addmarker è corretta?
NVM

2
@NVM: Invece di inviare più volte lo stesso codice javascript, suggerisco di creare una singola funzione javascript (che può essere conservata in un file .js nella cache) e di inviare diverse chiamate a quella funzione. Non ho idea se la funzione sia corretta: la basavo solo sul codice dell'OP.
StriplingWarrior,

1
Perché '@ Model.Latitude' in foreach. Perché non item.Latitude?
NVM

5
Le variabili C # devono essere salvate. Se @item.Titlecontiene una sola citazione questo codice esploderà.
Aprire il

7
@Mark: buona osservazione. In realtà, io di solito non combino javascript e Razor affatto nel mio codice: io preferisco usare il rasoio per generare HTML con data-gli attributi, e quindi utilizzare statica, javascript discreto per raccogliere queste informazioni dal DOM. Ma l'intera discussione andava oltre il campo di applicazione della domanda.
StriplingWarrior,

39

Ho appena scritto questa funzione di supporto. Mettilo in App_Code/JS.cshtml:

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

Quindi, nel tuo esempio, puoi fare qualcosa del genere:

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

Nota come non inserisco citazioni. Se il titolo contiene già virgolette, non esploderà. Sembra gestire anche dizionari e oggetti anonimi!


19
Se stai cercando di codificare un oggetto nella vista non è necessario creare un codice helper, ne esiste già uno. Lo usiamo sempre@Html.Raw(Json.Encode(Model))
PJH

2
Puoi espandere la tua risposta PJH? Come si specifica il titolo se si sta semplicemente codificando "Modello"?
obesechicken13

2
Inoltre, quando ho provato questo approccio con Model.Title ho delle citazioni extra attorno al javascript codificato. Non riesco a liberarmi delle citazioni anche se le concateno a qualcos'altro. Quelle citazioni diventano parte del tuo js.
obesechicken13

1
Il commento di PJH è eccezionale. In un certo senso si deserializzano i modelli lato server nel blocco javascript.
Netfed

24

Stai cercando di bloccare un piolo quadrato in un foro rotondo.

Razor era inteso come un linguaggio modello che genera HTML. Potresti benissimo farlo generare codice JavaScript, ma non è stato progettato per quello.

Ad esempio: cosa succede se Model.Title contiene un apostrofo? Ciò romperebbe il tuo codice JavaScript e Razor non lo sfuggirà correttamente per impostazione predefinita.

Probabilmente sarebbe più appropriato usare un generatore di stringhe in una funzione di aiuto. Probabilmente ci saranno meno conseguenze indesiderate di tale approccio.


17

Quali errori specifici stai vedendo?

Qualcosa del genere potrebbe funzionare meglio:

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

Nota che è necessario il <text>tag magico dopo il foreachper indicare che Razor dovrebbe passare alla modalità markup.


1
iterare Model (per foreach) e contrassegnato @ Model.Latidue? qual è la funzione dell'iterazione? penso che abbia perso qualcosa. potrebbe essere @ item.Latitude ecc.
Nuri YILMAZ,

12

Funzionerà perfettamente, purché si trovi in ​​una pagina CSHTML e non in un file JavaScript esterno.

Il motore del modello Razor non si preoccupa di ciò che sta producendo e non differenzia tra <script>o altri tag.

Tuttavia, è necessario codificare le stringhe per prevenire attacchi XSS .


2
Ho aggiornato la mia domanda, per me non funziona: qualsiasi idea è sbagliata? grazie
raklos il

3
@raklos: devi sfuggire alle tue corde. ChiamaHTML.Raw(Server.JavaScriptStringEncode(...))
SLaks il

1
HTML.Raw(HttpUtility.JavaScriptStringEncode(...))- La proprietà del server non ha questo metodo ora. HttpUtility lo fa.
it3xl

1
The Razor template engine doesn't care what it's outputting and does not differentiate between <script> or other tags.Ne sei sicuro? stackoverflow.com/questions/33666065/…
HMR

2
@HMR: Quella funzione non esisteva quando ho scritto questa risposta.
SLaks,

11

Preferisco "<! -" "->" come un "testo>"

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

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

</script>

questa è stranamente l'unica soluzione che ha funzionato per me, perché il testo che dovevo includere aveva alcuni delimitatori che a Razor non piacevano con i metodi @:e<text>
BuddyZ

8

Una cosa da aggiungere: ho scoperto che il silenziatore di sintassi di Razor (e probabilmente il compilatore) interpreta la posizione della parentesi di apertura in modo diverso:

<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

Un semplice e un buon esempio diretto:

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

Questo crea uno script nella tua pagina nella posizione in cui inserisci il codice che assomiglia al seguente:

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

Ora hai una variabile JavaScript globale denominata a razorUserNamecui puoi accedere e utilizzare sul client. Il motore Razor ha ovviamente estratto il valore da @User.Identity.Name(variabile lato server) e lo ha inserito nel codice che scrive nel tag dello script.


6

La seguente soluzione mi sembra più accurata rispetto alla combinazione di JavaScript con Razor. Dai un'occhiata a: https://github.com/brooklynDev/NGon

Puoi aggiungere quasi tutti i dati complessi a ViewBag.Ngon e accedervi in JavaScript

Nel controller:

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

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

Permette qualsiasi oggetto CLR (POCO) semplice che può essere serializzato usando il valore predefinito JavascriptSerializer.


5

C'è anche un'opzione in più rispetto a @: e <text></text>.

Utilizzando il <script>blocco stesso.

Quando devi eseguire grandi blocchi di JavaScript a seconda del codice Razor, puoi farlo in questo modo:

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

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

I pro di questo modo è che non mescola troppo JavaScript e Razor, perché mescolarli molto causerà alla fine problemi di leggibilità. Anche i blocchi di testo di grandi dimensioni non sono neanche molto leggibili.


4

Nessuna delle soluzioni precedenti funziona correttamente ... Ho provato tutti i modi, ma non mi ha dato il risultato atteso ... Alla fine ho scoperto che ci sono alcuni errori nel codice ... E viene fornito il codice completo sotto.

<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

Ho finalmente trovato la soluzione (* .vbhtml):

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

    /* String */
    var str = '@stringvariable';
    alert(str);
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.