Create a Vue extention with personalized settings

Looking to create a Vue plugin with custom options? Following the official Vue guidelines at https://v2.vuejs.org/v2/guide/plugins.html, but struggling to define those custom options that can be accessed by regular JavaScript which then exports an object used in a Vue component.

This is how my folder structure looks:

/src
    factory.js
    CustomComponent.vue

In the factory.js file:

import Vue from "vue";
import ImgixClient from "imgix-core-js";

var imgixClient = new ImgixClient({

  domain: CUSTOM_OPTION_URL <-- important bit
  domain: Vue.prototype.$imgixBaseUrl //tried it like this
});

export { imgixClient };

I've attempted setting up this custom option using Vue.prototype in the install method as shown below, but have been unsuccessful in getting it to work:

export function install(Vue, options) {
  if (install.installed) return;
  install.installed = true;
  Vue.prototype.$imgixBaseUrl = options.baseUrl;
  Vue.component("CustomComponent", component);
}

Answer №1

Unfortunately, the answer to your question is not as simple as you may have hoped. There are several complexities that need to be addressed.

Let's begin with factory.js. It's important to note that this is not a factory, but rather a singleton. Singletons can present challenges related to dependencies, configuration, and instantiation timing, which seems to be the issue at hand here. I will delve deeper into this shortly.

Now, turning our attention to the plugin. Firstly, let's examine these two lines of code:

if (install.installed) return;
install.installed = true;

This should not be necessary as Vue handles this automatically to ensure that your plugin is only installed once. It's possible that this practice originated from an outdated tutorial. A look at the source code for Vue.use reveals that the process is quite straightforward:

https://github.com/vuejs/vue/blob/4821149b8bbd4650b1d9c9c3cfbb539ac1e24589/src/core/global-api/use.js

Exploring the Vue source code can be beneficial. While it may seem daunting at first, there are aspects like this one that are relatively easy to follow. As you become more familiar with it, even the more complex sections start to make sense.

Regarding the plugin:

Vue.prototype.$imgixBaseUrl = options.baseUrl;

The reason for adding this to the prototype is not clear. Assuming you are already acquainted with how JavaScript function prototypes operate.

Component instances essentially derive from Vue. Therefore, any properties added to Vue.prototype are inherited by your components with minimal overhead. Consider the following basic component:

<template>
  <div @click="onClick">
    {{ $imgixBaseUrl }}
  </div>
</template>
<script>
export default {
  methods: {
    onClick () {
      const url = this.$imgixBaseUrl

      // ...
    }
  }
}
</script>

Since $imgixBaseUrl is an inherited property, it can be accessed within onClick using this.$imgixBaseUrl. Additionally, templates resolve identifiers as properties of the current Vue instance, so {{ $imgixBaseUrl }} will also access this.$imgixBaseUrl.

If the $imgixBaseUrl is not required within a component, there is no need to place it on the Vue prototype. In such cases, it can be directly assigned to Vue:

Vue.imgixBaseUrl = options.baseUrl;

In the above code snippet, the dollar sign ($) has been removed since there is no risk of conflicting with component instance properties when using the prototype.

Returning to the main issue:

As mentioned earlier, singletons pose significant challenges regarding creation timing and configuration. Vue offers its own solution for 'do it once at the start' scenarios through plugins. The key advantage of plugins is that they remain inactive until install is called, allowing control over the timing.

The issue with your original code lies in the fact that the contents of factory.js execute as soon as the file is imported. This occurs before your plugin is installed, so Vue.prototype.$imgixBaseUrl remains unset. Consequently, the ImgixClient instance is created immediately without waiting for utilization. Even if Vue.prototype.$imgixBaseUrl is set subsequently, it will have no impact as it is too late.

One approach (though not necessarily ideal) to address this would involve lazily instantiating ImgixClient. This implementation could resemble the following:

import Vue from "vue";
import ImgixClient from "imgix-core-js";

var imgixClient = null;

export function getClient () {
  if (!imgixClient) {
    imgixClient = new ImgixClient({
      domain: Vue.prototype.$imgixBaseUrl
    });
  }

  return imgixClient;
}

Assuming getClient() isn't invoked prior to installing the plugin, this method should work. However, ensuring this condition is met poses a challenge. Aside from the temporal coupling, sharing configuration through Vue creates direct coupling which may not be favourable. Although segregating ImgixClient instantiation code into a distinct file aligns logically, it only holds up if independent of Vue.

Alternatively, relocating the instantiation logic within the plugin might be preferable. The revised code structure could resemble the following:

import ImgixClient from "imgix-core-js";

export default {
  install (Vue, options) {
    Vue.imgixClient = Vue.prototype.$imgixClient = new ImgixClient({
      domain: options.baseUrl
    });

    Vue.component("CustomComponent", component);
  }
}

A few cosmetic changes have been made, utilizing a default export and encapsulating the function in an object. These adjustments can be disregarded if the original format is preferred.

If the client is required within a component, it can be accessed via the $imgixClient property inherited from the prototype. For other code snippets requiring access to the client, retrieval can be facilitated either through the component or directly from Vue.imgixClient. If neither scenario applies, the relevant section of the plugin can be omitted.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Express: ERROR_HTTP_HEADERS_ALREADY_SENT - It is not possible to set headers once they have already been sent to the client

