Skip to content

Custom Directives in Vue JS

Custom Directives in Vue JS

This article was written over 18 months ago and may contain information that is out of date. Some content may be relevant but please refer to the relevant official documentation or available resources for the latest information.

Vue JS promotes the use of components as the primary form of code reuse and abstraction. However, there are some instances when you want to manipulate the low-level DOM from within Vue JS components. In these instances, directives come to the rescue!

If you have already been developing apps with Vue JS you must surely be familiar with some of the core directives offered by the Vue JS core team. Here are a few worth mentioning: v-model, v-if, v-for’, etc.

In this article, I will cover everything you need to know to start building your own custom directives in Vue JS.

Dissect a Custom Directive

Custom directives enrich HTML with new reactive functionality that's fully managed by Vue JS.

Let's start with a full dissection of a basic custom directive written for Vue JS.

Consider the following directive:

Vue.directive('focus', {
  // When the bound element is inserted into the DOM...
  inserted: function (el) {
    // Focus the element
    el.focus()
  }
})

This example registers a new global custom directive into the main Vue instance. Later I will discuss the different ways available for registering directives. For now, let’s focus on the directive itself.

A custom directive is defined by a JavaScript literal object implementing a set of functions. These functions are called hooks by Vue JS and are standard to any custom directive. More on hook functions in the coming section.

The inserted() hook function accepts the el input parameter. This parameter represents the HTML element where this custom directive is applied.

Inside the function, the focus() function is called on the element itself.

In summary, when the element with the custom directive is added to its parent node, this function runs and makes the element in the focus state.

How do you apply this custom directive inside a component? Every custom directive should be prefixed by the letter v-. In this case, assuming we are adding this custom directive to an input control, then it follows like this:

<input v-focus>

Hook Functions

All hook functions provided by Vue Js for building custom directives are optional. Hook functions are there to help you customize and provide the needed functionality for the directive at certain stages of the directive life cycle.

There are five available:

  • bind
  • inserted
  • update
  • componentUpdate
  • unbind

bind

This function is called once when the directive is bound to the underlying element. Think of it as a one-time setup hook.

inserted

This is called when the underlying element is inserted into the parent node. This doesn’t mean the element is inserted into the live DOM but rather its context is now known and part of a well-defined tree of nodes. You can read more about VNodes to understand how Vue JS works with them.

update

This function is called after the containing component's VNode has updated, but possibly before its children have updated.

componentUpdate

This is called after the containing component's VNode and the VNodes of its children have updated.

unbind

This function is called only once when the directive is unbound from the element.

Vue JS engine passes the same set of input parameters to all hook functions. Let's look at these parameters.

Binding Function Parameters

Each and every hook function receives the same set of input parameters defined as follows.

el

This parameter represents the element that this custom directive is applied to. It can be any valid HTML element.

binding

This input parameter is an object containing the following properties:

name: The name of the directive without the v- prefix. For instance, using a custom directive as v-focus yields a name of focus.

value: The value passed to the directive. For instance, using the v-slot=”prop” directive yields a value of prop.

oldValue: This field is only available inside update() and componentUpdate() hook functions. It contains the previous value of the directive, before the update.

expression: This field represents the expression of the binding as a string literal. For instance, using the custom directive v-add="1+1" yields an expression of "1+1".

arg: This field represents the argument (if any) that's passed to the directive. There can be only one argument passed. For instance, using the v-slot:default directive yields an argument of default.

modifiers: This field is an object containing modifiers that could change and control the behavior of the directive if they are set. Think of modifiers as flags you set on the directive. If a modifier is set, it will have a value of true, if not set, it won’t even be visible to the directive. For example, using the directive v-on:click.prevent yields a modifier of { prevent: true } object.

vnode

The virtual node produced by Vue's compiler. See the VNode API for full details.

oldVnode

The previous virtual node, only available in the update() and componentUpdated() hooks.

Now that you know all about hooks and the details about their input parameters, let's see how you register a custom directive in your Vue JS app.

Globally Registered Directives

There are two ways to define and register a custom directive. In this section, we will look at how to register a custom directive globally in your app.

To do this, navigate to the main.js file located at the root folder of your application and add the following to register the focus custom directive.

import Vue from "vue";
import App from "./App.vue";

Vue.config.productionTip = false;

// Register a global custom directive called `v-focus`
Vue.directive('focus', {
  // When the bound element is inserted into the DOM...
  inserted: function (el) {
    // Focus the element
    el.focus()
  }
})

new Vue({
  render: h => h(App)
}).$mount("#app");

The `Vue.directive()` function accepts as a first parameter the name of the custom directive (without the `v-` prefix). The second parameter is the custom directive object. In this case, the object contains the `inserted()` hook function only.

