How to use disconnect() to close a Uppy JS instance – Stimulus - javascript

When I click the "back button", my Uppy form is briefly loaded twice. How can I get Stimulus to close the previous Uppy instance when I leave the page?
Following the Uppy Docs, I'm hoping something like this (code below) but I get the error: uppy is nil or undefined on disconnect().
How can I bind uppy.close() to the original instance of Uppy?
import { Controller } from "#hotwired/stimulus"
import Uppy from "#uppy/core"
export default class extends Controller {
connect() {
const uppy = new Uppy()
}
disconnect() {
// This causes an error
uppy.close();
}
}

The problem is that you are trying to access uppy as a local variable that you have set in the connect method. However, methods to get get access to other methods' scope, which is why uppy is undefined when you try to access it in the disconnect method.
Instead, you have a few options.
Option 1 - Store the variable on the class instance
Each class instance has its own this variable, and you an easily store values on it as it is an object.
By convention, the below example uses this._uppy (with an underscore) but any name will work.
import { Controller } from "#hotwired/stimulus";
import Uppy from "#uppy/core";
export default class extends Controller {
connect() {
// 'store' the uppy instance against your class instance
this._uppy = new Uppy();
}
disconnect() {
// read the instance and access its close method
this._uppy.close();
}
}
Option 2 - Store the variable globally
Another way to think about this is that you do not want to create a new Uppy instance if there is already one created.
You can store this instance on window, although this breaks the concept of your file being an isolated module.
Another way is to store the instance in the module file itself, this may create some edge case issues for unit tests but see how you go.
import { Controller } from "#hotwired/stimulus";
import Uppy from "#uppy/core";
// declare a variable in the 'module' scope (this file).
let uppy;
export default class extends Controller {
connect() {
// if uppy has already been instantiated - do not create a new one
if (uppy) return;
// 'store' the uppy instance against your module scoped global variable
uppy = new Uppy();
}
disconnect() {
// read the instance and access its close method
uppy && uppy.close();
// reset the variable to null
uppy = null;
}
}

Related

Get exported singleton instance from string in javascript

I have an ExampleView class which should be singleton. Inside this class I have method render which I would like to call from another js file via object name 'ExampleView'.
Here is the code:
import View from './view';
class ExampleView extends View {
render() {
return `<span>Test</span>`;
}
}
export default new ExampleView(); //<- exporting as singleton and this object I try to get via it's name
First I should do is retrieve somehow exported singleton object ExampleView from name 'ExampleView' and then call method render.
class Test {
getInstanceOfModuleByName(name) {
//window[name].render();
}
}
The problem is that I cannot find any way to get ExampleView instance from name, but my ExampleView class needs to be a singleton.
I know I can do this in following way and then call it by window['ExampleView'].render():
ExampleView = {
render: function() {
return `<span>Test</span>`;
}
}
But I really need to implement it with modulable ES6.
Can you explain me what I should do?
Populating global scope is not the ES6 way. I think you can do it like this to your export
//
// Class definition
//
window.ExampleView = new ExampleView();
export default window.ExampleView;
You just import the instance
import whateverYouWantToCallYourViewInstance from './ExampleView'
// snip
return whateverYouWantToCallYourViewInstance.render();
It will be the same object.
Worth noting: Singletons are evil. Using the ES module system to pass instances around is a bit of abuse.

Updating my imported variable's value in React.js

I am refractoring an app I've build using React.js. I am exporting a variable from the global scope of Spotify.js and importing it in two other files App.js and Button.js.
After calling a function from Spotify.js that sotres a new value to the variable, It's new value is exported to 'Button.js' but stays an empty string in 'App.js'.
Your help would be appriciated :)
export let userAccessToken = '';
export const Spotify = {
...
getUserAccessToken (){
//stores a new string to userAccessToken.
}
}
import {userAccessToken, Spotify} from '../../util/Spotify';
export class App extends React.Component {
//a conditional rendering happens depending on if(!userAccessToken)
}
import {userAccessToken, Spotify} from '../../util/Spotify'
export class Button extends React.Component {
componentDidMount() {
if (!userAccessToken) {
console.log(`Button's UAT before calling the fn: ${userAccessToken}`)
Spotify.getUserAccessToken();
console.log(`Button's UAT after calling the fn: ${userAccessToken}`);
}
}
...
}
This is not how you share data between react components.
Use react context or pass props between components
You could use react context to share data or simply pass it as props (if the components are closely related in the component tree)
The only thing I can recommend is to export the userAccessToken, something like this, but you can't change its value outside the module
export const Spotify = {
...
getUserAccessToken (){
//stores a new string to userAccessToken.
}
}
...
}
const accessToken = Spotify.getUserAccessToken();
export const userAccessToken = accessToken;
If you got to read this question I solved it.
Turns out I should have called my method Spotify.getUserAccessToken() from App.js using the react lifecycle method componentDidMount().
The export and import methods are like snapshots of the module and therefore when I exported the variable userAccessToke from Spotify.js before calling the my method I imported an empty string and it did not update in all files.
Thanks Jørgen and joseluismurillorios for your support and time spent answering :)

