The Vue CLI

18. Why Use the Vue CLI?

The previous tutorials taught us how to create a vue app in a single HTML element.  There, Vue could control only that HTML element.

The Vue CLI will allow us to modify the entire web site, use modern JavaScipt code, creates a live dev server, and allows us to optimize our code.

19. How to Use the Vue CLI?

Must install node.js, namely npm, to install the Vue CLI.

To open the terminal in VSC type ctrl+`

After installing node, execute the node -v in the terminal.  If installation is successful, you should see a version number.

Type the following to install the Vue CLI (command line interface) globally on the computer.

$ npm install -g @vue/cli

In your terminal, change your working directory to your projects directory.

Now create a Vue project using the following command.

$ vue create app2

Arrow down and choose Manually select features.  Turn the Linter off and accept the remaining default values.

After the script creates the project, do not run npm run serve.

Change directory to the directory that the CLI created.

$ cd app2

20. New Project Walkthrough

The CLI created index.html with a div having an id of app.  This is where Vue will dynamically inject templates.

The src folder is where we will put our code.  It already has some files and directories in it.

> assets
> components
    HelloWorld.vue
App.vue
main.js

The main.js file contains the following.

import {createApp} from 'vue'
import App from './App.vue'
createApp(App).mount('#app')

This kickstarts the application.  createApp is imported from the vue library in the node modules.

App.vue contains a template, script, and style.  The script exports an object.  We import that object as App.

Then in main.js, we create a Vue app using the App object and mount the app as the root component in the div that has an id named app in public/index.html.

Inside package.json are scripts.  One is named serve.  During development, if we want to test our app, we can run the serve script to start a web server on localhost to view and debug the app on localhost.

$ npm run serve

If we want to host our code on a shared-hosting server like Godaddy we do not need to run the serve script.  Instead we need to build a distribution of the app using the following command. The script will create a new directory named dist which will contain the code which can be loaded on the shared hosting server.

Before we run the build script however, we need to make one change.  The build script assumes that we will upload the distribution code to the root folder of our server (public_html).  This is likely not where you want to instal the app.

If you plan on accessing the app from somewhere other than public_html folder we need to change configure vue.config.js.   Edit your vue.config.js file so that it looks like the following:

module.exports = defineConfig({
    transpileDependencies: true,
    publicPath: "./"
})

Now run the build script.

$ npm run build

The build script creates a directory named dist which contains a minified version of your app.  Copy the code in dist to your where your production code resides on your laptop,  then upload the production code to your server.

21. Vue Files & Templates

Every Vue component (file ending in .vue) must have a template element.  They can also have optional script and style elements.  The .vue file must specify the template element first.

The template element can have multiple root elements (previously, it could only have one).

Inside App.vue, we see the following template and script elements.

<template>
    <img alt="Vue logo" src="./assets/logo.png">
    <msg="Welcome to Your Vue.js App"/>
</template>

<script>
    import from './components/HelloWorld.vue'
    export default {
        name: 'App',
        components: {}
    }
</script>

The HelloWorld.vue file contains another component.  The App component imports the HelloWorld component from HelloWorld.vue and injects it in the App template which is injected into the app div in index.html.

We can use the techniques we saw in the last section by modifying the object that is exported.  Here we return an object with a data() function and use the data in the template.

<template>
  <h1>{{ title }}</h1>
</template>

<script>
export default {
  name: 'App',
  data() {
    return {
        title: 'My first Vue App'
    }
  }
}
</script>

22. Template Refs

In JavaScript if you need a DOM element you use the queryselector.  In Vue we use don’t use queryselector, we use template refs.

Template refs allow us to store a reference to a DOM element.

<template>
  <input type="text" ref="name">
  <button @click="handleClick">click me</button>
</template>

Now in the app object we add the event listener.

methods: {
  handleClick() {
    this.$refs.name.classList.add('active')
    this.$refs.name.focus()
  }
}

23. Multiple Components

It is not a good idea to put all of your code in one Vue component.

We structure our Vue application in components.  These components form a tree.

App.Vue  // root component
  Header.vue
  Article.vue
    Content.vue
    Comments.vue
  Footer.vue

Vue components should start with a capital letter and end with .vue.

Let’s remove the HelloWorld component and create a new component named Modal with the following template.

<template>
  <div class="backdrop">
    <div class="modal">
      <p> modal content </p>
    </div>
  </div>
</template>

We want to use the Modal component in the App component.

In App.vue we need to import the Modal from Modal.vue.

<script>
  import Modal from '.components/Modal.vue'

  export default {
    name: 'App',
    components: {
      Modal
    },
    ...
  }
</script>

Now inside the App template we can add the Modal subcomponent as an HTML element.

<template>
  <Modal/>
</template>

24. Component Styles & Global Styles

By default, all styles declared in the component files are loaded into the head of the document and are global.

If we want styles to apply only to the component in which they’re defined we add the scoped attribute to the style element.

<style scoped>
  ...
</style>

If you need global styles we can create a global.css file in the assets directory.

Then in main.js we simply import global.css.

import './assets/global.css'

25. Passing Data with Props

Suppose we want to pass data from the App component to the Modal component.  For example, pass the title and modal content into the Modal component. To do this we define props in the Modal component for the title and modal content.  Props are exported in the script tag of the component.

<script>
  export default {
    props: ['title', 'content']
  }
</script>

Props can be used in the template of the component in which they are defined, just like data properties.

<template>
  <div class="backdrop">
    <div class="modal">
      <h1>{{ title }}</h1>
      <p>{{ content }} </p>
    </div>
  </div>
</template>

Now in the Modal element that we declared in the App component we can specify the values for the props.

<Modal title="New Modal Title" content="New Modal Content" />

In the App component we can also bind data properties to the props.

<template>
  ...
  <Modal :title="modalTitle" :content="modalContent" />
</template>

<script>
  ...
  export default {
    data() {
      return {
        modalTitle: 'Binded Title',
        modalContent: 'Binded Data'
    }
  }
}
</script>

We can also add a dynamic class based on the value of a prop.  In Modal.vue we add the following code.  Here, we add the class name sale if the value of theme is equal to the string sale.

<template>
  <div class="backdrop">
    <div class="modal" :class="{sale: theme === 'sale'}">
      <h1>{{ title }}</h1>
      <p>{{ content }} </p>
    </div>
  </div>
</template>

<script>
export default {
  props: ['title', 'content', 'theme']
}
</script>

In the app component we set the theme property to sale.

<Modal :title="modalTitle" :content="modalContent" theme="sale"/>

26. Emitting Custom Events

To show the modal we’ll do a few things.  First, we’ll create a boolean data property (initialized to false) that indicates if the modal should be displayed. Then, we’ll use the conditional v-if directive to display the modal based on the value of the boolean data property.  Last, we’ll create a button to toggle the data property’s boolean value to true.

data() { 
  return { 
    ... 
    showModal: false 
  } 
}, 
methods: { 
  ... 
  toggleShowModal() { 
    this.showModal = !this.showModal 
  } 
}

<Modal v-if="showModal" ... />
<button @click="toggleShowModal">show modal</button>

Now once the modal is displayed we need a way, from the modal component, to toggle the ShowModal data property in the App component.  To do this we’ll create a custom event that can be fired from the child component (modal) and listed to in the parent component (app)

First, we’ll create a method named closeModal and register it as an event handler for the click event on the backdrop of the modal. Next, we’ll add the closeModal method to the modal component’s export object.  In the method we’ll fire off a custom event named close.

<div class="backdrop" @click="closeModal">

export default {
  ...
  methods: {
    closeModal() { this.$emit('close') }
  }
}

Then on the Model element in the app component we register an event listener for the close event.

 <Modal v-if="showModal" ... @close="toggleShowModal"/>

27. Click Event Modifiers

Currently if we click on the modal itself, it closes since the modal is a child of the backdrop.  To prevent this we’ll add a self event modifier to the close event handler in the modal component so that the event (click) handler is only executed for mouse clicks on the backdrop and not its children.

<div class="backdrop" @click.self="closeModal">

28. Slots

Sometimes we want to pass templates from one component to another.  To do so, we’ll use slots.

A slot is defined with a slot element in the component that will receive template elements.  For example, we can define a slot in the modal component’s template element.

<template>
  <div class="backdrop" @click.self="closeModal">
    <div class="modal" :class="{sale: theme === 'sale'}">
      <slot></slot>
    </div>
  </div>
</template>

The parent component can then send HTML elements to fill the slot.  For example, the App component can fill the slot by specifying HTML elements in between opening and closing Modal elements.

<Modal v-if="showModal" ... @close="toggleShowModal">
  <h1>Slot Title</h1> 
  <p>Slot Description</p>
</Modal>

If the parent component doesn’t pass anything into the slot nothing is displayed.  You can specify default content between the slot tags.  The default content is only displayed if the parent does pass content.

<slot>
  <h1>Default Title</h1>
</slot>

We can also pass in named slots.  A named slot is a slot element with a name attribute.

<template>
  <div class="backdrop" @click.self="closeModal">
    <div class="modal" :class="{sale: theme === 'sale'}">
      <slot></slot>
      <div>
        <slot name="links"></slot>
      </div>
    </div>
  </div>
</template>

The parent component can specify a content for a named slot using a template element.

<Modal v-if="showModal" ... >
  ...
  <template v-slot:links>
    <a href="#">sign up now</a><br>
    <a href="#">more info</a>
  </template>
</Modal>

29. Challenge- Reusing Components

  • create an extra button to open a different modal
  • use the same modal component, but pass in a different template slot
  • use a different method (e.g. toggleModal2) to toggle on the modal.

30. Using Teleport

One of the new features in Vue 3 allows you to place (teleport) outside the V3 app some of the HTML content that is defined inside a component file.

For example we can place the modals outside the app div.  Inside index.html, lets create a div below the app div and give it an id named modals.

<div id="app"></div>
<!-- built files will be auto injected -->
<div id="modals"></div>

Now inside the appcomponent where we create instances of the modal component, we wrap the modal elements in <teleport> tags and specify where we want to load the modals by including a to attribute.  The value of the true attribute is a CSS selector specifying the id of the div in index.html.

<teleport to="#modals">
  <Modal ...>
    ...  </Modal>
</teleport>

Note that the stying of the modals may have changed since the modals no longer exist in the app element.

© 2022, Eric.

Leave a Reply

Your email address will not be published. Required fields are marked *