DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Please enter at least three characters to search
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

The software you build is only as secure as the code that powers it. Learn how malicious code creeps into your software supply chain.

Apache Cassandra combines the benefits of major NoSQL databases to support data management needs not covered by traditional RDBMS vendors.

Generative AI has transformed nearly every industry. How can you leverage GenAI to improve your productivity and efficiency?

Modernize your data layer. Learn how to design cloud-native database architectures to meet the evolving demands of AI and GenAI workloads.

Related

  • Vue.js Tutorial: Build a Tesla Battery Range Calculator in Vue 3
  • Automated Bug Fixing: From Templates to AI Agents
  • Dynamic File Upload Component in Salesforce LWC
  • Mocking Dependencies and AI Is the Next Frontier in Vue.js Testing

Trending

  • Distributed Consensus: Paxos vs. Raft and Modern Implementations
  • After 9 Years, Microsoft Fulfills This Windows Feature Request
  • Introduction to Retrieval Augmented Generation (RAG)
  • Navigating the LLM Landscape: A Comparative Analysis of Leading Large Language Models
  1. DZone
  2. Coding
  3. JavaScript
  4. Extending Vue.js Components

Extending Vue.js Components

Using HTML, and a little bit of Pug, this web dev expert shows us how to extend the components of our Vue.js applications.

By 
Anthony Gore user avatar
Anthony Gore
DZone Core CORE ·
Updated Feb. 10, 20 · Tutorial
Likes (5)
Comment
Save
Tweet
Share
37.3K Views

Join the DZone community and get the full member experience.

Join For Free

Do you have components in your Vue app that share similar options, or even template markup?

It'd be a good idea to create a base component with the common options and markup, and then extend the base component to create sub components. Such an architecture would help you apply the DRY principle in your code (Don't Repeat Yourself) which can make your code more readable and reduce the possibility of bugs.

Vue provides some functionality to help with component inheritance, but you'll also have to add some of your own ingenuity.

Example: Survey Questions

Here is a simple survey made with Vue.js:

Image title

You'll notice that each question has a different associated input type:

  1. Text input
  2. Select input
  3. Radio input

A good architecture would be to make each question/input type into a different, reusable component. I've named them to correspond to the above:

  1. SurveyInputText
  2. SurveyInputSelect
  3. SurveyInputRadio

It makes sense that each question/input is a different component because each needs its own markup (e.g. <input type="text"> vs <input type="radio">) and each needs its own props, methods, etc., as well. However, these components will have a lot in common:

  • A question.
  • A validation function.
  • An error state.
  • Etc.

So I think this is a great use case for extending components!

Base Component

Each of the sub components will inherit from a single file component called SurveyInputBase. Notice the following:

  • The question prop is going to be common across each component. We could add more common options, but let's stick with just one for this simple example.
  • We somehow need to copy the props from this component to any extending component.
  • We somehow need to insert the markup for the different inputs inside the template.

SurveyInputBase.vue

<template>
  <div class="survey-base">
    <h4>{{ question }}</h4>
    <!--the appropriate input should go here-->
  </div>
</template>
<script>
  export default {
    props: [ 'question' ],
  }
</script>

Inheriting Component Options

Forgetting the template for a moment, how do we get each sub component to inherit props? Each will need question as a prop, as well as their own unique props:

Image title

This can be achieved by importing the base component and pointing to it with the extends option:

SurveyInputText.vue

<template>
  <!--How to include the question here???-->
  <input :placeholder="placeholder">
</template>
<script>
  import SurveyInputBase from './SurveyInputBase.vue';
  export default {
    extends: SurveyInputBase,
    props: [ 'placeholder' ],
  }
</script>

Looking in Vue Devtools, we can see that using extends has indeed given us the base props in our sub component:

Image title

Merge Strategy

You may be wondering how the sub component inherited the question prop instead of overwriting it. The extends option implements a merge strategy that will ensure your options are combined correctly. For example, if the props have different names, they will obviously both be included, but if they have the same name, Vue will preference the sub component.

The merge strategy also works with other options like methods, computed properties, and lifecycle hooks, combining them with similar logic. Check the docs for the exact logic on how Vue does it, but if you need you can define your own custom strategy.