Making global functions to access vuex store

so I want to make a global function that I can access in every component of mine. So I stumbled upon Vue Plugins. They worked great, till I tried out my use case. I need to use some information from the vuex store in my plugin and return a true or false value.
So this is what I have tried
plugin.js
export default {
install (Vue) {
Vue.prototype.$rbac = (method, route) => {
$store.state.generic.user.routes.some(m => m.method.includes(method) && m.route==route)
}
}
}
main.js
import plugin from './utils/common/plugin'
...
Vue.use(plugin)
...
component.vue
<template>
...
<div v-if="$plug('post', '/project')></div>
...
</template>
But I get an error saying "ReferenceError: $store is not defined".
It kind of makes sense that I cannot access the store. The store only gets the value once the user logs in.
So is there any way I can make a global function that can access the store when it gets values?
You're getting the reference error because the $store variable hasn't been defined anywhere. It's not a local variable, nor is it a function parameter or global variable.
You probably meant to do this.$store; also make sure you use function () {} syntax and not () => {} because you don't want to bind this.
export default {
install(Vue) {
Vue.prototype.$rbac = function (method, route) {
this.$store.state.generic.user.routes.some(m => m.method.includes(method) && m.route == route)
}
}
}
You could also use a global mixin to do a similar thing instead of a plugin.

(Vue) Inject variable into all components [duplicate]