That's it! Now you can use the custom directive anywhere inside your components.

Locally Registered Directives

The second way of registering custom directives is local to the component. You can define and register a custom directive to be used inside a single component. In case you want to use the same custom directive somewhere else in your app, you have to redefine it again inside the other component.

This method of registering custom directives is definitely limited and might not be used often, if not at all!

I strongly recommend registering your custom directives as global directives for better performance and easier access across your app.

To register a custom directive locally, navigate to the component where you want to use the custom directive, and add the method below as part of the Vue Options API:

…
directives: {
  focus: {
    // directive definition
    inserted: function (el) {
      el.focus()
    }
  }
}
...

That’s it!

Demo: List Custom Directive

Now that you understand custom directives in Vue JS, let's build a custom directive.

The custom directive I am going to build in this section is the v-list directive.

Using this custom directive as such:

<div class="list" v-list:ul.square.inside="items"/>

Yields the following HTML being generated inside the DOM:
<ul style="list-style-type: square; list-style-position: inside;">
   <li>Item #1</li>
   <li>Item #2</li>
   <li>Item #3</li>
</ul>

Given a variable named `items` defined as an array of strings, yields the app showing in __Figure 1__:
figure1
Figure 1: The List custom directive in action

Figure 2 below shows the details of using this custom directive:

figure2
Figure 2: Using the custom directive

The diagram above is self-explanatory!

Let's sift through the code and define this custom directive.

You can play with the custom directive on codesandbox.io.

Add a new \directives\List.directive.js file and add the following code:

const directive = {
  bind(el, binding, vnode) {
    if (!Array.isArray(binding.value)) {
      return;
    }

    // validate value, arguments, and modifiers
    const { items, listType, listStyleType, listStylePosition } = validate(
      binding
    );

    render(el, { items, listType, listStyleType, listStylePosition });
  }
};

export default directive;

This code snippet defines an object called `directive`. Then, this object is exported as the default export of this code file.

The custom directive at hand makes use of the bind() hook function to implement the functionality of this directive.

First of all, it checks if the binding.value is bound to an array variable. If not, it returns and nothing happens.

The next step is to validate the argument and modifiers. This is done in a separate local utility function called validate. We will get into this very shortly.

The validate() function not only validates the different parts of the custom directive but also sets some default values in order to appease the rendering process.

Finally, it's time to render the list, whether a ul or ol list.

Let's have a look at the validate() method.

const validate = binding => {
  let results = {
    items: [],
    listType: "ul",
    listStyleType: "disc",
    listStylePosition: "outside"
  };

  // grab items
  results["items"] = [...binding.value];

  // grab argument
  const arg = binding.arg;
  const validArgs = ["ul", "ol"];

  if (arg && validArgs.includes(arg)) {
    results["listType"] = arg;
  }

  // grab modifiers
  const modifiers = binding.modifiers;
  const validModifiers = [
    "disc",
    "circle",
    "square",
    "decimal",
    "decimal-leading-zero",
    "lower-roman",
    "upper-roman",
    "lower-greek",
    "lower-latin",
    "upper-latin",
    "armenian",
    "georgian",
    "lower-alpha",
    "upper-alpha",
    "none",
    "inside",
    "outside"
  ];

  if (modifiers) {
    for (const [key, value] of Object.entries(modifiers)) {
      if (value) {
        // modifier included
        if (key && validModifiers.includes(key)) {
          if (key === "inside" || key === "outside") {
            results["listStylePosition"] = key;
          } else {
            results["listStyleType"] = key;
          }
        }
      }
    }
  }

  return results;
};

The method prepares a well-defined result object containing the following properties:
  • items: This property represents the binding.value of the directive. Whatever array variable you bind to the directive, it is being captured inside the items property.

  • listType: This property represents the type of list to render. Whether it is a ul element or ol element. It represents the binding.arg property defined on the custom directive.

  • listStyleType: This property represents the list-style-type CSS property defined on an ul or ol element. It represents one of the modifiers that this custom directive accepts. The code validates this modifier based on a known list of values that the list-style-type property accepts.

  • listStylePosition: This property represents the list-style-position CSS property defined on a ul or ol element. It represents one of the modifiers that this custom directive accepts. The code validates this modifier based on a known list of values that the list-style-position property accepts.

The properties above are defined with a default value representing the real default value behind list-style-type and list-style-position respectively. If these modifiers are incorrect, the default values take precedence.

Let's have a look at the render() method:

const render = (el, { items, listType, listStyleType, listStylePosition }) => {
  if (!el) return;

  // clear container
  el.innerHTML = "";

  // add the list
  const list = document.createElement(listType);
  el.appendChild(list);

  // configure list
  list.style.listStyleType = listStyleType;
  list.style.listStylePosition = listStylePosition;

  items.forEach(item => {
    const li = document.createElement("li");

    list.appendChild(li);

    li.textContent = item;
  });
};