After reviewing numerous discussions on the same topic, I am struggling to identify where I might be sending the headers initially. The stack trace provided indicates the following: It is concerning that I receive a 204 as it successfully adds to the data ...

Is there a way to selectively display items that are grouped with children only?

I am currently experimenting with Vuetify and exploring the usage of v-list-group. I am curious to know if there is a way to prevent the grouping behavior for items that do not have any children? <template> <v-layout fill-height> ...

Extract content from whole webpage including HTML, CSS, and JavaScript

Currently I am working on developing a webpage version control backup/log system. The goal is to automatically save a static copy of the webpage, including all its CSS and JavaScript files, whenever there are any changes made. I have already figured out h ...

Discover the nearest locations along your route using Google Maps API V3's boundary feature

I am trying to find locations that fall within a specific boundary along a route. I need the results to be ordered by distance from the route. I attempted to use rankby=distance in my Nearby Search request, but it didn't work because it requires a lo ...

What is the best way to send a prop to my home route following a redirect?

I am working with react-router-dom and I want to pass :id to my first route (/) when redirecting. This is important so that I can access :id in my Interface component and maintain consistent URL structure for my single-page application. Is it feasible to a ...

use JavaScript to automatically select checkboxes based on their value

I've been facing a challenge for the past week with one particular issue. I have two arrays and I'm trying to automatically select checkboxes based on the values stored in one of the arrays. Initially, I use a loop to generate checkboxes based o ...

Retrieve the child property of an object directly without referencing the parent property

When using html2json, it returns an object with child objects. The challenge is to retrieve the value of the key "text", which can be located in different places depending on how many child objects there are. I have attempted the following code, but due t ...

When utilizing TinyMCE in numerous Vuetify dialogs, the editor appears completely empty

My challenge is using TinyMCE in multiple dialogs, as switching dialogs causes the TinyMCE editor to become blank and uneditable. To showcase the issue I'm facing, I have prepared a demo accessible here: https://codesandbox.io/s/tinymce-vue-demo-fork ...

Guide on setting up the Facebook Messenger customer chat SDK in Nuxt and Vue

Recently, I attempted to integrate the Facebook Messenger customer chat SDK into my Nuxt app. Potential Solution 1 (0% success): I experimented with the https://www.npmjs.com/package/vue-fb-customer-chat package but encountered issues. The package' ...

My variable calculations just don't add up correctly

I'm struggling to keep track of the count in my var counts within the if statement - can anyone assist me with this? Laser.prototype.update = function () { //.3 this.rot += .3 this.pos.add(this.dir.clone().mult(5)); this.alive = !(th ...

Having trouble with Ajax Cross Domain functionality?

Similar Question: Ajax cross domain call Using the following code... var URLs = new Array(); var titulo = new Array(); $.ajax({ url: 'http://www.example.com.br', type: 'GET', success: functio ...

React Material-ui Dropdown Component

Once I completed my application, I decided to enhance its appearance using Material UI. During the transition from HTML to Material UI, a warning started appearing when selecting an item: index.js:1 Warning: findDOMNode is deprecated in StrictMode. findDO ...

Unable to completely conceal the borders of Material UI Cards

Despite my efforts to blend the card with the background, I am still struggling with the tiny exposed corners. I've spent hours searching for a solution, but nothing seems to work. I've tried various methods such as adjusting the border radius in ...

What is the best way to combine an array with an array of objects?

I'm working with two arrays in my code: one is called Indicators and the other is Departments. My task is to link each department to every indicator. So, if there are 4 departments, I need to create 4 indicators, each with a different department but ...

Issue with React hook state persistence in recursive function

I implemented a recursion custom hook that utilizes a setTimeout function to provide 3 chances for an operation. Once the chances run out, the recursion should stop. However, I encountered an issue where the setTimeout function is not properly decrementin ...

Sending a JSON array from an Ajax request to a Spring controller: A step-by-step guide

My HTML table data is converted to JSON format and sent via AJAX to a Spring controller. In the controller, I used @RequestParam Map<String, String> to retrieve the values, but the entire JSON string was received as the only key. I cannot use a model ...

Issue encountered while attempting to utilize the concat method to condense an array

Describing the Problem: I am facing a challenge with flattening an array of sales data. Each element in the array contains an ID, sale code, seller username, timestamp, and details which include an array of products, quantities, and subtotals for each item ...

Explaining the functionality of parentheses {} within a promise object in React

In my current project, I have implemented a simple React component. Here is the code snippet: import React from 'react'; export default class UserProfile extends React.Component { constructor(props) { super(props); ...

Table header sticking does not work when overflow is set to scroll or auto

After trying numerous solutions without success, I am reposting this question. My table has a horizontal scroll and I attempted to make the table header sticky using position:sticky, but it caused the scrolling functionality to stop working. Is there a wa ...

What are the steps for transmitting an array of data to Parse Cloud Code?

Trying to send an array of contact emails as a parameter in Cloud Code function for Parse, here is how I am doing it: HashMap<String, ArrayList<String>> params = new HashMap<>(); ArrayList<String> array = new ArrayList<>(); a ...