Vue

Create an Application with Web Template Studio

Creating new applications is something I do a fair amount of. Most of the time they are throwaway projects used to test something out or demo projects used for this blog. With all the project creation going on I try and keep an eye out for tools that make the process easier. This post is going to cover a tool a came across a few weeks ago from Microsoft call Web Template Studio (WebTS).

What is WebTS?

From the project’s GitHub read me:

Microsoft Web Template Studio (WebTS) is a Visual Studio Code Extension that accelerates the creation of new web applications using a wizard-based experience. WebTS enables developers to generate boilerplate code for a web application by choosing between different front-end frameworks, back-end frameworks, pages and cloud services. The resulting web app is well-formed, readable code that incorporates cloud services on Azure while implementing proven patterns and best practices.

For the front-end, we have options of Angular, React, or Vue and on the back-end, there are options for ASP.NET Core, Flask, Molecular, or Node.

Installation

We are assuming you already have a recent version of Visual Studio Code (VSCode) installed, but if not you can download it from here. Open VSCode and from the left side of the screen select Extensions. In the search box at the top enter Web Template Studio and then from the list select Web Template Studio. In the detail page that opens click the green Install button at the top.

Project Creation

Now that we have the extension installed press Ctrl + Shift + P to open the VSCode command palette and enter Web Template Studio: Launch (or as much as needed for the option to show) and then select Web Template Studio: Launch.

This will launch the Web Template Studio project creation process. The first page that shows gives you a very basic set of options, but enough for our example. To see the full array of options Web Template Studio provides hit next on this first screen instead of Create Project like we do in this example. Also, note that if you want an ASP.NET [Core] back-end you need to select it on this summary screen as it isn’t yet an option later in the process. Back to our example, on this page, you will need to enter a Project Name, pick a Front-end Framework, and a Back-end Framework before finally clicking Create Project.

After the process finishes a dialog will show letting us know it is complete. Click Open Project to load the created project in a new instance of VSCode.

Running the Project