This method starts by clearing the parent container, the `el` element.

It then creates a new HTML element, whether a new ul or ol element. It appends the list into the parent container el element.

After that, it sets the listStyleType and listStylePosition properties on the newly created list element.

It then iterates over the items stored inside the binding.value array. For each array item, it creates a new li element, appends it to the list element created above, and sets it's textContent property to the value of the array item.

To use this custom directive, switch back to the \main.js file and register this directive globally as follows:

...
import ListDirective from "./directives/List.directive";

Vue.directive("list", ListDirective);
...

That's all!

Navigate to App.vue file and add the directive as follows:

<template>
  <div id="app">
    <h2>Awesome List</h2>
    <div class="list" v-list:ul.square.inside="items"/>
  </div>
</template>

<script>
export default {
  name: "App",
  data() {
    return {
      items: ["Item #1", "Item #2", "Item #3"]
    };
  }
};
</script>

Attach the directive to a `
` element. Then set the `items` variable to an array of strings.

Running the app yields the same app shown above in Figure 1.

This custom directive can be made much more complicated. However, I opted for a simplified implementation to illustrate the ideas behind building a custom directive in Vue JS.

You can play with the custom directive on codesandbox.io.

Conclusion

Despite the fact that Vue JS pushes for coding components rather than custom directives, there are some instances when you need to manipulate the DOM reactively using custom directives.

This Dot is a consultancy dedicated to guiding companies through their modernization and digital transformation journeys. Specializing in replatforming, modernizing, and launching new initiatives, we stand out by taking true ownership of your engineering projects.

We love helping teams with projects that have missed their deadlines or helping keep your strategic digital initiatives on course. Check out our case studies and our clients that trust us with their engineering.

You might also like

3 VueJS Component Libraries Perfect for Beginners cover image

3 VueJS Component Libraries Perfect for Beginners

For developers checking out VueJS for the first time, the initial steps are overwhelming, particularly when setting up projects from square one. But don’t worry! The VueJS ecosystem offers a plethora of remarkable component libraries, easing this early obstacle. These three libraries are pre-built toolkits, providing beginners with the means to kickstart their VueJS projects effortlessly. Let’s take a look! Quasar Quasar is among the most popular open-source component libraries for Vue.js, offering a comprehensive set of ready-to-use UI components and tools for building responsive web applications and websites. Designed with performance, flexibility, and ease of use in mind, Quasar provides developers with a wide range of customizable components, such as buttons, forms, dialogs, and layouts, along with built-in support for themes, internationalization, and accessibility. With its extensive documentation, active community support, and seamless integration with Vue CLI and Vuex, Quasar empowers developers to rapidly prototype and develop high-quality Vue.js applications for various platforms, including desktop, mobile, and PWA (Progressive Web Apps). PrimeVue PrimeVue is a popular Vue.js component library offering a wide range of customizable UI components designed for modern web applications. Developed by PrimeTek, it follows Material Design guidelines, ensuring responsiveness and accessibility across devices. With features like theming, internationalization, and advanced functionalities such as lazy loading and drag-and-drop, PrimeVue provides developers with the tools to create elegant and high-performing Vue.js applications efficiently. Supported by clear documentation, demos, and an active community, PrimeVue is an excellent choice for developers seeking to streamline their development process and deliver polished user experiences. Vuetify Vuetify is a powerful Vue.js component library that empowers developers to create elegant and responsive user interfaces with ease. Built according to Google's Material Design guidelines, Vuetify offers a vast collection of customizable UI components, ranging from buttons and cards to navigation bars and data tables. Its comprehensive set of features includes themes, typography, layout grids, and advanced components like dialogues and sliders, enabling developers to quickly build modern web applications that look and feel polished. With extensive documentation, active community support, and ongoing development, Vuetify remains a top choice for Vue.js developers seeking to streamline their workflow and deliver visually stunning user experiences. For newcomers venturing into Vue.js, the initial setup might seem daunting. Thankfully, Vue.js offers a variety of component libraries to simplify this process. Quasar, PrimeVue, and Vuetify are standout options, each providing pre-built tools to kickstart projects smoothly. Whether you prefer Quasar's extensive UI components, PrimeVue's Material Design-inspired features, or Vuetify's responsive interfaces, these libraries cater to diverse preferences and project requirements. With their clear documentation and active communities, these libraries empower developers to start Vue.js projects confidently and efficiently, enabling Vue developers to create polished user experiences....

Getting started with Vitepress cover image

Getting started with Vitepress

