Usando o Razor no JavaScript

435

É possível ou existe uma solução alternativa para usar a sintaxe Razor no JavaScript que está em uma view ( cshtml)?

Estou tentando adicionar marcadores a um mapa do Google ... Por exemplo, tentei isso, mas estou recebendo muitos erros de compilação:

<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
fonte
3
você pode estar interessado na minha atualização sobre @:sintaxe.
StriplingWarrior
@ qualquer um que pensa em fazê-lo dessa maneira, pelo menos coloque os dados em uma tag de script separada que simplesmente define algum JSON que é usado no JS. O JS acoplado ao backend no front end tem sido um PITA herdado para mim em várias ocasiões. Não escreva código com código se não for necessário. Distribua os dados.
precisa saber é o seguinte

Respostas:

637

Use o <text>pseudo-elemento, conforme descrito aqui , para forçar o compilador Razor de volta ao modo de conteúdo:

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

Atualizar:

Scott Guthrie postou recentemente sobre a @:sintaxe no Razor, que é um pouco menos desajeitada que a <text>tag, se você tiver apenas uma ou duas linhas de código JavaScript para adicionar. A abordagem a seguir provavelmente seria preferível, porque reduz o tamanho do HTML gerado. (Você pode até mover a função addMarker para um arquivo JavaScript estático em cache para reduzir ainda mais o tamanho):

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

Atualizado o código acima para tornar a chamada addMarkermais correta.

Para esclarecer, as @:forças Razor voltam ao modo de texto, mesmo que a addMarkerchamada pareça muito com o código C #. O Razor pega a @item.Propertysintaxe para dizer que deve exibir diretamente o conteúdo dessas propriedades.

Atualização 2

Vale a pena notar que o código View realmente não é um bom lugar para colocar código JavaScript. O código JavaScript deve ser colocado em um .jsarquivo estático e, em seguida, ele deve obter os dados necessários a partir de uma chamada Ajax ou varrendo data-atributos do HTML. Além de tornar possível o cache do seu código JavaScript, isso também evita problemas com a codificação, já que o Razor foi projetado para codificar para HTML, mas não para JavaScript.

Ver Código

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

Código JavaScript

$('[data-marker]').each(function() {
    var markerData = $(this).data('marker');
    addMarker(markerData.Latitude, markerData.Longitude,
              markerData.Description, markerData.Title);
});
StriplingWarrior
fonte
3
Não entendo seu exemplo atualizado. A função addmarker está correta?
NVM
2
@NVM: Em vez de emitir o mesmo código javascript várias vezes, sugiro criar uma única função javascript (que pode ser mantida em um arquivo .js em cache) e gerar várias chamadas para essa função. Não tenho idéia se a função está correta: eu estava apenas baseando-a no código do OP.
StriplingWarrior
1
Por que o '@ Model.Latitude' no foreach. Por que não item.Latitude?
NVM
5
Suas variáveis ​​C # precisam ser escapadas. Se @item.Titlecontiver uma citação única, esse código explodirá.
MPEN
7
@ Mark: Boa observação. De fato, normalmente não combino javascript e Razor no meu próprio código: prefiro usar o Razor para gerar HTML com data-atributos e, em seguida, usar javascript estático e discreto para coletar essas informações do DOM. Mas toda essa discussão estava além do escopo da questão.
precisa saber é o seguinte
39

Acabei de escrever esta função auxiliar. Coloque-o em App_Code/JS.cshtml:

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

Então, no seu exemplo, você pode fazer algo assim:

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

Observe como não coloco aspas. Se o título já contiver aspas, ele não explodirá. Parece lidar com dicionários e objetos anônimos também!

mpen
fonte
19
Se você estiver tentando codificar um objeto na visualização, não há necessidade de criar um código auxiliar, ele já existe. Usamos isso o tempo todo@Html.Raw(Json.Encode(Model))
PJH
2
Você pode expandir sua resposta PJH? Como você especifica o título se está apenas codificando "Modelo"?
precisa saber é o seguinte
2
Além disso, quando tentei essa abordagem com Model.Title, recebo algumas citações extras em torno do javascript codificado. Não consigo me livrar das aspas, mesmo se concatenar para outra coisa. Essas citações se tornam parte dos seus js.
precisa saber é o seguinte
1
O comentário de PJH é excelente. De certa forma, você desserializa modelos do lado do servidor no bloco javascript.
Netfed
24

