Skip to content
On this page

Chapter 2 - Your First Component

The Items List

Let's now pretend we have been giving requirements for our app to have a component that displays a list of "items". We will keep this simple initially and as we move towards more advanced chapter expand on it to show how we can better structure our application to support:

  • Quick prototyping and development using mocked data
  • Component Organization
  • Unit Testing
  • State Management
  • Internationalization support so we can render our user interface using different languages
  • Localization for number and date formatting for different cultures

ItemsList Component Requirements

Your initial version of the ItemsList component, will have to implement the following requirements (later, in more advanced chapters, we will expand on these as we get into more advanced topics):

  • The component will display a list of items

  • An item will have 3 properties:

    • id
    • name
    • selected
  • The item name will be displayed to the user

  • The user should be able to select/deselect one or more item

  • An icon will be shown next to the name to indicate if the item is selected

ItemsList Component Code

Within the src/components directory, create a sub-directory called items. Within this folder add a new file called ItemsList.component.vue[1]

Your directory structure will now look like this:

Within the ItemsList.component.vue file, paste the following code:

html

// file: src/components/items/ItemsList.component.vue
<script setup lang="ts">
  // expose a property called items with a default value of a blank array
  defineProps<{ items: any[] }>()
  // explicetely using any[] as we'll replace this with an interface in the next chapters
</script>

<template>
  <div>
    <h3>Items:</h3>
    <ul>
      <li v-for="(item, index) in items"
        :key="item.id">
        {{item.name}}
      </li>
    </ul>
  </div>
</template>

A few things to notice here. First, we specify the lang attribute on the script element with the value ts so we can use TypeScript. We define a property called items using Vue`s defineProps [2]. When you using "script setup" this is how you create properties for your compopnent. If you prefer using defineComponent instead of "script setup" then this does not apply.

Note that items is just using the type any[3] for now (later we'll replace any with an interface we'll create).

For our html template, we added a <h3> element with hard-coded text just saying "Items:". Then a <ul> with a v-for binding that will render all our items within <li> elements.

App.vue

Open the existing App.vue file. Replace the existing code with this:

html
// file: src/App.vue

<script setup lang="ts">
 // TODO ....
</script>
<template>
  <div class="home">
   ... TODO
  </div
</template>

Within <script> the section, import a reference to the ItemsListComponent:

html
// file: src/App.vue

<script setup lang="ts">
  // import a reference to our ItemsList component
  import ItemsListComponent from './components/items/ItemsList.component.vue'
...

For now, also mock some data for our list of items that we will feed to our ItemsListComponent. For this we instantiate a local const called items and initialize it with some hard-coded data[4]. Let;s just add it after the import statement:

html
// file: src/App.vue

<script setup lang="ts">
  // import a reference to our ItemsList component
  import ItemsListComponent from './components/items/ItemsList.component.vue'

  // mock some data:
  const items: any[] = [{ // explicetely using any[] as we'll replace this with an interface in the next chapters
    id: 1,
    name: 'Item 1'
  }, {
    id: 2,
    name: 'Item 2'
  }, {
    id: 3,
    name: 'Item 3'
  }]
</script>
...

Finally, we add an <ItemsListComponent> element within the <template> markup. Insert it within the <div class="home"> element. Add an attribute called items to our <ItemsListComponent>. This is how we pass properties to from parent to child components in Vue[5]. The items data is fed into the component items property this way. The complete code within the App.vue file should now look like this:

html
// file: src/App.vue

<script lang="ts">
  // import a reference to our ItemsList component
  import ItemsListComponent from './components/items/ItemsList.component.vue'

  // mock some data:
  const items: any[] = [{
    id: 1,
    name: 'Item 1'
  }, {
    id: 2,
    name: 'Item 2'
  }, {
    id: 3,
    name: 'Item 3'
  }]
</script>

<template>
  <div class="home">
    <ItemsListComponent :items="items"/>
  </div>
</template>

Save the file. The web browser will refresh and display our preliminary items list being rendered more or less like this:

Note that when you pass properties by reference to other variables or properties you using the coon prefix like in :items="items". If instead you are passing a raw value like an hard-coded string, you would omit the colon. I.e. name="John"

Chapter 2 Recap

What We Learned

  • How to create a basic component that displays a list of items
  • How to consume that component from another component/view

Observations

  • The items property within the ItemsList.component.vue is declared as an array of type any
  • The App.vue view contains hard-coded data (items) which is also declared as an array of any
  • This means we are not leveraging strong-type checking at development time using TypeScript interfaces/models/types

Based on these observations, there are a few improvements that we will make in the next chapters:

Improvements

  • Create a TypeScript interface called ItemInterface for enforcing type checking at development time for our items data
  • Update our code so it uses the new ItemInterface interface

  1. We are following a file naming convention where higher level components' names are pascal-case and follow this format [ComponentName].component.vue (Reference: Naming Conventions section at the end of this book) ↩︎

  2. Vue's script setup and defineProps https://vuejs.org/api/sfc-script-setup.html#defineprops-defineemits ↩︎

  3. With 'any', TypeScript does not enforce type-checking on a property or variable. However, this is considered a bad practice as we lose the main benefit of TypeScript. There might be exceptions to this rule when using older 3rd party packages/libraries/plugins that do not offer type definitions. However, even in those cases it would be strongly recommended to provide interfaces and types so that you can still avoid using 'any'. ↩︎

  4. Note: using hard-coded data is a bad practice and here we are only doing it to first illustrate how things flow, and later in the next chapters will remove in favor of best practices and patterns (see Chapter 5) ↩︎

  5. https://vue.dev/docs#template-syntax-attributes-and-props ↩︎

This is a sample from the book.