Getting started with Vitepress to create your blog site Have you heard about Vitepress, but you have not checked it out at it yet? Well, in this article, we are going to cover the basic setup and share what all the fuss about this new fantastic static site generator. If you are looking for something that would allow you to quickly create a static site with Markdown support, search, Light/Dark theme, advertisement, navigation, and much more, then Vitepress is what you are looking for. Suppose you want some proof of Vitepress's power without having to invest any further time, I suggest you head over to the main Vitepress site (vitepress.dev) and experience what Vitepress can do, as the official site is built on Vitepress! What is Vitepress Vitepress is a powerful Static Site Generator powered by Vite mainly used for Documentation and Blogs with the default theme, and open to customization to be used for anything you may need it to with custom themes and theme overrides.. Vitepress power comes from its low learning curve, powerful configuration settings, and the ability to easily customize it. For example, internationalization is built-in, Theme toggling is built-in and even full-site searches require a single line in the config to be set up. What makes Vitepress so powerful is its extensive configuration and the fact that it can be customized by overriding the existing theme, creating your theme, or simply enhancing pages with custom Vue code. Prerequisites To successfully run the following demo, you will need: - Node.js version 18 or higher. - Terminal for accessing VitePress via its command line interface (CLI). - Text Editor with Markdown syntax support. - VSCode is recommended, along with the official Vue extension. Create your project To create the project, we will follow the steps shown in the official Getting Started guide. If you want to set up a Vitepress project quickly, run the following steps: - Create a new folder mkdir vitepress-tutorial - Access the folder cd vitepress-tutorial - Install Vitepress npm add -D vitepress - Run the Wizard npx vitepress init - Answer the questions: - Where should VitePress initialize the config? - Site title - Site Description - Theme - Do you want Typescript support? - run npm run docs:dev - Enjoy your site. After you run the above steps, your Vitepress will be ready and running as shown below What is available out of the box The site comes fully set up with enough to get you started. Let's see what features are available within the software: - Navbar: The site comes with a Navbar that already includes links to our blog pages - Light/Dark theme: Out of the box theming - Home page: Basic Homepage layout - Blogs: Two different blog posts with sidebar navigation All of the above is available with just 4 files! Let's move forward and see how to customize our app. Overriding the homepage It is time to modify our site by accessing its folders. What you will probably notice when opening the site, is that Vitepress follows a very simple file structure, in fact, a new installation just includes 4 extra files on top of the expected package.json and node_modules. Let's update our homepage to make it more unique. Because Vitepress follows a "file-system based router" we can find this file within the root of the project within a file called index.md. This file will be accessible at "/" or "/index.html" of your site. The content of the file is the following: ` As you can see, the file has no code in it, and it is just driven by configuration, or more specifically in this case Markdown frontmatter, that is a set of configurations accepted by markdown files that help you set up your options such as title, description, og:image. We will describe frontmatter in more detail later in the article. Before we jump into modifying the configuration of the file, you should know that Vitepress has a very extensive and well-detailed documentation site where you can find help if you are stuck or would like to learn more. In this case, we are overriding the "home" page, so the documentation can be found here: Home Page Default Theme configuration. If you read the current file within the homepage, you will notice that it defines a "layout" of "home". This layout displays a main header with CTAs and a set of feature blocks. There are three different layouts available in the Default theme: "home", "doc" and "page". The title and description have already been set from the information we provided from the installation wizard. But the rest is just a basic boiler template. Let's see what we can change: 1) Add a hero image. This image can either be externally sourced or saved within the repository. ` 2) Update the action buttons. ` As shown above, Action buttons have two themes "brand" or "alt" and can accept internal or external links. Vitepress is clever enough to set the external links to open in a new tab and set the rel="noreferrer". 3) Change the feature blocks: Features blocks are great for engaging with the user and telling them what makes your site special. This accepts an icon and some text, so let's change ours with the following: ` The homepage is now completed and updated and it should look like this: Please note that what we did on the homepage is just changing the markdown front matter of the "home" layout. What this means is that if you want to, you can easily customise the page further by either adding more blocks to the layout file or actually by writing and styling the rest of the page as normal. Site configuration In this step, we are going learn about the main site configuration and how we can use it to customize our site. The configuration file can be found in the .vitepress folder under the name of "config.mjs". Just like the homepage layout, the configuration used in the preset file is self-explanatory: ` This file is very important and you are probably going to have it open at all times as it is going to be responsible for the layout of the sidebar navigations of your site. In this section, we are going to learn how to use the Vitepress configuration file to achieve the following: - Modify the sidebar navigation - Enable Search - Add Favicon to your site Modify the sidebar navigation The sidebar is the component displayed on the left-hand side of the blog pages. This is not automatically generated, but it is manually set in this file. You can have more than one navigation, so, for example, you could specify sidebar navigation that shows in pages that have "/blog/" within their path and another one for all markdown pages that have "/tutorials/". Let's see how to implement this: ` With the above code, there will be 2 different sidebars. One will just show if the URL includes "/blog" and the other will be displayed if the path includes "tutorials". Clicking on the above items will give a 404 error as we do not have any pages in that specific location. Still, we can easily fix the issue by creating the two folders "blog" and "tutorial" and creating the required files within these folders in our case are "index.md" and "one.md" for the blog and "index.md" and "two.md" for tutorials. The sidebar has further settings like the ability to create a nested tree or create collapsable menus. You can learn more by checking out the the official documentation on default theme sidebar. Enable Search No documentation or blog site is complete until a search is fully implemented on it. Luckily for us, enabling a site-wide search is extremely simple with Vitepress. Vitepress supports two different search types: a full-text search using minisearch and Algolia based search. In this tutorial, we are going to enable the full-text search. This is fully built-in and requires no external accounts. To enable search, we need to add a "Search" parameter within our themeConfig and set the provider to "local". We can add this right below the socialLinks. ` With just a simple config change, our site will now have a full site search working as shown below: The official search documentation has further information about the look and functionality of the search. Add Favicon to your site Even if the site configuration is very extensive, there are times when you will need to add something within the site-wise page that is not available. In this section we are going to learn how to add specific attributes to the of our site, and specifically how to add a favicon. To add custom values to the head, we are going to use the "head" properties available with the Vitepress configuration. The "head" configuration is an array that accepts an array with the element type (eg. link, script, meta), and an object including all the attributes for that specific element. So for example to replicate the following HTML: ` we would define the following head config: ` We can use this technique to set up metadata and even load fonts or Google Analytics. More info in the site-config head Writing your post In the last section of this article, we are going to learn how to actually write a blog post and also learn how to customize the pages with JS and styles. Writing your first page In the previous section, we created a couple of markdown files, but they are currently empty. Let's see how to create beautiful documentation using the built-in feature of Vitepress. In this section, we are going to work on the file stored in /blog/index.md. This file can be accessed in your browser by accessing "http://localhost:5174/blog/index.html". Blog Frontmatter Every file needs to have a frontmatter defined. This is used to define basic information on the page, such as the title, description, and more. For best SEO results, it is best practice to always define a title and description for a new page. We can do this by adding the following code at the top of our file: ` Frontmatter in Markdown files are delimited by the "---". In the above code, we set our title and description. Vitepress frontmatter supports further config like lastUpdates, sidebar, outline, and more. You can find info on the frontmatter-config documentation. Frontmatter has some global configuration, such as title and description, but also "layout specific" settings that are just available on specific layouts. This is clearly explained in the documentation. Blog Markdown Now that our post frontmatter is set, it is time to write the content of it. This is done using a very extensive markdown engine. Let's start with the basics and write a heading with some text and a link as shown below: ` After saving, our development environment will show the following results: Let's add a couple of headings and a table of contents to our document using [[toc]] to the top of our page: ` The above will produce an unordered list with all our links. The last feature that I want to share about the Vitepress markdown engine is the code editors. The markdown engine used in Vitepress offers extensive features when it comes to code rendering. Features available are "code highlight", "code focus", external snippets loading and much more. The full list of features can be found in the markdown docs. Let's implement a simple code snippet with a line of code focused. This can be achieved by adding "// [!code focus]" at the end of our row. Let's give it a try: ` The output of this will be: Customize your blog with Vue In this last section, we will learn how to customize our code by adding js logic, style, and external components. Before we begin, it is important to understand that when you use the default template of Vitepress, your pages are going to be rendered with Vue. This means that we can expand the individual page's functionality by rendering other vue components and/or writing logic and styles directly on the page. To better explain this concept, we are going to create a team page. Create a file called team.md within the root of our repository. This file will be accessible on "http://localhost:5174/team.html" and will be just a simple Markdown file. First, we set up the frontmatter as before. In this case, we have to specify an additional config called "layout". This will ensure the page has no style or components loaded such as sidebar or aside. ` Then we will have to create our team variable, just like a normal Vue component; we can do so by defining a script setup tag and defining the constant there. ` In the above code, we have imported the VPTeamMembers component from the vitepress/theme and defined an array of members. The values used in the members array are hardcoded, but as you may expect, you can load this information directly from an API. We now have to use the "members" variable within our file. We can do so by writing normal JavaScript. The markdown file will evaluate the JavaScript and render it, so in the following example it will render a specific component. ` Before we move forward we need to define some style as the page is unstyled since we have loaded the layout of "page". Just like we did with the tag, we can load a tag and update our styles: ` The completed file will look like this: ` The file above now includes: - A frontmatter that specifies a "page" layout - A script tag that loads an external component and defines a variable - Styles to make the page pretty - Markdown with a custom component The completed team page would render the following: Conclusion Vitepress is an extremely flexible static site generator. Its default theme provides you with everything you need to get started quickly. The ability to load external themes, enhance the current theme, and or write custom code within your page makes this tool extremely powerful. In future articles, we will discover what components are available within the Default Theme and see how you can make the most of your Vitepress site but keep on coding....