Você está tentando enfiar uma estaca quadrada em um buraco redondo.

O Razor foi concebido como uma linguagem de modelo geradora de HTML. Você pode muito bem conseguir gerar código JavaScript, mas não foi projetado para isso.

Por exemplo: E se Model.Titlecontiver um apóstrofo? Isso quebraria o código JavaScript e o Razor não escapará corretamente por padrão.

Provavelmente seria mais apropriado usar um gerador de String em uma função auxiliar. Provavelmente haverá menos consequências não intencionais dessa abordagem.

Adam Lassek
fonte
17

Quais erros específicos você está vendo?

Algo assim poderia funcionar melhor:

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

Observe que você precisa da <text>tag mágica após a foreachpara indicar que o Razor deve mudar para o modo de marcação.

marcind
fonte
1
itere o Model (por foreach) e marque @ Model.Latidue? qual é a função da iteração? Eu acho que perdeu alguma coisa. poderia ser @ item.Latitude etc
Nuri YILMAZ
12

Isso funcionará bem, desde que esteja em uma página CSHTML e não em um arquivo JavaScript externo.

O mecanismo de modelo do Razor não se importa com o que está sendo produzido e não diferencia entre <script>ou outras tags.

No entanto, você precisa codificar suas seqüências para evitar ataques XSS .

SLaks
fonte
2
Eu atualizei minha pergunta. Isso não funciona para mim. Quaisquer idéias que estão erradas? graças
raklos
3
@raklos: Você precisa escapar das cordas. ChamadaHTML.Raw(Server.JavaScriptStringEncode(...))
SLaks
1
HTML.Raw(HttpUtility.JavaScriptStringEncode(...))- A propriedade do servidor não possui esse método agora. HttpUtility faz.
It3xl 08/07/2015
1
The Razor template engine doesn't care what it's outputting and does not differentiate between <script> or other tags.Você tem certeza disso? stackoverflow.com/questions/33666065/…
HMR
2
@HMR: Esse recurso não existia quando escrevi esta resposta.
SLaks
11

Eu prefiro "<! -" "->" como um "texto>"

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

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

</script>
Fernando JS
fonte
essa é estranhamente a única solução que funcionou para mim, porque o texto que eu precisava incluir tinha alguns delimitadores dos quais o Razor não gostava nos métodos @:e<text>
BuddyZ
8

Uma coisa a acrescentar - descobri que a sintaxe do Razor hilighter (e provavelmente o compilador) interpreta a posição do suporte de abertura de maneira diferente:

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

Um exemplo simples e direto:

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

Isso cria um script em sua página no local em que você coloca o código acima, que se parece com o seguinte:

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

Agora você tem uma variável JavaScript global denominada, a razorUserNamequal pode acessar e usar no cliente. O mecanismo Razor obviamente extraiu o valor de @User.Identity.Name(variável do lado do servidor) e o colocou no código que escreve na sua tag de script.

raddevus
fonte
6

A solução a seguir me parece mais precisa do que combinar o JavaScript com o Razor. Verifique isso: https://github.com/brooklynDev/NGon

Você pode adicionar quase todos os dados complexos ao ViewBag.Ngon e acessá-los em JavaScript

No controlador:

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

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

Ele permite que qualquer objeto CLR antigo simples (POCOs) possa ser serializado usando o padrão JavascriptSerializer.

Ice2burn
fonte
5

Há também mais uma opção que @: e <text></text>.

Usando o <script>próprio bloco.

Quando você precisa criar grandes pedaços de JavaScript, dependendo do código do Razor, pode fazê-lo assim:

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

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

Prós dessa maneira é que ele não mistura muito JavaScript e Razor, porque misturá-los bastante causará problemas de legibilidade eventualmente. Também grandes blocos de texto também não são muito legíveis.

Tuukka Lindroos
fonte
4

Nenhuma das soluções anteriores funciona corretamente ... Eu tentei de todas as formas, mas não me deu o resultado esperado ... Finalmente, descobri que existem alguns erros no código ... E o código completo é fornecido abaixo.

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

Finalmente encontrei a solução (* .vbhtml):

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

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