DEV Community

Cover image for Reutilizando o @model do Razor com VueJs
Marcelo Machado
Marcelo Machado

Posted on

Reutilizando o @model do Razor com VueJs

O problema

Para quem trabalha com .Net, é muito comum renderizar suas páginas server-side utilizando o Razor. E, por muito tempo, tivemos o jQuery como aliado quando se era necessário fazer alguma chamada AJAX ou manipular algum DOM. Esta era a stack padrão.

Com a adoção do VueJS, nossa vida melhorou muito. Porém, sempre que quisermos representar o objeto renderizado pelo Razor no projeto Vue, precisamos reescrever todo o objeto usando a sintaxe Js.

Neste post, pretendo mostra um pouco de como eu faço para que os objetos enviados do controller para a view estejam também disponíveis no código Vue de forma automática.

Vamos imaginar que tenhamos a seguinte estrutura:

Person.cs

public class Person
{
    public int Id { get; set; }
    public string Name { get; set; }
    public int Age { get; set; }
}

PersonController.cs

public class PersonController : Controller
{
    public IActionResult Form()
        {
            var person = new Person();
            return View(person);
        }
}

Os inputs do formulário já fazem o bind com os atributos do objeto Vue que vamos criar mais adiante.

Form.cshtml

@model Person

<form asp-route="Save" method="post">
    <input asp-for="Id" v-model="Model.Id"  />
    <input asp-for="Name" v-model="Model.Name"  />
    <input asp-for="Age" v-model="Model.Age"  />
</form>

Para que possamos trabalhar com a classe Person no projeto Vue, e também para que o bind acima funcione, é necessário rescrever a classe inteira dentro do Vue:

main.js

import Vue from 'vue'
import App from './App.vue'

Vue.config.productionTip = false

new Vue({
  data() {
    return {
      Model: {
        Id: 0,
        Name: '',
        Age: 0
      }
    }
  },
  render: h => h(App),
}).$mount('#app')

Obviamente esta é uma abordagem bem simplista, mas ainda assim, seria necessário rescrever toda a classe Person de alguma forma, para poder ter acesso como no exemplo abaixo:

//...
methods: {
  changeName () {
    this.Model.Name = 'New Name'
  }
}
//...

Preparando as coisas

Precisamos trabalhar com 3 alterações para que as coisas funcionem de forma automática. Primeiro, criaremos um método que transporte o objeto a ser renderizado para um formato JSON. Aqui, vou assumir que você estará criando uma classe BaseController para poder utilizar este método em todos os seus controllers.

BaseController.cs

public class BaseController : Controller
{
    protected JsonSerializerSettings jsonSettings;

    public BaseController()
    {
        this.jsonSettings = new JsonSerializerSettings
        {
            Culture = new CultureInfo("pt-BR"),
            DateFormatString = "dd/MM/yyyy",
            ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
            Formatting = Formatting.Indented
        };
    }

    public ViewResult ViewJson(object model, string view = "")
    {
        // Transformamos o objeto em Json e enviamos para a view usando ViewBags
        ViewBag.Model = JsonConvert.SerializeObject(model, this.jsonSettings);

        // Depois a view é renderizada normalmente
        if (string.IsNullOrWhiteSpace(view))
            return View(model);
        else
            return View(view, model);
    }
}

Agora com o objeto serializado, vamos adicioná-lo em uma variável JavaScript global chamada model. Esta variável será utilizada posteriormente em nosso projeto Vue.

Faremos isso no arquivo de Layout, uma vez que esta solução será usada por todo o sistema.

_Layout.cshtml

<html>
    <head>
        <script>
            @if (string.IsNullOrWhiteSpace(ViewBag.Model))
            {
                <text>const model = null;</text>
            }
            else
            {
                <text>const model = @Html.Raw(ViewBag.Model);</text>
            }
        </script>
    </head>
    ...
</html>

E por último, precisamos vamos tornar a variável global model acessível a partir do root do Vue.

Main.js

import Vue from 'vue'
import App from './App.vue'

Vue.config.productionTip = false

new Vue({
  data() {
    return {
      Model: model
    }
  },
  render: h => h(App),
}).$mount('#app')

// Sempre adiciono esta linha para deixar o Vue acessível pelo console do navegador
window.vue = vue;

Aplicando as modificações

Agora, basta alterar os controllers, trocando a chamada do método View() para ViewJson().

PersonController.cs

public class PersonController : Controller
{
    public IActionResult Form()
        {
            var person = new Person();
            //Única alteração necessária nos controllers
            return ViewJson(person);
        }
}

Conclusão

Agora, toda e qualquer action retornará além da página Razor renderizada em server-side, você também terá a sua disposição um objeto Vue representando a classe atribuída em @model.

Top comments (0)