Reutilizando o @model do Razor com VueJs
Tags: vue, dotnet, csharp
Published: 2020-05-05 15:06:15


## **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`**
```csharp
public class Person
{
    public int Id { get; set; }
    public string Name { get; set; }
    public int Age { get; set; }
}
```

### **`PersonController.cs`**
```csharp
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`**
```html
@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`**
```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:

```javascript
//...
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`**
```csharp
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
<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`**
```javascript
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`**
```csharp
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`.