DEV Community

Milos Protic
Milos Protic

Posted on • Edited on

How to Integrate Vue with ASP .NET MVC - The synergy between the two

In the world of all growing popularity of front-end development, it seems that the other side, the backend, has been put to the side. This is due to the super useful libraries and frameworks out there, like Vue, React or Angular.

If you ask me, I disagree with the assumption that the backend is less popular or useful to learn than the front-end development. ASP .NET has a lot of great features released in the previous years, and it open-sourced, which was a huge step for Microsoft I believe.

In this post, I will show a way how to integrate Vue with your ASP .NET MVC application, meaning that our server-side view models will be serialized and represented as our Vue app data.

We can achieve this (on a single MVC view) by creating the following:

  1. Vue Data - ASP .NET Attribute
  2. Vue Parser Service
  3. Home View Model (example page)
  4. Home Page - Index.cshtml (example page)
  5. Vue App

Note that I will exclude the need for the page layout (_Layout.cshtml) and base classes since I want to make this post as simple as possible. With the proper architecture, for example, you would have a base view model to hold the data dictionary and the serialization would be done on a layout page or within a helper method.

Vue Data - ASP .NET Attribute

Attributes are a very useful and powerful tool in the ASP .NET world. They can be responsible for a simple task such as an object property validation (is the field required, what is the maximum length allowed...etc.), or they can wrap more complex logic, for example, user authorization.

They can be applied to many different targets, but the most commonly used are:

  1. Class
  2. Property
  3. Method

Our VueData attribute will target only property and will be allowed multiple times on the same class, meaning that we could apply this attribute to as many properties as we want on a specific object.

The attribute will inherit the core Attribute class and it will have only one string property (Name) which will represent the name of the data property within our Vue app.

[AttributeUsage(AttributeTargets.Property, AllowMultiple = true)]
public class VueData : Attribute
{
    public VueData(string name)
    {
        Name = name;
    }

    public string Name { get; }
}

Vue Parser Service

This service will be responsible for parsing the properties with the VueData attribute applied. It will use reflection to loop through all properties of an object, check if the property has the attribute applied and if it does, it will populate a dictionary that will be serialized to the client later on.

// the interface

public interface IVueParser 
{
    Dictionary<string, object> ParseData<TModel>(TModel model);
}

// the implementation

public class VueParser : IVueParser
{
    public Dictionary<string, object> ParseData<TModel>(TModel model)
    {
        var props = model.GetType().GetProperties();
        var result = new Dictionary<string, object>();

        foreach (var prop in props)
        {
            var attr = prop.GetCustomAttributes(typeof(VueData), true)?.FirstOrDefault()
                as VueData;

            if (attr == null)
            {
                continue;
            }

            result.Add(attr.Name, prop.GetValue(model));
        }

        return result;
    }
}

Ok, now when we have our parsing mechanism implemented, it's time to put it in action by creating an example page and view model.

Home View Model (example page)

Earlier in this post, I wrote that we want to define our server-side view model as a source for the Vue application. By creating the above attribute and service we made this possible.

Let's say that we want to have a header with menu items and a body with a welcome message on the view. As MVC pattern applies, we will create a view model object with two properties. After all, a view model is an object representation of a view. The first property will be a type of string and the second will be a type of string list.

And all we need to do in order to make our view model properties accessible to the serialization (will be explained later in this post) is to apply the attribute to it. Something like the following:

public class HomeViewModel
{
    [VueData("message")]
    public string Message { get; set; } = "Hello from Vue!";

    [VueData("menu")]
    public List<string> MenuItems { get; set; } = new List<string>()
    {
        "Menu 1",
        "Menu 2",
    };

    public string RazorMessage { get; set; } =  "Hello from Razor!";

    // in a real app, this would be placed in the base view model class
    public Dictionary<string, object> VueData { get; set; } = new Dictionary<string, object>();
}

In addition, we will add the property called RazorMessage (string type) just to show that we can combine the two ways of data presentation in our view. This property will not be serialized and accessible to the client.

The view model is ready, time to move on to the next step, which is to create a view.

Home Page - Index.cshtml

In our view, we want to use a server-side view model as a data source and a Vue component to present that data to the user. How can we do this?

First, we need to create the actual MVC controller and a Razor view. This is needed in order to navigate to the page in the first place (we are not using Vue router).

The Controller

public class HomeController : Controller
{
    public ActionResult Index()
    {
        // create our view model and parser
        var viewModel = new HomeViewModel();
        var parser = new VueParser(); // in the real app you would use DI

        // in a real app, this would be placed somewhere in the base controller
        viewModel.VueData = parser.ParseData(viewModel);

        return View(viewModel);
    }
}

The View

@model ..MyNamespace/HomeViewModel