Take your App to the Next Level with Vue 3 cover image

Take your App to the Next Level with Vue 3

Vue 3 has now officially launched and you are probably wondering how you are going to start migrating your existing Vue 2 apps to Vue 3. I will be honest with you: a framework migration is always the most tedious and painstaking task you will ever encounter. The good news is that migrating from Vue 2 to Vue 3 is not that difficult and complicated. As you may know, Vue 3 source code has been written from scratch. However, the maintainers of the framework made sure not to change the API too much. In other words, we will benefit from all the goodies Vue 3 brings, with minimal change. How awesome is that?! Vue 3 Official Migration Guide The Vue documentation website has been refreshed to reflect the latest changes. The Vue community has maintained the best documentation to help us learn and use Vue. The Vue documentation dedicates a section on Vue 3 Migration making mention of the new features, the breaking changes for Vue 2 apps, the supporting libraries like Vue CLI, Vuex, Vue Router and others. The website explicitly states that the team is still working on a dedicated migration guide from Vue 2 to Vue 3. Meanwhile, until an official migration guide is released, let’s get a little insight on what could possibly be involved if you were to tackle this for yourself. What to consider before upgrading to Vue 3? As Vue 3 is still new, there will be some things to keep in mind. There are thousands of applications and third-party libraries created for Vue 2 and even Vue 1.5. It’s going to be a lengthy and time consuming effort to migrate all those libraries to support Vue 3. Before you attempt any migration process, make sure all the libraries you use are supported by Vue 3. For instance, Vuetify isn't. You can read more about this here. In addition, if you use any of the third-party libraries, they need to be checked or you might find they have upgraded already. Moreover, the Vue 3 reactivity system has been rewritten to utilize the new language features in ES2015. Vue 3 uses proxies for its reactivity system instead of the Object.defineProperty() function. JavaScript proxies are supported by most modern browsers. Unfortunately, Proxies cannot be polyfilled for older browsers; therefore, Vue 3 offers two implementations of it’s reactivity system. One implementation will use proxies for the most recent and modern browsers. The other one will fall back to the Vue 2 way of implementing reactivity to support the older browsers. Step by Step Migration - Demo In this section, we'll go through migrating the This Dot - Vue Meetup website. The existing website is built with Vue 2. It’s essential to follow the steps below as is. Of course, depending on the features you’ve used, you will adjust this workflow to fit your needs and migrate more features in your apps. Let's start! Step 1: Create a new Git branch It’s important to start off with a brand new branch to play around with the migration. This way your main branches, like master or dev, will remain intact without disrupting any live code. Let’s assume we are branching from the master branch. Run the following command to create a new branch: ` Step 2: Install the latest Vue CLI version Currently, as of the time of writing this article, the latest version of the Vue CLI is 4.5.6. To install the latest version, run the following command: ` Verify the installation by running the following command and making sure it reads as @vue/cli 4.5.6: ` Upgrading the Vue CLI not only helps in upgrading the existing application, but it also gives you the chance to scaffold a new Vue 3 app in the future. Step 3: Upgrade the Vue libraries The next step is to upgrade the Vue NPM packages and all other packages used inside the package.json file. To start with, open the package.json file and make sure to amend the Vue libraries with the following versions: ` Now, let’s upgrade the rest of the libraries using the Vue CLI. Run the following command to start upgrading the libraries: ` The command goes through all the libraries you are using inside the package.json file and tries to upgrade them to the latest compatible version. For example, when I run this command, the Vue CLI detects the Vue libraries that need to be upgraded and prompts for confirmation: Type Y to continue with the upgrade. In summary, the upgrade reported the following packages changes, additions, and removal. While the CLI is upgrading the @vue/cli-plugin-eslint it will also upgrade the current ESLint version installed on your computer. The latest Vue CLI supports ESLint v6. Once again, the Vue CLI prompts for confirmation before upgrading ESLint. Type Y to continue with the upgrade. ` The numbers will definitely be different for you and depending on the app. It’s now time to run the app and make sure you don’t have missing libraries or other problems. In my case, I ran the app and got a few ESLint issues. Luckily, the Vue CLI comes packaged with a command to auto fix ESLint issues. In this case, you run the npm run lint and the Vue CLI handles the rest for you! Step 4: Add the @vue/compiler-sfc NPM package The @vue/compiler-sfc package contains lower level utilities that you can use if you are writing a plugin / transform for a bundler or module system that compiles Vue single file components into JavaScript. It is used in the vue-loader. This is an essential component if you are using Single File Components which is the case in most of the Vue apps. Let’s install this package by running the following command: ` Let’s move on and start upgrading the source code to use the latest APIs offered by Vue 3. Step 5: Upgrade the main.js file Vue 3 changes the way an application is created by introducing the createApp() function. Back in Vue 2 and earlier versions, we used to create a global Vue instance. This approach had several disadvantages. The main one had third-party libraries to making changes to our Vue single App instance. By introducing createApp(), you can instantiate multiple Vue apps side by side. It creates a context or boundary for your app instance where you do all the registration as you will see shortly. Typically, a Vue app is started inside the main.js file. Let’s visit this file and make the necessary changes to upgrade to Vue 3. ` This is a slimmed down version of the original main.js file in the app. Let’s dissect the file one line at a time and upgrade accordingly. ` Replace the line above with: ` Let’s replace the code that’s creating the app using the Vue 3 createApp() function. ` Replace with: ` The app variable now holds a new Vue app instance for us. The router instance will be registered separately. Let’s update the Vue component registration. ` Replace with: ` With Vue 3, you register components at the app instance level and not globally. Let’s update the Vue mixin registration. ` Replace with: ` Now register the router on the app instance as follows: ` Now let’s register the vue-analytics plugin on the app instance. ` Replace with: ` The plugin is now installed on the app instance rather than the global Vue instance. This is also valid for any other plugin out there. Make sure to remove the line below as it’s not needed anymore in Vue 3 apps: ` Finally, let’s mount the app instance by using the following: ` The final version of the upgrade main.js file looks like this: ` That’s it! Step 6: Upgrade the router.js file The Vue Router has undergone changes and it’s now under v4.0. Let’s review what the current router.js file looks like: ` Replace the import statements with the following line: ` Instead of creating a new instance of the Router object, we will be using the new function provided by Vue Router which is createRouter(). ` The router.js file now exports an instance of the Router object using the createRoute() function. This function expects an input parameter of type object. The routes and history properties are the minimum accepted to pass into this object. The routes array is still the same as in Vue 2. It’s an array of routes, nothing has changed here. The createWebHashHistory() function is now used to specify a Hash History mode in the Vue Router. As a side note, depending on what you are using in your app, there is also the createWebHistory() function that sets the mode to HTML 5. You can read more about History Modes. Next, we will update the scrollBehavior() function as it has undergone some major changes. Replace the existing function with the following: ` You can read more about Scroll Behavior in the Vue Router 4.0. Now, let’s run the app and see if everything works as expected. When I run the app, I get the following warning in the Dev Tools: ` This warning has to do with Vue Router and the Transition component. You can read more about the Transitions in Vue Router. Let’s navigate to the App.vue component and check what the current source code is: ` In Vue Router v4.0, you can no longer nest a component inside a component. The fix is simple and provided to you in the documentation. Replace the above with the following: ` These were all the steps needed to upgrade the Vue Meetup app. Others I’d like to draw your attention to a few more things when upgrading your apps to Vue 3. One of the components in the app had a single slot; that is, the default slot. The way it was used in the Vue 2 app was: ` When I ran the app, the component was showing nothing, an empty screen! It seems Vue 2 was more tolerant by not forcing me to specify the name of the slot, even though this is the default slot. The quick fix in Vue 3 is as follows: ` Something else I didn’t mention is the Vuex v4.0. The same steps that we followed to upgrade the Vue Router can be followed here. The approach is similar. You can read more about the Vuex v4.0 Breaking Changes. Conclusion I am pretty sure we will all face more issues and encounter different hiccups while upgrading our apps. It will all depend on the features of your Vue 2. Remember, everything has a solution! While we wait for the Vue team to share an official migration guide, start trying to upgrade and see how you go. If you get stuck, feel free to drop me a message on twitter using my Twitter handle @bhaidar....

