Verwenden von Razor in JavaScript

434

Ist es möglich oder gibt es eine Problemumgehung, um die Razor-Syntax in JavaScript zu verwenden, das sich in einer Ansicht befindet (cshtml )?

Ich versuche, einer Google-Karte Markierungen hinzuzufügen ... Ich habe dies beispielsweise versucht, erhalte jedoch eine Menge Kompilierungsfehler:

<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
quelle
3
Vielleicht interessiert Sie mein Update zur @:Syntax.
StriplingWarrior
@jemand, der dies in Betracht zieht, fügt die Daten zumindest in ein separates Skript-Tag ein, das einfach einen JSON definiert, der dann im JS verwendet wird. Back-End-gekoppeltes JS am Frontend war für mich mehrfach ein Legacy-PITA. Schreiben Sie keinen Code mit Code, wenn Sie nicht müssen. Geben Sie stattdessen Daten weiter.
Erik Reppen

Antworten:

636

Verwenden Sie das hier<text> beschriebene Pseudoelement , um den Razor-Compiler wieder in den Inhaltsmodus zu versetzen:

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

Aktualisieren:

Scott Guthrie hat kürzlich über die @:Syntax in Razor geschrieben, die etwas weniger klobig ist als das <text>Tag, wenn Sie nur ein oder zwei Zeilen JavaScript-Code hinzufügen müssen. Der folgende Ansatz wäre wahrscheinlich vorzuziehen, da er die Größe des generierten HTML-Codes verringert. (Sie können die Funktion addMarker sogar in eine statische, zwischengespeicherte JavaScript-Datei verschieben, um die Größe weiter zu verringern.)

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

Der obige Code wurde aktualisiert, um den Aufruf addMarkerkorrekter zu gestalten.

Zur Verdeutlichung @:zwingt Razor den Textmodus zurück, obwohl der addMarkerAufruf dem C # -Code sehr ähnlich sieht. Razor greift dann auf die @item.PropertySyntax zurück, um zu sagen, dass der Inhalt dieser Eigenschaften direkt ausgegeben werden soll.

Update 2

Es ist erwähnenswert, dass View-Code wirklich kein guter Ort ist, um JavaScript-Code einzufügen. JavaScript-Code sollte in eine statische .jsDatei eingefügt werden und dann die benötigten Daten entweder aus einem Ajax-Aufruf oder durch Scannen abrufendata- Attributen aus dem HTML- . Dies ermöglicht nicht nur das Zwischenspeichern Ihres JavaScript-Codes, sondern vermeidet auch Probleme mit der Codierung, da Razor für die Codierung von HTML, nicht jedoch von JavaScript ausgelegt ist.

Code anzeigen

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

JavaScript-Code

$('[data-marker]').each(function() {
    var markerData = $(this).data('marker');
    addMarker(markerData.Latitude, markerData.Longitude,
              markerData.Description, markerData.Title);
});
StriplingWarrior
quelle
3
Ich verstehe Ihr aktualisiertes Beispiel nicht. Ist die Addmarker-Funktion korrekt?
NVM
2
@NVM: Anstatt denselben Javascript-Code mehrmals auszugeben, empfehle ich, eine einzelne Javascript-Funktion (die in einer zwischengespeicherten .js-Datei gespeichert werden kann) zu erstellen und mehrere Aufrufe dieser Funktion auszugeben. Ich habe keine Ahnung, ob die Funktion korrekt ist: Ich habe sie nur auf den Code des OP gestützt.
StriplingWarrior
1
Warum das '@ Model.Latitude' in foreach. Warum nicht item.Latitude?
NVM
5
Ihre C # -Variablen müssen maskiert werden. Wenn @item.Titleein einfaches Anführungszeichen enthalten ist, explodiert dieser Code.
Mpen
7
@ Mark: Gute Beobachtung. In der Tat, ich kombinieren typischerweise kein Javascript und Razor überhaupt in meinem eigenen Code: Ich ziehe es Razor zu verwenden , um zu generieren HTML mit data-Attributen, und dann statisch, unaufdringliche Javascript verwenden diese Informationen aus dem DOM aufzulesen. Aber diese ganze Diskussion ging irgendwie über den Rahmen der Frage hinaus.
StriplingWarrior
39

Ich habe gerade diese Hilfsfunktion geschrieben. Setzen Sie es ein App_Code/JS.cshtml:

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

In Ihrem Beispiel können Sie dann Folgendes tun:

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

Beachten Sie, dass ich keine Anführungszeichen darum setze. Wenn der Titel bereits Anführungszeichen enthält, wird er nicht explodieren. Scheint auch gut mit Wörterbüchern und anonymen Objekten umzugehen!

mpen
quelle
19
Wenn Sie versuchen, ein Objekt in der Ansicht zu codieren, muss kein Hilfecode erstellt werden. Es ist bereits einer vorhanden. Wir benutzen dies die ganze Zeit@Html.Raw(Json.Encode(Model))
PJH
2
Können Sie Ihre Antwort PJH erweitern? Wie geben Sie den Titel an, wenn Sie nur "Modell" codieren?
obesechicken13
2
Als ich diesen Ansatz mit Model.Title ausprobierte, erhielt ich einige zusätzliche Anführungszeichen für das codierte Javascript. Ich kann die Zitate nicht loswerden, selbst wenn ich sie mit etwas anderem verkette. Diese Zitate werden Teil Ihres js.
obesechicken13
1
Der Kommentar von PJH ist hervorragend. In gewisser Weise deserialisieren Sie serverseitige Modelle in den Javascript-Block.
Netfed
24