Before getting to a runnable state we need to run a couple of terminal commands first. This can be done from VSCode’s built-in terminal or from an external terminal of your choice. In VSCode if you don’t see a terminal you can use Cntrl + Shift + ` to open a new one, or from the menu select Terminal > New Terminal. The following are the two commands that need to be run using npm, but they can be adjusted to use yarn instead if you prefer it. The second command may be specific to an ASP.NET Core back-end, make sure and check the project README.md for verification.

npm install
npm run restore-packages

Now the project can be run with the following command.

npm start

The resulting application will look something like this.

Wrapping Up

WebTS seems to be a great tool for quickly getting a project up and running. I do recommend going through the full project creation wizard as it has options to set up Azure integration as well as the ability to add up to 20 different pages to the generated application. Also, keep in mind that the ASP.NET back-end framework option is pretty new so I wouldn’t be surprised to see some changes as it progresses through the preview stage. Make sure and check out the project’s GitHub repo.

Using NSwag to Generate a Vue Client for an ASP.NET Core 3.1 API

This week we are going to add a Vue project that will utilize the contacts API we created a few weeks ago using a client-generated by NSwag. This post is part of the revamp of the ASP.NET Core Basics repo that was kicked off when .NET Core 3.0 was released which is now targeting .NET Core 3.1. For details on how the associated samples got to their current point check out the following posts.

Swagger/OpenAPI with NSwag and ASP.NET Core 3
ASP.NET Core 3: Add Entity Framework Core to Existing Project
New Razor Pages Project Backed with an API
Using NSwag to Generate Angular Client for an ASP.NET Core 3 API
Using NSwag to Generate React Client for an ASP.NET Core 3 API
Using NSwag to Generate Blazor Server Client for an ASP.NET Core 3.1 API
Using NSwag to Generate a Vue Client for an ASP.NET Core 3.1 API

The sample code before any changes from this post can be found here.

Create the Vue Project

Unlike the rest of the projects in this series, there is no .NET CLI template from Microsoft that has Vue support so to crate the Vue project we will be using the Vue CLI. Before getting started ensure you have npm installed.

Install the Vue CLI using the following command from a command prompt.

npm install -g @vue/cli

Next, use the following command to start the project creation process using the Vue CLI. Keep in mind that the CLI creates a directory with the project name.

vue create contacts-vue

The above command kicks off a series of questions about the application. This sample is going to use TypeScript which means that the default can’t be used so we need to select Manually select features.

For the next question, we need to select TypeScript. I also included the Router and Linter / Formatter. I also found out later in the process that Babel was needed so feel free to select it on this question.

The project creation process asked a bunch more questions that I basically took the defaults on. Here is a screenshot of all the questions and the options I used.

Now that the project is created if we need to change directories to the one created in the above process.

cd contacts-vue

Use the following command to run the new project.

npm run serve

Use NSwagStudio to Generate an API Client

NSwag provides multiple options for client generation including a CLI, code, or a Windows application. This post is going to use the Windows application which is called NSwagStudio. NSwagStudio can be downloaded and installed from here.

Next, make sure your API is running and get the URL of its OpenAPI/Swagger specification URL. For example, using a local instance of the sample solution’s Contacts API the URL is https://localhost:5001/swagger/v1/swagger.json. If you are using the Swagger UI you can find a link to your swagger.json under the API title.

Now that we have the OpenAPI/Swager specification URL for the API switch over to NSwagStudio. The application will open with a new document ready to go. There are a few options we will need to set. First, select the OpenAPI/Swagger Specification tab and enter your API’s specification URL in the Specification URL box.

In the Outputs section check the TypeScript Client checkbox and then select the TypeScript Client tab. There are a lot of options to play with, the highlighted options are the ones that are important for this sample. First, make sure Module name and Namespace are both empty. I’m sure there is a way to get the client working with a module or namespace, but I didn’t have any luck.   For Template, we just need a Fetch based client. The final option that needs to be set is the Output file path and this is the location you want the generated file to be. I output to the Vue project directory under /src/apis/contactApi.ts. After all the options are set click Generate Files.

Create UI and Use Generated Client

Note that I haven’t touch Vue in a long time so the actually UI bits may or may not be the “proper” way to do this stuff in Vue, but it should be understandable enough that you can see how the API client is used. As with the other post in this same vein, we are going to create a contact list that gets its data from an API.

First, we are going to create a new component for the contact list in the /src/component directory with the filename of ContactList.vue with the following contents. The lines specific to the usage of the NSwag generated client are highlighted.

<template>
  <div>
    <table class="table table-striped" aria-labelledby="tabelLabel">
      <thead>
        <tr>
          <th>Name</th>
          <th>Address</th>
          <th>City</th>
          <th>State</th>
          <th>Postal Code</th>
          <th>Phone</th>
          <th>Email</th>
        </tr>
      </thead>
      <tbody>
        <tr v-for="contact in contacts" v-bind:key="contact.id">
          <td>{{contact.name}}</td>
          <td>{{contact.address}}</td>
          <td>{{contact.city}}</td>
          <td>{{contact.state}}</td>
          <td>{{contact.postalCode}}</td>
          <td>{{contact.phone}}</td>
          <td>{{contact.email}}</td>
        </tr>
      </tbody>
    </table>
  </div>
</template>

<script lang="ts">
import { Component, Prop, Vue } from 'vue-property-decorator'
import { ContactsClient, Contact } from '../apis/contactsApi'
@Component
export default class HelloWorld extends Vue {
  name: string = 'ContactList';
  contacts: Contact[] = [];
  private created () {
    let client = new ContactsClient()
    client.getContacts().then(data => (this.contacts = data))
  }
}
</script>

<style scoped>
</style>

As you can see from the created function above we are creating a new instance of the ContactsClient and calling its getContacts function and using the data we get back from the API to replace the contacts field with the results of the API call.

Next, we are going to create a ContactList.vue under the /src/views directory with the following code. This is basically a wrapper around the component we created above.

<template>
  <div>
    <ContactListComponent/>
  </div>
</template>

<script>
import ContactListComponent from '@/components/ContacList'
export default {
  name: 'contactList',
  components: {
    ContactListComponent
  }
}
</script>

Now that we have our view ready we need to add a link to the application’s navigation so a user can get to the contact list. Open App.vue and a router link to the nav div for the contact list.

<div id="nav">
    <router-link to="/">Home</router-link> |
    <router-link to="/contactList">Contacts</router-link> |
    <router-link to="/about">About</router-link>
</div>

Now we need to add the new component to the routes for the application. Open index.ts in the /src/router directory and add an import for the contact list view.

import ContactList from '../views/ContactList.vue'

Finally, add the contact list to the routes array.

const routes = [
  {
    path: '/',
    name: 'home',
    component: Home
  },
  {
    path: '/contactList',
    name: 'contactList',
    component: ContactList
  },
  {
    path: '/about',
    name: 'about',
    // route level code-splitting
    // this generates a separate chunk (about.[hash].js) for this route
    // which is lazy-loaded when the route is visited.
    component: () => import(/* webpackChunkName: "about" */ '../views/About.vue')
  }
]

Wrapping Up

As always NSwag makes it very easy to create a client to interact with an API. Hopefully, this was useful even if my Vue code might not be idiomatic.

The sample projects after all the changes in this post can be found here.

Vue: Contact Detail

Last week’s post covered adding a Vue project to the ASP.NET Core Basics example solution. This post is going to cover adding a read-only view of a contact’s detail to being the Vue sample a step closer to being in line with the Angular, Aurelia and React samples. The code before any changes can be found here.

Contact Class

The Contact interface in the contactlist.ts file should be deleted. Add a contact.ts file to the ClientApp/components/contacts/ directory. This will hold the replacement for the interface we just deleted. The full class definition follows.

export class Contact {
    id: number;
    name: string;
    address: string;
    city: string;
    state: string;
    postalCode: string;
    phone: string;
    email: string;

    constructor(data?: any) {
        if (data == null) return;
        (<any>Object).assign(this, data);
    }

    getAddress(): string {
        return `${this.address} ${this.city}, ${this.state} ${this.postalCode}`;
    }

}

This class is very simple and will be used later to show how a function call can be used as part of a view.

Contact Service

Since the application will now have two places that need to access the ASP.NET Core API lets to refactor the API access into a service. This is the same style used by the Aurelia, Angular and React applications. Add a contactService.ts file to the contacts directory. The service will provide functions to get all contacts or a single contact an ID. The following is the full class.

import { Contact } from './contact';

export class ContactService {
    private baseUrl = 'http://localhost:13322/api/contactsApi/';

    getAll(): Promise<Contact[]> {
        return fetch(this.baseUrl)
            .then(response => response.json() as Promise<Contact[]>)
            .then(contacts => Array.from(contacts, c => new Contact(c)));
    }

    getById(id: string): Promise<Contact> {
        return fetch(`${this.baseUrl}${id}`)
            .then(response => response.json())
            .then(contact => new Contact(contact));
    }
}

Now the API access in the ContactListComponent can be refactored to use the service. First, add imports for contact and service.

import { Contact } from './contact';
import { ContactService } from './contactService';

Next, replace the fetch call with a service call.

Before:
fetch('http://localhost:13322/api/contactsApi/')
    .then(response => response.json() as Promise<Contact[]>)

After:
let contactService = new ContactService();
contactService.getAll()

Contact Detail Component

Add a contactDetail.ts which will be the backing component for the contact detail view. The following is the full class. Pretty much all it is doing is using get contact service to get contact detail by ID when it is mounted.

import Vue from 'vue';
import { Component, Prop } from 'vue-property-decorator';
import { Contact } from './contact';
import { ContactService } from './contactService';

@Component
export default class ContactDetailComponent extends Vue {
    contact: Contact = new Contact();
    @Prop() id: string;

    mounted() {
        let contactService = new ContactService();
        contactService.getById(this.id)
            .then(data => {
                this.contact = data;
            });
    }
}

Now add a contactDetail.vue.html file for the UI bits of the contact detail. This is basically just HTML with the same binding syntax as before. Notice that the binding of address is a function call. The other Vue things to note are that the details will only show if contact is truthy using the v-if.

<template>
    <div>
        <h1>Contact Details</h1>
        <hr />
        <div v-if="contact">
            <dl class="dl-horizontal">
                <dt>ID</dt>
                <dd>{{ contact.id }}</dd>
                <dt>Name</dt>
                <dd>{{ contact.name }}</dd>
                <dt>Address</dt>
                <dd>{{ contact.getAddress() }}</dd>
                <dt>Phone</dt>
                <dd>{{ contact.phone }}</dd>
                <dt>Email</dt>
                <dd>{{ contact.email }}</dd>
            </dl>
        </div>
        <router-link to="/contactlist">Back to List</router-link>
        <hr />
    </div>
</template>

<script src="./contactDetail.ts"></script>

Routing with a Parameter

Finding the proper way to route with at parameter in Vue was not easy. The way it is done has evolved over time making find the right way a little challenging. The first step is to add a route for the contact detail by adding a new item to the routes array in the boot.ts file.

{ path: '/contactdetail/:id', component: require('./components/contacts/contactDetail.vue.html'), props: true }

The :id in the path denotes a parameter. The props: true bit is also key to getting the ID when in the contact detail component. The contactlist.vue.html needs to be updated to link to the contact detail route.

Before:
<td>{{ contact.id }}</td>

After:
<td>
  <router-link :to="'/contactdetail/' +  contact.id">{{ contact.id }}
  </router-link>
</td>

Finally, in the ContactDetailComponent we need to use the ID. To do so we need an import to allow the use of the Prop decorator.

import { Component, Prop } from 'vue-property-decorator';

Now all that is needed is to apply the decorator to a property of the class that matches the name of the parameter in the route.

@Prop() id: string;

Now the ID can be used to get the proper contact from the contact service. The following is code that was already shown, but the full context of how the prop decorator is used is important so I am repeating it here.

@Component
export default class ContactDetailComponent extends Vue {
    contact: Contact = new Contact();
    @Prop() id: string;

    mounted() {
        let contactService = new ContactService();
        contactService.getById(this.id)
            .then(data => {
                this.contact = data;
            });
    }
}

Wrapping Up

This brings the projects one step closer to being on the same level feature-wise. Look for one more post to get the Vue project features lined up with the other samples in the solution.

Keep in mind that this is my first look at Vue and my examples may or may not be idiomatic.

The code in a finished state can be found here.

ASP.NET Core Basics: Vue with an API

In the past, I have done some exploration on AureliaAngular 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.