Book Image

ASP.NET Core 2 and Vue.js

By : Stuart Ratcliffe
5 (1)
Book Image

ASP.NET Core 2 and Vue.js

5 (1)
By: Stuart Ratcliffe

Overview of this book

This book will walk you through the process of developing an e-commerce application from start to finish, utilizing an ASP.NET Core web API and Vue.js Single-Page Application (SPA) frontend. We will build the application using a featureslice approach, whereby in each chapter we will add the required frontend and backend changes to complete an entire feature. In the early chapters, we’ll keep things fairly simple to get you started, but by the end of the book, you’ll be utilizing some advanced concepts, such as server-side rendering and continuous integration and deployment. You will learn how to set up and configure a modern development environment for building ASP.NET Core web APIs and Vue.js SPA frontends.You will also learn about how ASP.NET Core differs from its predecessors, and how we can utilize those changes to our benefit. Finally, you will learn the fundamentals of building modern frontend applications using Vue.js, as well as some of the more advanced concepts, which can help make you more productive in your own applications in the future.
Table of Contents (15 chapters)

Component presentation

We've now seen how a component can work with and manipulate the data of an application, so let's move on to look at how to actually display that data in the template section of our components.

Directives

Out of the box, Vue provides a number of directives that help us display our data. A directive is a special token that we attach to the HTML element markup in order to instruct Vue to do something special with the DOM element that it renders. Let's look at a simple example to make this a little easier to understand:

<template>
<input v-bind:value="message" />
</template>

<script>
export default {
name: 'data-binding',
data () {
return {
message: 'Hands on Vue.js and ASP.NET Core'
}
}
}
</script>

Here, we use the v-bind directive to bind the value of a standard HTML input element to the message property of the component's data object. In an MVC application, we can do something similar with Razor syntax to bind DOM elements to properties on a view model. We can also use Razor to do things like loop over a list and render the same output for each item, and use conditional rendering and control flow to render different groups of elements depending on runtime model conditions.

The default directives that Vue provides can do all if these things and more, all while using a much cleaner and easier to read syntax!

Attribute binding with v-bind

We've already seen a simple example of v-bind, but there are many other uses for it. We can use v-bind to turn any standard HTML attribute into a reactive version that we can change the underlying data value of to cause the UI to refresh. For example, we can create a very basic image carousel by using v-bind on the src attribute of an HTML image tag, and then rotate through an array of data values to cause the image to refresh:

<template>
<img v-bind:src="imageSrc" alt="image" />
</template>

<script>
export default {
name: 'v-bind',
data () {
return {
imageSrc: '',
images: [
'path/to/image/1.jpg',
'path/to/image/2.jpg',
'path/to/image/3.jpg'
]
}
},
mounted () {
let vm = this
let count = 0

setInterval(function() {
vm.imageSrc = vm.images[count++]
if (count == vm.images.length)
count = 0
}, 1500)
}
}
</script>

Notice how we're using the mounted lifecycle hook that we talked about earlier, which is a perfect example of triggering a UI transition/animation as the component is rendered and displayed in the DOM.

One of the most common usages that we'll see throughout the rest of this book is class bindings to change how an element is styled reactively. We can conditionally add or remove classes based on the component's state, and change that state in response to user interactions such as button clicks. Until now, we've been using the full v-bind:attr syntax, where the section after the : is the specific HTML attribute you wish to data bind. However, Vue also provides a shorthand syntax for a number of its directives. In the case of v-bind, we can simply omit the v-bind part and use :attr instead:

<template>
<div :class="{ 'blue': isBlue }"></div>
</template>

<script>
export default {
name: 'v-bind',
computed: {
isBlue () {
return Math.random(0, 1) > 0.5
}
}
}
</script>

<style>
div {
background: red;
}

div.blue {
background: blue;
}
</style>

Notice how we shortened the directives syntax to :class rather than v-bind:class. This is a very simple example that changes the color of a div element. However, we could use the same techniques to add colored borders and other indicators to show that a piece of the UI has been selected by the user, or that a form input element contains an error after validation.

Conditional display with v-show

Rich UIs usually require us to conditionally show and hide elements based on variables and user interactions. Vue gives us a couple of ways of achieving this with the v-show and v-if directives. We'll talk more about v-if in the next section, but ultimately, both options can be used for controlling the visibility of an element based on the component's state:

<template>
<div v-show="show">
v-show example
</div>
</template>