Note: there is also the option of using the mixin property in a component instead of extends. I prefer extends for this use case, though, as it has a slightly different merge strategy that gives the sub components options higher priority.

Extending the Template

It's fairly simple to extend a component's options - what about the template, though?

The merge strategy does not work with the template option. We either inherit the base template, or we define a new one and overwrite it. But how can we combine them?

My solution to this is to use the Pug pre-processor. It comes with include and extends options so it seems like a good fit for this design pattern.

Base Component

Firstly, let's convert our base component's template to Pug syntax:

<template lang="pug">
  div.survey-base
    h4 {{ question }}
    block input
</template>

Notice the following:

  • We add lang="pug" to our template tag to tell vue-loader to process this as a Pug template (also, don't forget to add the Pug module to your project as well npm i --save-dev pug)
  • We use block input to declare an outlet for sub component content.

So here's where it gets slightly messy. If we want our child components to extend this template we need to put it into its own file:

SurveyInputBase.pug

div.survey-base
  h4 {{ question }}
  block input


We then include this file in our base component so it can still be used as a normal standalone component:

SurveyInputBase.vue

<template lang="pug">
  include SurveyInputBase.pug
</template>
<script>
  export default {
    props: [ 'question' ]
  }
</script>

It's a shame to have to do that since it kind of defeats the purpose of "single file" components, but for this use case, I think it's worth it. You could probably make a custom webpack loader to avoid having to do this.

Sub Component

Let's now convert our sub component's template to Pug as well:

SurveyInputText.vue

<template lang="pug">
  extends SurveyInputBase.pug
  block input
    input(type="text" :placeholder="placeholder")
</template>
<script>
  import SurveyInputBase from './SurveyInputBase.vue';
  export default {
    extends: SurveyInputBase,
    props: [ 'placeholder' ],
  }
</script>

The sub components use the extends feature of Pug which includes the base component and outputs any custom content in the input block (a concept analogous to slots).

Here's what the sub component's template would effectively look like after extending the base and being translated back to a regular HTML Vue template:

<div class="survey-base">
  <h4>{% raw %}{{ question }}{% endraw %}</h4>
  <input type="text" :placeholder="placeholder">
</div>

Bring it All together

Using this strategy we can go ahead and create the other two sub components SurveyInputSelect and SurveyInputRadio with their own props and markup.

If we then use them in a project our main template might look like this:

<survey-input-text
  question="1. What is your name?"
  placeholder="e.g. John Smith"
></survey-input-text>

<survey-input-select
  question="2. What is your favorite UI framework?"
  :options="['React', 'Vue.js', 'Angular']"
></survey-input-select>

<survey-input-radio
  question="3. What backend do you use?"
  :options="['Node.js', 'Laravel', 'Ruby']"
  name="backend"
>
</survey-input-radio>

And here's the rendered markup:

<div class="survey-base">
  <h4>1. What is your name?</h4>
  <input type="text" placeholder="e.g. John Smith">
</div>
<div class="survey-base">
  <h4>2. What is your favorite UI framework?</h4>
  <select>
    <option>React</option>
    <option>Vue.js</option>
    <option>Angular</option>
  </select>
</div>
<div class="survey-base">
  <h4>3. What backend do you use?</h4>
  <div><input type="radio" name="backend" value="Node.js">Node.js</div>
  <div><input type="radio" name="backend" value="Laravel">Laravel</div>
  <div><input type="radio" name="backend" value="Ruby">Ruby</div>
</div>

Note: we could also instantiate the SurveyInputBase component, since it will work standalone, but it wasn't really needed in this example. I thought that was an important point to mention, though.

Template Vue.js

Published at DZone with permission of Anthony Gore, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

Related

  • Vue.js Tutorial: Build a Tesla Battery Range Calculator in Vue 3
  • Automated Bug Fixing: From Templates to AI Agents
  • Dynamic File Upload Component in Salesforce LWC
  • Mocking Dependencies and AI Is the Next Frontier in Vue.js Testing

Partner Resources

×

Comments
Oops! Something Went Wrong

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!