In the past, I have done some exploration on Aurelia, Angular and React via the ASP.NET Core Basics series. This post is going to take a similar approach using Vue. The code for the project will be in the same repo as the previous basics examples and will be utilizing the same API to pull data. The code before adding the Vue project can be found here.
Project Creation
There is not a template for Vue built into Visual Studio, but there is a set of templates that can be used via the .NET CLI with the dotnet new that includes Vue (as well as Aurelia and Knockout). A full list of available templates can be found here. The template we need is Microsoft.AspNetCore.SpaTemplates and can be installed using the following command from a command prompt.
dotnet new -i "Microsoft.AspNetCore.SpaTemplates::*"
Create a Vue folder at the same level as the other projects, for the samples this would be in the src folder. In the command prompt navigate to the src/Vue/ directory. Run the following command to create the Vue project.
dotnet new vue
After the project generation completes run the following to get the needed npm packages installed.
npm install
The last step is to get the new project added to the existing solution. Navigate the command prompt to your solution file. For the sample project, this is the root of the repo. Then run the following command (this could also be done through Visual Studio instead of using the CLI).
dotnet sln add src/Vue/Vue.csproj
Adding the Contact List
In the ClientApp/components directory add a new contacts directory to house all the awesome contact related functionality we will be adding. Next, add a new contactlist.ts. To this new file add the interface which defines what a Contact looks like.
interface Contact { id: number; name: string; address: string; city: string; state: string; postalCode: string; phone: string; email: string; }
Above the contact interface, add the following imports needed to create a Vue component.
import Vue from 'vue'; import { Component } from 'vue-property-decorator';
Finally, add the contact list component. This component maintains a list of contacts which gets filled from our existing API when the component is mounted (see the docs for more information on Vue’s lifecycle hooks).
@Component export default class ContactListComponent extends Vue { contacts: Contact[] = []; mounted() { fetch('http://localhost:13322/api/contactsApi/') .then(response => response.json() as Promise<Contact[]>) .then(data => { this.contacts = data; }); } }
The following if the full component just to provide full context.
import Vue from 'vue'; import { Component } from 'vue-property-decorator'; @Component export default class ContactListComponent extends Vue { contacts: Contact[] = []; mounted() { fetch('http://localhost:13322/api/contactsApi/') .then(response => response.json() as Promise<Contact[]>) .then(data => { this.contacts = data; }); } } interface Contact { id: number; name: string; address: string; city: string; state: string; postalCode: string; phone: string; email: string; }
Next up is the UI for this contact list component. In the same directory add contactlist.vue.html. The following is the full file.
<template> <div> <h1>Contact List</h1> <table v-if="contacts.length" class="table"> <thead> <tr> <th>ID</th> <th>Name</th> </tr> </thead> <tbody> <tr v-for="contact in contacts"> <td>{{ contact.id }}</td> <td>{{ contact.name }}</td> </tr> </tbody> </table> <p v-else><em>Loading...</em></p> </div> </template> <script src="./contactlist.ts"></script>
All of the above is pretty straightforward. All the Vue specific items have a v- prefix.
Add the Contact List to Navigation
The last bit needed to have a working contact list is adding it to navigation so the user can get to the list. The routes are defined in the boot.ts file in the routes array. Add the following line to the array to handle our contact list.
{ path: '/contactlist', component: require('./components/contacts/contactlist.vue.html') }
Now that the router knows to handle the contact list it needs to be added to the navigation UI. Open the /navmenu/navmenu.vue.html file and find the unordered list that is the navigation menu. Add a new list item to provide a link to the contact list.
<li> <router-link to="/contactlist"> <span class="glyphicon glyphicon-list-alt"></span> Contact List </router-link> </li>
Wrapping Up
Vue reminds me a lot of Aurelia in its simplicity so far which is awesome. Look for the same progression of posts for Vue that happened with React over the last few weeks.
The code in its final state can be found here.
Also published on Medium.
Nice to see you giving Vue a try Eric!
One Q: why do you use the ASP.NET Core SPA templates, and not use the Vue CLI instead (and keep the SPA and API as two separate projects)?
It was fun to give it a shot!
As for the use of the ASP.NET Core templates, the API is hosted in a different project that is part of the same solution. For the client, there are a few reasons I have stuck with the SPA templates. The first is it matches with the other “Basics” examples I have done in the past. The second is it was simple and build in way to get started quickly without having to learn each framework’s CLI. Third thinking about hosting I was looking for a way to host a single application but still have a pretty good separation and with the ASP.NET Core project hosting the API and containing the client code (this is not how the “Basics” examples are set up, but is why I went done the SPA templates path, to begin with.
I would love to hear your thoughts on the subject.
My main concern was that there should be a separation between the SPA and the API, as those really are 2 separate applications. But you commented that this is the case (and I also noticed after initially commenting when I browsed the GH repository)
Another thing is that the CLIs have a lot of power, and by not using them you are crippling yourself a bit by having to do a lot of things manually which the CLI would otherwise have done for you.
As for hosting, you can perhaps look into something like Netlify (https://www.netlify.com/) for hosting the SPA. They even have prerendering support for SPAs (https://www.netlify.com/docs/prerendering/) if you require that. But besides that, their global CDN is probably going to deliver your SPA much more efficiently than hosting it in an ASP.NET project on Azure (or wherever else) can give you.
BTW, I am publising a blog post tomorrow on Netlify. Not specifically about hosting SPAs, but you may still find it useful.
I wonder how much of the JS framework CLIs could be used from within the ASP.NET Core SPA templates. Could be something to look into.
I haven’t looked into anything like netlify yet I look forward to reading your blog to get a good introduction.
Speaking of hosting in general, is Azure the best place to host ASP.NET Core applications? I’m hoping one day have something that needs to be hosted and would love to have an idea of some of the options.
I tried using the Angular CLI at some point with the SPA templates, but was not successful because of the vastly different folder structures. I know the team is however working on aligining the two:
https://github.com/aspnet/JavaScriptServices/issues/1288
Not sure if Azure is the best… but it probably is the easiest way to deploy an ASP.NET app ? Everything else just seem to require a lot of manual labour…
Good to know Microsoft sees the CLIs as important and is making changes to support them.
I really hope the options for hosing start to improve over time. I tried to google for ASP.NET Core hosting and the results weren’t awesome.
Hi Guys
Using Angular CLI alongside the dotnet CLI is certainly possible. I put the pieces together earlier myself (main issue was making ng CLI output to /wwwroot instead of /dist) but looks like the team is improving this out of the box:
https://www.hanselman.com/blog/ASPNETSinglePageApplicationsAngularReleaseCandidate.aspx
As to hosting for my money I’ll stick with Azure for overall developer productivity. You can also leverage their CDN along with a SPA deployed from blob storage as simple static assets. Likely there is some server side goodness lost, but the price is right.
Mark
Great info Mark thank you!
Thanks for such a great info shared here.