@{
    // here we do the serialization of our dictionary into JSON using Newtonsoft.Json package
    // this object will be used in our Vue application

    // over the years of web development, I've found out that the following 
    // serialization solves the problems I've encountered so far

    // in a real app this would be created as a helper method somewhere
    // if we want to exclude the c# code from our view
    var serializationSettings = new JsonSerializerSettings()
    {
        ContractResolver = new CamelCasePropertyNamesContractResolver(),
        StringEscapeHandling = StringEscapeHandling.EscapeHtml
    };   

    var data = 
        Html.Raw(
             HttpUtility.JavaScriptStringEncode(
                JsonConvert.SerializeObject(
                    Model.VueData, Formatting.None, serializationSettings
                ), 
             false)
        );
}

<!DOCTYPE html>
<html>
<head>
    <title>Vue and .NET synergy</title>
</head>
<body>
    // Here we can combine the Razor with Vue without problems

    <main id="vue-app">
        <header-component v-bind:menu-items="menu"></header-component>
        <div>@Model.RazorMessage</div>
        <div>{{message}}</div>
    </main>

    <script src="https://cdn.jsdelivr.net/npm/vue"></script>
    <script src="...my-path/header-component.js"></script>
    // place for the script from the next section here
</body>
</html>

The following script should be placed before the closing of a body tag:

new Vue({
    el: '#vue-app',
    data: function() {
        // parse the serialized data
        return JSON.parse('@data');
    }
});

The header-component.js source code:

// header-component.js
// in a real app you might use TypeScript or ECMAScript
// in a real app this would be a single file component probably

Vue.component('header-component', {
    props: ['menuItems'], // pass in the menu from the app
    template: '<header><ul><li v-for="item in menuItems">{{ item  }}</li</ul></header>'
});

If you run the page the output should look like this:

  • Menu 1
  • Menu 2

Hello from Razor!
Hello from Vue!

Conclusion

Note that the same approach can be used to create a connection with the Vue props, only in this case the parsing is a little bit different since Vue props could be defined in several ways.

The solution here would be to create a VuePropertyDescriptor class and use it as a value for each Vue prop. This class would reflect the Vue prop interface. It would contain properties like type, default, required...etc. But this is a topic for another post.

Sometimes going with the full front-end app is not an option. In those moments implementations like the one from this post can come in handy. With the proper architecture and separation of concerns, this could be very powerful and it can increase the speed of development.

For more up to date articles, be sure to check out devinduct.com

Thank you for reading and see you in the next post!

Top comments (15)

Collapse
 
whitebookmark profile image
Rein

Thank you for writing this tutorial. However I still do not understand how will you make a production build like this ? If you use webpack to build Vue.js then it would appear in a separate file which means razor syntax cannot be integrate like that. I also noticed you aren't using SFC with .vue extensions, is that not possible ?

Collapse
 
proticm profile image
Milos Protic

Sorry for my far late reply :D I did not use Webpack, I used Rollup to build the scripts (but it should not make any difference), and it worked quite nicely. I have it fully operating with SFC components and Razor views where I use them. I cannot show you the project because it's a private repository

Collapse
 
drewtownchi profile image
Drew Town

This is interesting and looks like a nice approach for sprinkling in Vue to a server rendered asp.net project. I love asp.net core but have only ever done an API project with Vue so it is nice to see some alternatives like this.

Collapse
 
proticm profile image
Milos Protic • Edited

Thanks. I've built a lot of applications using this approach with knockout.

Collapse
 
scriptedpixels profile image
👨🏾‍💻 Kam Banwait

Have you a guide or steps on how to replace knockout with Vue?

I’m new to DotNet & have the task of replacing knockout with Vue but not in a SPA way, just in a way of replacing certain areas of the app with Vue components

Thread Thread
 
proticm profile image
Milos Protic

I do not have a guide, but it should be straight forward, especially if you are not required to create Vue components. It is the easiest switch you could get (Knockout -> React or Knockout to Angular would be harder). I think that your question is too broad to be answered in the comment, especially if you are new to the mentioned tech stack

Collapse
 
janpauldahlke profile image
jan paul

Nice man. Can you write about the same approach in Java JSP and react?

Collapse
 
proticm profile image
Milos Protic

Thanks. I don't have enough experience with Java JSP, but if I do it the post will be published on dev.to

Collapse
 
janpauldahlke profile image
jan paul

i see. but nonetheless a nice article :-) I enjoyed reading it.

Thread Thread
 
proticm profile image
Milos Protic

Thank you very much.

Collapse
 
scottsea profile image
ScottSEA

This is great for half the pipeline - have you found a good solution for getting the Vue data back into the MVC View Model?

Collapse
 
proticm profile image
Milos Protic

Thanks.

I usually invoke a POST request with the Vue data formatted as JSON. MVC binder will do the rest and map the incoming object to the view model, but of course, you need to be careful and name the Vue data properties the same as the view model has them named.

Collapse
 
garsidestephen profile image
SG Digital

Thanks for the informative post - just what I was looking for!

Collapse
 
proticm profile image
Milos Protic

No problem 👍

Collapse
 
shivp profile image
shivp • Edited

Nice article! However is it possible to use this structure with .vue files? In a large project with over 100 views using js files for vue components would be difficult to scale