Olá, tudo bom?

Pode parecer simples, na verdade é muito simples mesmo, mas me perguntaram como fazer isso e resolvi escrever. Estou falando do assunto do título desse post, como usar um ViewBag de dentro de uma tag <script>, afinal, para acessar um ViewBag a partir de uma View usamos Razor. É possível utilizar o Razor em conjunto com o javascript desde que você esteja dentro de uma View (arquivo com extensão .cshtml, .vbhtml) e não de um arquivo .js (isso é bem óbvio, certo?). O razor trabalha no servidor e, nesse caso, o javascript no client, ou seja, quando o javascript "for trabalhar" o ViewBag já vai ter sido "transformado" em texto pelo razor. Vamos ver alguns exemplos para entender isso.

asp

Acessando um ViewBag de dentro de uma tag script

Para os exemplos eu utilizei C# em uma aplicação asp.net MVC 5 com o template Internet Application no Visual Studio 2013 e o navegador Chrome.

Veja o exemplo 1:

//Controller Home

[sourcecode language="csharp"]
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;

namespace MvcApplication1.Controllers
{
public class HomeController : Controller
{
public ActionResult Index()
{
ViewBag.Message = "Modify this template to jump-start your ASP.NET MVC application.";

return View();
}

public ActionResult About()
{
ViewBag.Message = "Your app description page.";

return View();
}

public ActionResult Contact()
{
ViewBag.Message = "Your contact page.";

return View();
}
}
}[/sourcecode]

Perceba que eu não fiz nenhuma alteração no controller Home do template. No método Index podemos ver que existe um ViewBag (ViewBag.Message), vamos utilizá-lo para esse exemplo. Agora, abra a view /Home/Index.

[caption id="attachment_383" align="alignnone" width="646"]View Home/Index Figura 1 - View Home/Index[/caption]

Na Figura 1, podemos ver que também não houve alteração no que foi criado automaticamente. Ao final do html da view /Home/Index vamos adicionar uma tag script com o código para acessar o ViewBag.Message e mostrá-lo em um alert.

//Snippet 1

[sourcecode language="javascript"]
<script>
var texto = @ViewBag.Message
alert(texto);
</script>[/sourcecode]

Se executarmos nossa aplicação agora e abrirmos o Chrome DevTools (tecle F12 no navegador), veremos que houve um erro de sintaxe no javaScript conforme mostra a Figura 2:

[caption id="attachment_388" align="alignnone" width="646"]Erro de sintaxe no JavaScript Figura 2 -Erro de sintaxe no JavaScript[/caption]

Por que o erro? Ainda com o Chrome DevTools aberto, no canto direito da linha onde está sendo descrito o erro é possível clicar no link para verificarmos o local exato onde ocorreu, conforme indicado na Figura 2. Ao clicar seremos redirecionados para a aba source no local onde o erro aconteceu, conforme mostra a Figura 3.

[caption id="attachment_389" align="alignnone" width="646"]Local onde aconteceu o erro de sintaxe. Figura 3 - Local onde aconteceu o erro de sintaxe.[/caption]

Ao analisar a Figura 3, fica fácil entender o porque do erro de sintaxe. O ViewBag.Message está armazenando uma string, entretanto, não estamos colocando-o entre aspas. Para corrigir este erro, basta colocarmos o @ViewBag.Message entre aspas, veja isso no Snippet 2.

//Snippet 2

[sourcecode language="javascript"]
<script>
var texto = "@ViewBag.Message";
alert(texto);
</script>
[/sourcecode]

Na figura 3, podemos analisar também  que não temos mais o "ViewBag.Message", mas somente o seu conteúdo, isso porque antes da view ser enviada para o browser o razor faz sua renderização (transforma tudo em html/texto). Nesse momento, ao executarmos a aplicação teremos o retorno esperado, um alerta com o conteúdo do ViewBag.Message. Veja isso na Figura 4:

[caption id="attachment_391" align="alignnone" width="646"]Retorno esperado do exemplo 1. Figura 4 - Retorno esperado do exemplo 1.[/caption]

Se o ViewBag.Message estivesse guardando um valor numérico ou um valor booleano não seria necessário as aspas.

Vamos ver agora um segundo exemplo, como exibir um ViewBag que está armazenando um objeto json, dentro da tag script.

Acessar um objeto json armazenado dentro de um ViewBag no javaScript

Utilizando a mesma aplicação do exemplo 1. Nesse exemplo vamos instalar um pacote para nos auxiliar com a serialização de objetos para json. Vamos instalar esse pacote utilizando o nuget. No Visual Studio navegue até View >> Other Windows e clique em Package Manager Console.

[caption id="attachment_398" align="aligncenter" width="646"]Abrindo o package manager console Figura 5 - Abrindo o package manager console[/caption]

No console que se abrirá, digite o seguinte comando e espere a mensagem de que a instalação foi concluída.

PM> Install-Package Newtonsoft.Json

