Templating

Setup

I’ve created a git repo with the base application. Inside that repo, I’ve set up a page with the start of a session list for the CodeMash conference. The data is currently hard-coded, giving three sample sessions to show how they should look. Our job is to convert that hard-coded data into a vue template that reads data from the API.

I recommend you type in the code rather than copy-paste. Copying might be faster, but the act of personally entering each line of code helps you build muscle memory and reinforces connections between concepts.

First, we’ll get the code up and running. Execute the following commands:

  1. git clone https://github.com/akatakritos/codemash-schedule-vue.git- this gets the source code from GitHub
  1. cd codemash-schedule-vue - change the directory into the code we just downloaded
  1. git checkout 1-templating to switch to the first exercise branch
  1. npm install - install all the libraries needed to build and run the application
  1. npm run dev- start the application

You should now be able to open the site in your web browser. You can copy-paste the link from your terminal or press o, then Enter to have Vite open it.

Data overview

In the application, we have a list of conference sessions. Each “card” shows:

This data is served from a fake API hosted in the codebase. The public directory contains an api folder with many .json files that comprise the data. The full session list driving this page is in public/api/sessions/index.json. The starter kit includes some code to load and log it to the console. We’ll work more closely with the API in a later module.

Open src/components/SessionList.vue in your editor. Notice that the list of sessions is loaded and stored into a variable called sessions. We’ll use that variable in our templates to display real data.

Updating the Count

Let’s start with an easy one. The application will eventually support filters, but for now, there is a line that says, “Showing 3 of 3 talks.” Once we have filters, this would show how many sessions matched the user’s current search, for example, “Showing 17 of 212 talks”. Let's update this so that it is driven from the real session count instead of a hardcoded 3.

In src/components/SessionList.vue find the line of code that outputs the text “Showing 3 of 3 talks”. We want to replace the 3’s with the current length of the sessions variable.

Make the following changes:

  <!-- we're going to add filters in a later module that will update these counts -->
- <p>Showing 3 of 3 talks</p>
+ <p>Showing {{ sessions.length }} of {{ sessions.length }} talks</p>

Save your changes and switch back to your browser. It should now say “Showing 211 of 211 talks”.

Congratulations, you’re officially a Vue programmer!

Implement the List

Right now the screen is showing three hard-coded sessions at three different skill levels. The first thing we can do is get it start showing all 211 talks. Vue uses the v-for directive to control repetition of a template across each item in a list.
Find the markup for the first card in the list, after the
<!-- introductory and overview --> comment. Apply the v-for and :key bindings as follows.

  <!-- introductory and overview -->
- <div class="card">
+ <div class="card" v-for="session in sessions" :key="session.id">
  <div class="card-body">

After saving and switching back to the browser you should see that you have the first card duplicated 211 times! Progress! We still have to update the template to read the title and excerpt and other data from the real list.

Remember to include the :key attribute when using v-for. This is an important performance optimization that helps Vue efficiently modify a list of elements when the data changes.

Making the cards show real data

We’ll start with updating the template to output the text values.

Open public/api/sessions/index.json to see the properties that are available on each session. Think about which spot in the UI corresponds to each property. For this card we’ll need

Since the parent div for the card uses v-for="session in sessions, it’s child elements will have access to the session variable which is the current item in the loop. This is very similar to how a foreach or for loop works in C# and Java.

We can read properties from that variable and output them to the screen, for example {{ session.title }} will show the title on the screen.

Implementing the template

For every piece of hard-coded data, replace the text with the corresponding property. For example replace Introductory and overview with {{ session.level }}. Don’t worry about getting the background color on the skill level badge right, we’ll tackle that next.

Note that speakers is an array of strings because a conference talk can have multiple speakers. We need to convert that array into a single, comma separated string. Luckily javascript has a method on Array to do that: join. Using session.speakers.join(', ') we can convert the list of speaker names into a comma-separated string. Vue will let us call it from the template inside the double curly braces just like any other javascript expression. No magic here!

The full set of changes so far should look like this:



         <div class="card-body">
-          <span class="badge bg-success">Introductory and overview</span>
-          <h5 class="card-title">Build a Modern Single Page Application with Vue</h5>
-          <h6 class="card-subtitle mb-2 text-body-secondary">Matt Burke</h6>
-          <p class="card-text">
-            Vue offers developers a way to build ambitious front-end applications with powerful reactive programming
-            patterns and an intuitive HTML-based templating language. This workshop will give you a jumping-off point
-            for large front-end applications using Vue with blazing-fast dev tools like esbuild,...
-          </p>
+          <span class="badge bg-success">{{ session.level }}</span>
+          <h5 class="card-title">{{  session.title }}</h5>
+          <h6 class="card-subtitle mb-2 text-body-secondary">{{ session.speakers.join(', ') }}</h6>
+          <p class="card-text">{{ session.excerpt }}</p>
           <div class="footer pt-2">
-            <span> Tuesday 8:00 - 12:00</span>
+            <span> {{ session.day }} {{ session.startTime }} - {{ session.endTime }}</span>
             <a href="" class="btn btn-primary">Details</a>
           </div>
         </div>

Using the class binding

If you look at it now, we have all the data, but the color of the level is not changing depending on if its Advanced, Intermediate, or Introductory and overview.

Lets look more closely at the final two hard-coded sample talks that are now all the way at the bottom. Carefully considering them, we can see that there is a difference in the class attribute on the span showing the skill level text:


In short, each has the
badge class and a different bg-* class depending on level. We could tackle this with v-if and v-else-if, but a better way to do dynamic CSS classes is to use Vue’s :class binding. This binding can accept an object and any property on that object that evaluates to true will apply the property name as a css class.

In other words, we can produce an object like this and Vue will know to apply the right bg-* class to the element.

{
  'bg-success': session.level === 'Introductory and overview',
  'bg-warning': session.level === 'Intermediate',
  'bg-danger': session.level === 'Advanced'
}
Javascript properties can have symbols (like -) in them if they are wrapped in quotes.
{
  'bg-success': true,
  'bg-warning': false,
  'bg-danger': false
}

If the object evaluated to something like this, any property with a true value will be turned into an addition to the element’s class attribute.

The badge class is static and should be there always, but we can also add a dynamic class binding through :class

  <div class="card-body">
-   <span class="badge bg-success">Introductory and overview</span>
+   <span class="badge" :class="{
+     'bg-success': session.level === 'Introductory and overview',
+     'bg-warning': session.level === 'Intermediate',
+     'bg-danger': session.level === 'Advanced'
+    }">{{ session.level }}</span>
  <h5 class="card-title">{{  session.title }}</h5>

Now we can delete the remaining two examples from the original markup

Conditionally show the empty message

The last piece of dynamic content is a message that is supposed to show when there are no talks matching the user’s search. In the template, it’s currently commented out with an HTML comment (<!— —>). We need to uncomment it, and then use the v-if binding to only render it when there are no sessions

    <!-- show this if there are no sessions -->
-   <!--<p>No sessions found</p>-->
+   <p v-if="sessions.length === 0">No sessions found</p>
  </div>

If you open your browser now, you’d still not see it, because we don’t have filters hooked up yet, and there are 211 talks to show. If you want to see it, comment out the assignment to session.value in the onMounted hook near the top of the file:

// sessions.value = data

Recap

Further Reading

Bonus