In the preceding example, we're using v-show to control the visibility of a div element based on a simple Boolean property. This property is toggled by a simple method which is invoked by a button click.

Control flow with v-if and v-else

If...else statements will need no introduction, as we often use them in both our backend C# code and our frontend Razor views. Usage in Vue is no different; we just use v-if and v-else in place of the Razor syntax:

<template>
<div v-if="show">
v-if example
</div>
<div v-else>
v-else example
</div>
</template>

It is also possible to control multiple elements at once with a single v-if statement to prevent duplication. Once rendered, the following template tag will not be included, as it acts as an invisible wrapper:

<template>
<div>
<template v-if="show">
<div class="first"></div>
<div class="second"></div>
<div class="third"></div>
</template>
</div>
</template>

As of version 2.1.0 of Vue, a v-else-if directive has also been added, and it behaves exactly as you'd expect:

<template>
<div>
<div v-if="show">
v-if example
</div>
<div v-else-if="show2">
v-else-if example
</div>
<div v-else>
v-else example
</div>
</div>
</template>

As with a normal if...else statement, it must follow a v-if directive, and similarly the v-else statement must follow either a v-if or v-else-if statement.

There's nothing stopping us from using a v-if directive by itself, without any associated v-else or v-else-if directives, in which case it has a very similar behavior to v-show. At this point, you're probably wondering why we need both if they do the same thing. There is one very big difference between v-if and v-show, and it's important to understand how that difference should influence the decision on which to use in a given scenario.

An element with a v-show directive attached is always rendered into the DOM, and visibility is controlled via the CSS display property. On the other hand, elements controlled by a v-if directive are only rendered into the DOM if the conditional is truthy. This means that visibility is controlled by removing the element from the DOM entirely.

So, which one do we use and why? Generally speaking, v-show is more appropriate for use cases where we know the conditional is going to change frequently. This is because the overhead of toggling the element is much lower, as we have taken that performance hit by always rendering the element, regardless of the conditional. If the condition is unlikely to change at runtime, then we would prefer v-if, as there is a chance that the elements won't ever be rendered, saving us time upfront.

Rendering lists with v-for

Rendering lists of items is a very common requirement of most web applications, so Vue has us covered on this one with the v-for directive. Again, the syntax is very similar to Razor, and we can assign an alias to the current item in the loop. We can then reference the alias to give us access to any or all of its properties:

<template>
<div>
<div v-for="item in items" :key="item">
{{ item }}
</div>
</div>
</template>

It is also possible to access the index of the item within the array by using an optional second argument:

<template>
<div>
<div v-for="(item, index) in items" :key="item">
{{ index }} - {{ item }}
</div>
</div>
</template>

Although we've been using the standard syntax—for example, item in items—it is also possible to use on rather than in. This is closer to the standard JavaScript syntax, so feel free to use this version if you feel more comfortable with it.

Event handling with v-on

Vue gives us a really easy way to attach event handlers to elements using the v-on directive. With standard HTML elements, we can listen for any native DOM event:

<template>
<div>
<button v-on:click="clickHandler()">Click me!</button>
<form v-on:submit="submitHandler()">
<input type="text" />
</form>
</div>
</template>

Here, we are listening for a native click event on a standard HTML button, and invoking the clickHandler method each time it is clicked. We are also listening for the form's submit event to be fired, and intercepting it with the submitHandler method. You've probably noticed that we already saw some button click examples earlier when looking at some of the other directives in Vue. However, we didn't use the v-on syntax like we did here, and instead we used the @ shorthand notation. Here's the same example again using @click and @submit instead:

<template>
<div>
<button @click="clickHandler()">Click me!</button>
<form @submit="submitHandler()">
<input type="text" />
</form>
</div>
</template>

When writing event handler functions in jQuery, we often find ourselves needing to prevent the default event behavior or stopping the event from propagating up through the DOM. In order to do this, we would normally receive the native DOM event as an argument to the event handler function. This is also possible with Vue, as the native event is passed to the handling function, by default, as the only argument:

<template>
<div>
<button @click="clickHandler">Click me!</button>
</div>
</template>
<script>
export default {
name: 'events',
methods: {
clickHandler (event) {
event.preventDefault()
event.stopPropagation()
}
}
}
</script>

If you've written a lot of jQuery in the past, then this will feel right at home for you. However, it's not the only way to achieve the same result, and Vue gives us a much nicer way through the use of modifiers:

<template>
<div>
<button @click.prevent.stop="clickHandler">Click me!</button>
</div>
</template>

It should be fairly self-explanatory as to what's happening here, but adding the .stop modifier is a shorthand way of calling event.stopPropagation(), and adding the .prevent modifier is a shorthand way of calling event.preventDefault(). There are a number of other modifiers available to us, including but not limited to the following:

  • .native: This is used for listening to the native events on the component's root element
  • .{keyCode}: This is used for listening to specific keyboard key presses, for example, @keyup.13
  • .{keyAlias}: This is also used for listening to specific keyboard key presses, for example, @keyup.enter
  • .left, .middle, or .right: This is used for listening to specific mouse button clicks

Notice how we also chained the prevent and stop modifiers in the preceding example. This is perfectly valid in Vue.

Event handling in Vue is incredibly flexible and powerful. We can attach as many v-on directives as we wish, but we can also attach multiple handlers using a single directive by making use of the object syntax:

<template>
<div v-on="{ click: clickHandler, mouseover: hoverHandler }"></div>
</template>

Here, we are listening for both a click and hover event on a single div element. We can add as many properties to this object as we wish, depending on how many events we need to listen for.

Form input binding with v-model

When rendering the form input fields, it might seem obvious to make use of the v-bind directive that we've already looked at. We've already seen that binding an input element's value property can be done as follows:

<template>
<input type="text" :value="value" />
</template>

Upon rendering of the component, you'll find that the textbox is displayed seemingly correctly with the data property prefilled. However, if you tried to change the value of the input by typing into the box, nothing would happen. This is because v-bind is a one-way binding. It is not possible to update the backing property using v-bind, and as such, the input will always display the initial value unless we change that value programmatically via some kind of component method. Following on from the preceding example, this would look like the following:

<template>
<input type="text" :value="value" @input="onInputChange" />
</template>

<script>
export default {
name: "form-input-binding",
data() {
return {
value: "Some value"
};
},
methods: {
onInputChange(event) {
this.value = event.target.value;
}
}
}
</script>

This is quite a lot of code for something as common, and simple, as binding a text input to a data property. Luckily for us, Vue has a much nicer way to create a two-way binding, and this is by using the v-model directive. Under the hood, it does a very similar job to what we've done earlier, but helps us keep our code much leaner and focused on more complicated functionality. It can also be used on all form input elements and text area elements, and automatically uses the correct way of updating the elements' values based on their types:

<template>
<input type="text" v-model="value" />
</template>

By using v-model rather than the combination of v-on and v-bind, we completely negate the need to create a component method just to update the data property. The property is updated automatically for us by the v-model directive. As with the v-on directive, there are some modifiers that we can use to help us out in certain circumstances. These are as follows:

  • .lazy: This is used to sync the input and data properties after a change event rather than an input event
  • .number: This is used to automatically typecast the input value as a number rather than a string
  • .trim: This is used to automatically trim the leading and trailing whitespace from the input value

Parent-child component communication

Earlier in this chapter, we looked at composing parent-child component relationships, and without explicitly saying so, we also covered the basics of how two components in such a relationship can communicate with one another.

Firstly, for a parent component to pass data down to its children, props can be used. However, this is only suitable for a one-way flow of data from parent to child. If we need to send data in the opposite direction, that is, from child to parent, we must use event handling using the v-on directive that we saw earlier. More specifically, we fire events from the child component, and instruct the parent component to listen for those events using the v-on directive. Vue is smart enough to know that we are listening to an event on a custom HTML element, and as such, it is capable of listening for custom events as well as native DOM events:

<template>
<person @name-changed="handleNameChange" />
</template>

In this example, we render a custom person element and listen for a custom name-changed event. In order to trigger this handler, the child component must manually emit the event:

<script>
export default {
name: 'person',
data () {
return {
name: 'Stu Ratcliffe'
}
},
methods: {
save () {
this.$emit('name-changed', this.name)
}
}
}
</script>

This completes our overview of Vue and the main features that we'll be using throughout the rest of this book. Let's move on and take a whistle-stop tour of ASP.NET Core. Again, if you're already familiar with ASP.NET Core or, more specifically, how it differs from previous versions of ASP.NET, you can skip straight ahead to Chapter 2, Setting up the Development Environment, where we'll get stuck straight into setting up our development environment.