I have a javascript variable which I want to pass globally to Vue components upon instantiation thus either each registered component has it as a property or it can be accessed globally.
Note:: I need to set this global variable for vuejs as a READ ONLY property
Just Adding Instance Properties
vue2
For example, all components can access a global appName, you just write one line code:
Vue.prototype.$appName = 'My App'
Define that in your app.js file and IF you use the $ sign be sure to use it in your template as well: {{ $appName }}
vue3
app.config.globalProperties.$http = axios.create({ /* ... */ })
$ isn't magic, it's a convention Vue uses for properties that are available to all instances.
Alternatively, you can write a plugin that includes all global methods or properties. See the other answers as well and find the solution that suits best to your requirements (mixin, store, ...)
You can use a Global Mixin to affect every Vue instance. You can add data to this mixin, making a value/values available to all vue components.
To make that value Read Only, you can use the method described in this Stack Overflow answer.
Here is an example:
// This is a global mixin, it is applied to every vue instance.
// Mixins must be instantiated *before* your call to new Vue(...)
Vue.mixin({
data: function() {
return {
get globalReadOnlyProperty() {
return "Can't change me!";
}
}
}
})
Vue.component('child', {
template: "<div>In Child: {{globalReadOnlyProperty}}</div>"
});
new Vue({
el: '#app',
created: function() {
this.globalReadOnlyProperty = "This won't change it";
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.1.3/vue.js"></script>
<div id="app">
In Root: {{globalReadOnlyProperty}}
<child></child>
</div>
In VueJS 3 with createApp() you can use app.config.globalProperties
Like this:
const app = createApp(App);
app.config.globalProperties.foo = 'bar';
app.use(store).use(router).mount('#app');
and call your variable like this:
app.component('child-component', {
mounted() {
console.log(this.foo) // 'bar'
}
})
doc: https://v3.vuejs.org/api/application-config.html#warnhandler
If your data is reactive, you may want to use VueX.
You can use mixin and change var in something like this.
// This is a global mixin, it is applied to every vue instance
Vue.mixin({
data: function() {
return {
globalVar:'global'
}
}
})
Vue.component('child', {
template: "<div>In Child: {{globalVar}}</div>"
});
new Vue({
el: '#app',
created: function() {
this.globalVar = "It's will change global var";
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.1.3/vue.js"></script>
<div id="app">
In Root: {{globalVar}}
<child></child>
</div>
If the global variable should not be written to by anything, including Vuejs, you can use Object.freeze to freeze your object. Adding it to Vue's viewmodel won't unfreeze it.
Another option is to provide Vuejs with a frozen copy of the object, if the object is intended to be written globally but just not by Vue: var frozenCopy = Object.freeze(Object.assign({}, globalObject))
you can use Vuex to handle all your global data
In your main.js file, you have to import Vue like this :
import Vue from 'vue'
Then you have to declare your global variable in the main.js file like this :
Vue.prototype.$actionButton = 'Not Approved'
If you want to change the value of the global variable from another component, you can do it like this :
Vue.prototype.$actionButton = 'approved'
https://v2.vuejs.org/v2/cookbook/adding-instance-properties.html#Base-Example
If you’d like to use a variable in many components, but you don’t want to pollute the global scope. In these cases, you can make them available to each Vue instance by defining them on the Vue prototype:
Vue.prototype.$yourVariable = 'Your Variable'
Please remember to add this line before creating your Vue instance in your project entry point, most of time it's main.js
Now $yourVariable is available on all Vue instances, even before creation. If we run:
new Vue({
beforeCreate: function() {
console.log(this.$yourVariable)
}
})
Then "Your Variable" will be logged to the console!
doc: https://v2.vuejs.org/v2/cookbook/adding-instance-properties.html#Base-Example
If you want to make this variable immutable, you can use the static method Object.defineProperty():
Object.defineProperty(Vue.prototype, '$yourVariable', {
get() {
return "Your immutable variable"
}
})
This method by default will prevent your variable from being removed or replaced from the Vue prototype
If you want to take it a step further, let's say your variable is an object, and you don't want any changes applied to your object, you can use Object.freeze():
Object.defineProperty(Vue.prototype, '$yourVariable', {
get() {
return Object.freeze(yourGlobalImmutableObject)
}
})
A possibility is to declare the variable at the index.html because it is really global. It can be done adding a javascript method to return the value of the variable, and it will be READ ONLY. I did like that:
Supposing that I have 2 global variables (var1 and var2). Just add to the index.html header this code:
<script>
function getVar1() {
return 123;
}
function getVar2() {
return 456;
}
function getGlobal(varName) {
switch (varName) {
case 'var1': return 123;
case 'var2': return 456;
// ...
default: return 'unknown'
}
}
</script>
It's possible to do a method for each variable or use one single method with a parameter.
This solution works between different vuejs mixins, it a really global value.
in main.js (or any other js file)
export const variale ='someting'
in app.vue (or any other component)
import {key} from '../main.js' (file location)
define the key to a variable in data method and use it.
Simply define it in vite configuration
export default defineConfig({
root:'/var/www/html/a1.biz/admin',
define: {
appSubURL: JSON.stringify('/admin')
}, ..../// your other configurations
});
Now appSubURL will be accessible everywhere

How to call a class methods in JavaScript

I am currently learning ES6. While I was playing with the new features, I got stuck at one point. How to call a class method.
So let's say I have a class in one file like below:
class Auth {
checkUserSignedIn() {
//check user signed in
}
signupUser(account) {
//signup user
}
loginUser(account) {
//login user
}
getCurentUser() {
//Current User
}
}
module.exports = Auth;
and then in some other file, let's say a controller I would like to call these functions.
const Auth = require('./auth');
class Controller {
signupUserUsingEmailAndPass(user) {
Auth.signupUser(account);
}
loginUserUsingEmailAndPass(account) {
Auth.loginUser(account);
}
isUserSignedIn() {
checkUserSignedIn();
}
}
module.exports = Controller;
But this doesn't work at all. I guess there is something I am not understanding correctly. Any suggestion/advice?
Methods defined in a class require an instance of that class, i.e. a new Auth somewhere.
Your controller should be defined has
class Controller {
constructor(auth) {
this.auth = auth;
}
}
This approach over the require('myclass') allows for you to inject different implementations of your Auth class.
Should you not desire an instance of Auth for those methods, declare them as static. More on that here
You need to either instatiate the Auth
const authorization = new Auth();
Possibly inside the file containing the Auth class, and just export the instance.
export const authorization = new Auth();
Or, if You want this methods available outside. You can make the methods static. Just add static keyword before the method name during method creation.
You can read more about static methods here
Javascript classes don't work quite like the module pattern (that it seems) you're used to. When you export `Auth, you're exporting the class definition but you still need to instantiate it.
let auth = new Auth()
auth.signupUserUsingEmailAndPass() // or any other function you define
In javascript, a class is useful when you want to populate it with data or some sort of state. If you don't want or need that then you can use a module.

Categories

Resources