Implementing Dynamic Types in Docusign Extension Apps cover image

Implementing Dynamic Types in Docusign Extension Apps

Implementing Dynamic Types in Docusign Extension Apps In our previous blog post about Docusign Extension Apps, Advanced Authentication and Onboarding Workflows with Docusign Extension Apps, we touched on how you can extend the OAuth 2 flow to build a more powerful onboarding flow for your Extension Apps. In this blog post, we will continue explaining more advanced patterns in developing Extension Apps. For that reason, we assume at least basic familiarity with how Extension Apps work and ideally some experience developing them. To give a brief recap, Docusign Extension Apps are a powerful way to embed custom logic into Docusign agreement workflows. These apps are lightweight services, typically cloud-hosted, that integrate at specific workflow extension points to perform custom actions, such as data validation, participant input collection, or interaction with third-party services. Each Extension App is configured using a manifest file. This manifest defines metadata such as the app's author, support links, and the list of extension points it uses (these are the locations in the workflow where your app's logic will be executed). The extension points that are relevant for us in the context of this blog post are GetTypeNames and GetTypeDefinitions. These are used by Docusign to retrieve the types supported by the Extension App and their definitions, and to show them in the Maestro UI. In most apps, these types are static and rarely change. However, they don't have to be. They can also be dynamic and change based on certain configurations in the target system that the Extension App is integrating with, or based on the user role assigned to the Maestro administrator on the target system. Static vs. Dynamic Types To explain the difference between static and dynamic types, we'll use the example from our previous blog post, where we integrated with an imaginary task management system called TaskVibe. In the example, our Extension App enabled agreement workflows to communicate with TaskVibe, allowing tasks to be read, created, and updated. Our first approach to implementing the GetTypeNames and GetTypeDefinitions endpoints for the TaskVibe Extension App might look like the following. The GetTypeNames endpoint returns a single record named task: ` Given the type name task, the GetTypeDefinitions endpoint would return the following definition for that type: ` As noted in the Docusign documentation, this endpoint must return a Concerto schema representing the type. For clarity, we've omitted most of the Concerto-specific properties. The above declaration states that we have a task type, and this type has properties that correspond to task fields in TaskVibe, such as record ID, title, description, assignee, and so on. The type definition and its properties, as described above, are static and they never change. A TaskVibe task will always have the same properties, and these are essentially set in stone. Now, imagine a scenario where TaskVibe supports custom properties that are also project-dependent. One project in TaskVibe might follow a typical agile workflow with sprints, and the project manager might want a "Sprint" field in every task within that project. Another project might use a Kanban workflow, where the project manager wants a status field with values like "Backlog," "ToDo," and so on. With static types, we would need to return every possible field from any project as part of the GetTypeDefinitions response, and this introduces new challenges. For example, we might be dealing with hundreds of custom field types, and showing them in the Maestro UI might be too overwhelming for the Maestro administrator. Or we might be returning fields that are simply not usable by the Maestro administrator because they relate to projects the administrator doesn't have access to in TaskVibe. With dynamic types, however, we can support this level of customization. Implementing Dynamic Types When Docusign sends a request to the GetTypeNames endpoint and the types are dynamic, the Extension App has a bit more work than before. As we've mentioned earlier, we can no longer return a generic task type. Instead, we need to look into each of the TaskVibe projects the user has access to, and return the tasks as they are represented under each project, with all the custom fields. (Determining access can usually be done by making a query to a user information endpoint on the target system using the same OAuth 2 token used for other calls.) Once we find the task definitions on TaskVibe, we then need to return them in the response of GetTypeNames, where each type corresponds to a task for the given project. This is a big difference from static types, where we would only return a single, generic task. For example: ` The key point here is that we are now returning one type per task in a TaskVibe project. You can think of this as having a separate class for each type of task, in object-oriented lingo. The type name can be any string you choose, but it needs to be unique in the list, and it needs to contain the minimum information necessary to be able to distinguish it from other task definitions in the list. In our case, we've decided to form the ID by concatenating the string "task_" with the ID of the project on TaskVibe. The implementation of the GetTypeDefinitions endpoint needs to: 1. Extract the project ID from the requested type name. 1. Using the project ID, retrieve the task definition from TaskVibe for that project. This definition specifies which fields are present on the project's tasks, including all custom fields. 1. Once the fields are retrieved, map them to the properties of the Concerto schema. The resulting JSON could look like this (again, many of the Concerto properties have been omitted for clarity): ` Now, type definitions are fully dynamic and project-dependent. Caching of Type Definitions on Docusign Docusign maintains a cache of type definitions after an initial connection. This means that changes made to your integration (particularly when using dynamic types) might not be immediately visible in the Maestro UI. To ensure users see the latest data, it's useful to inform them that they may need to refresh their Docusign connection in the App Center UI if new fields are added to their integrated system (like TaskVibe). As an example, a newly added custom field on a TaskVibe project wouldn't be reflected until this refresh occurs. Conclusion In this blog post, we've explored how to leverage dynamic types within Docusign Extension Apps to create more flexible integrations with external systems. While static types offer simplicity, they can be constraining when working with external systems that offer a high level of customization. We hope that this blog post provides you with some ideas on how you can tackle similar problems in your Extension Apps....

Let's innovate together!

We're ready to be your trusted technical partners in your digital innovation journey.

Whether it's modernization or custom software solutions, our team of experts can guide you through best practices and how to build scalable, performant software that lasts.

Prefer email? hi@thisdot.co