Vamos alterar o método Index do controller Home. Substitua o ViewBag.Message pelo ViewBag.obj e atribua a ele um tipo anônimo com as propriedades id, nome e sobrenome conforme mostra o snippet 3. Em seguida, utilizando a biblioteca que acabamos de instalar, vamos serializar o objeto anônimo antes de atribui-lo ao ViewBag.obj, veja como fazer isso no Snippet 4.

//Snippet 3

[sourcecode language="csharp"]
public ActionResult Index()
{
ViewBag.obj = new { id=1, nome = "Wennder", sobrenome = "Santos" };

return View();
}
[/sourcecode]

//Snippet 4

[sourcecode language="csharp"]
public ActionResult Index()
{
ViewBag.obj = Newtonsoft.Json.JsonConvert.SerializeObject(new { id=1, nome = "Wennder", sobrenome = "Santos"});

return View();
}
[/sourcecode]

Não podemos esquecer de alterar também o javaScript que está acessando o ViewBag na View /Home/Index. Devemos trocar o "@ViewBag.Message" por "@ViewBag.obj", veja como ficou após a alteração no Snippet 5.

//Snippet 5

[sourcecode language="javascript"]
<script>
var texto = "@ViewBag.obj";
alert(texto);
</script>
[/sourcecode]

Lembrando que json é string, por isso continuo usando as aspas. Se executarmos a aplicação nesse momento ela funcionará, porém, não da forma esperada. O objeto anônimo será exibido no alert com o formato html encode, conforme mostra a Figura 6:

[caption id="attachment_400" align="aligncenter" width="646"]Alert com o conteúdo do ViewBag.obj no formato html encode Figura 6 - Alert com o conteúdo do ViewBag.obj no formato html encode[/caption]

Isso acontece porque o método Index do controller Home é do tipo ActionResult e usa o método View() para fazer o retorno para quem o "chama", o método View() renderiza e retorna um html. No caso do ViewBag.obj, para não gerar confusão com tag's html alguns caracteres são substituídos por pequeno "códigos". Conseguimos ver isso de outra forma também, através do DevTools. Abra-o teclando F12, navegue até a aba Network e clique na requisição localhost, feito isso clique na aba Response, conforme mostra a Figura 7.

[caption id="attachment_401" align="aligncenter" width="646"]Requisições da página /Home/Index Figura 7 - Requisições da página /Home/Index[/caption]

Após entendido o problema, vamos corrigi-lo. Para isso utilizaremos um html helper, o @html.raw. Conforme diz a documentação, o @html.raw recebe como parâmetro uma string e a retorna sem encoding. É exatamente o que precisamos, certo? Pois bem, vamos alterar o javaScript da view /Home/Index e decodificar o conteúdo do ViewBag.obj. Veja o javaScript alterado no snippet 6.

//Snippet 6

[sourcecode language="javascript"]
<script>
var texto = "@Html.Raw(ViewBag.obj)";
alert(texto);
</script>
[/sourcecode]

Se executarmos a aplicação agora, conseguiremos ver o objeto anônimo criado no controller, entretanto, com um erro sintaxe gerado por conflito de aspas, veja isso na Figura 8:

[caption id="attachment_404" align="aligncenter" width="646"]Erro de sintaxe provado por conflito de aspas Figura 8 - Erro de sintaxe provado por conflito de aspas[/caption]

Resolveremos isso de forma simples, basta trocar as aspas duplas, que "envelopam" o ViewBag.obj, por aspas simples. Veja essa alteração no snippet 7:

//Snippet 7

[sourcecode language="javascript"]
<script>
var texto = '@Html.Raw(ViewBag.obj)';
alert(texto);
</script>
[/sourcecode]

Agora sim temos uma cadeia de caracteres no formato json, se rodarmos a aplicação já teremos um alert mostrando o objeto criado no controller no formato json. Vamos fazer uma ultima alteração para mostrarmos uma propriedade por linha. Para fazer isso, teremos que "falar" para o javaScript que o ViewBag.obj é um objeto, fazemos isso com o JSON.parse. Veja essa alteração no snippet 8.

//Snippet 8

[sourcecode language="javascript"]
<script>
var texto = JSON.parse('@Html.Raw(ViewBag.obj)');
alert("Id:" + texto.id + "\nNome: " + texto.nome + "\nSobrenome: " + texto.sobrenome);
</script>
[/sourcecode]

No alert estou concatenando o nome da propriedade com o seu valor e alguns "n" para quebra de linha. Veja o resultado na Figura 9:

[caption id="attachment_406" align="aligncenter" width="646"]Exibindo valores do objeto anônimo criado no controller Figura 9 - Exibindo valores do objeto anônimo criado no controller[/caption]

Pessoal, uma vez que, a variável texto for declarada em um escopo global ela estará disponível em todos os arquivos .js que estão sendo referenciados pela view.

Espero ter sido claro nos exemplos.

Outro artigo sobre esse assunto é o Convertendo objetos para o padrão json em views do asp.net mvc do mestre Renato Groffe.

Seria legal que qualquer tipo de comentário fosse feito aqui no blog para que todos possam participar.

Até o próximo post.