Sie versuchen, einen quadratischen Stift in ein rundes Loch zu stecken.

Razor war als HTML-generierende Vorlagensprache gedacht. Sie können es sehr gut dazu bringen, JavaScript-Code zu generieren, aber es wurde nicht dafür entwickelt.

Zum Beispiel: Was wäre wenn Model.Title ein Apostroph enthalten ist? Das würde Ihren JavaScript-Code beschädigen und Razor wird ihn standardmäßig nicht korrekt umgehen.

Es wäre wahrscheinlich besser, einen String-Generator in einer Hilfsfunktion zu verwenden. Dieser Ansatz wird wahrscheinlich weniger unbeabsichtigte Folgen haben.

Adam Lassek
quelle
17

Welche spezifischen Fehler sehen Sie?

So etwas könnte besser funktionieren:

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

Beachten Sie, dass Sie das magische <text>Tag nach dem benötigen, foreachum anzuzeigen, dass Razor in den Markup-Modus wechseln soll.

Marcind
quelle
1
Modell iterieren (von foreach) und mit @ Model.Latidue markiert? Was ist die Funktion der Iteration? Ich denke, das hat etwas verpasst. es könnte @ item.Latitude etc sein
Nuri YILMAZ
12

Das wird gut funktionieren, solange es sich auf einer CSHTML-Seite und nicht in einer externen JavaScript-Datei befindet.

Die Razor Template Engine kümmert sich nicht um die Ausgabe und unterscheidet nicht zwischen <script> oder anderen Tags.

Sie müssen jedoch Ihre Zeichenfolgen codieren, um XSS- Angriffe zu verhindern .

SLaks
quelle
2
Ich habe meine Frage aktualisiert. Es funktioniert bei mir nicht. Irgendwelche Ideen, was ist falsch? danke
Raklos
3
@raklos: Du musst deinen Saiten entkommen. Rufen Sie anHTML.Raw(Server.JavaScriptStringEncode(...))
SLaks
1
HTML.Raw(HttpUtility.JavaScriptStringEncode(...))- Die Servereigenschaft hat diese Methode jetzt nicht. HttpUtility tut es.
it3xl
1
The Razor template engine doesn't care what it's outputting and does not differentiate between <script> or other tags.Bist du dir darüber sicher? stackoverflow.com/questions/33666065/…
HMR
2
@HMR: Diese Funktion gab es nicht, als ich diese Antwort schrieb.
SLaks
11

Ich bevorzuge "<! -" "->" wie ein "Text>"

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

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

</script>
Fernando JS
quelle
Dies ist seltsamerweise die einzige Lösung, die für mich funktioniert hat, da der Text, den ich @:<text>
einfügen
8

Eine Sache hinzuzufügen - ich fand, dass Razor Syntax Hilighter (und wahrscheinlich der Compiler) die Position der öffnenden Klammer unterschiedlich interpretieren:

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

Ein einfaches und gutes Beispiel:

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

Dadurch wird ein Skript auf Ihrer Seite an der Stelle erstellt, an der Sie den Code platzieren, der wie folgt aussieht:

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

Jetzt haben Sie eine globale JavaScript-Variable mit dem Namen, auf razorUserNamedie Sie zugreifen und die Sie auf dem Client verwenden können. Die Razor-Engine hat den Wert offensichtlich aus @User.Identity.Name(serverseitige Variable) extrahiert und in den Code eingefügt, den sie in Ihr Skript-Tag schreibt.

Raddevus
quelle
6

Die folgende Lösung scheint mir genauer zu sein, als JavaScript mit Razor zu kombinieren. Überprüfen Sie dies: https://github.com/brooklynDev/NGon

Sie können ViewBag.Ngon fast alle komplexen Daten hinzufügen und in JavaScript darauf zugreifen

In der Steuerung:

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>

Es erlaubt alle einfachen alten CLR-Objekte (POCOs), die mit der Standardeinstellung serialisiert werden können JavascriptSerializer.

Ice2burn
quelle
5

Es gibt auch eine Option mehr als @: und <text></text>.

Mit <script>Block selbst.

Wenn Sie abhängig vom Razor-Code große Teile von JavaScript ausführen müssen, können Sie dies folgendermaßen tun:

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

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

Vorteile dieser Art sind, dass JavaScript und Razor nicht zu stark gemischt werden, da ein häufiges Mischen letztendlich zu Problemen mit der Lesbarkeit führt. Auch große Textblöcke sind nicht sehr gut lesbar.

Tuukka Lindroos
quelle
4

Keine der vorherigen Lösungen funktioniert korrekt ... Ich habe alle Möglichkeiten ausprobiert, aber es hat mir nicht das erwartete Ergebnis gebracht ... Endlich habe ich festgestellt, dass der Code einige Fehler enthält ... Und der vollständige Code ist angegeben unten.

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

Ich habe endlich die Lösung gefunden (* .vbhtml):

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

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