share a js-class between .vue files - javascript

Back in my day we had this crazy thing called include or import or whatever. It meant that you wrote some code in one file and then reused it in different files. So you would create a class in file x, include this file in another file y and instantiate an object from this. Basically this means, that you were able to call a function form file y even though it is written in file x.
How can something like this be done in vue.js? I understand that the new hip way to do it is called "components api". Online I only found really convoluted examples that had some coupling with the dom, something I don't need.
Or should I use mixins? Mixins seem to be working at least, but they have a scope problem.
Here is the file I want to share (x.vue):
<script>
export default
{
data ()
{
return {
a: 'test'
}
}
,mounted ()
{
}
,methods:
{
test ()
{
console.log( "test : " + this.a );
}
}
}
</script>
The y.vue:
<script>
import x from './x.vue'
export default {
name:"whatever"
,components:
{
x
}
,mounted()
{
x.a = "it is working";
x.test();
}
}
</script>
The web-browser tells me that a is not a function. does anyone know where the mistake is? Or should i just create a js class and try to import that?
Thanks for any help.

I believe you're looking for mixins:
https://v2.vuejs.org/v2/guide/mixins.html
They Allow you to share functions, data, hooks and all other from another file.

This would be an approach in your case.
mixins.js:
export default {
methods: {
sayHello () {
console.log( "test : " + this.a);
}
}
};
component.vue:
<script>
import mixins from "#/path/to/mixins/mixins";
export default {
name: "whatever",
mixins: [mixins],
data ()
{
return {
a: 'HELLO'
}
},
created() {
this.sayHello();
}
}
</script>
For further information: Mixins
EDIT: Looks like there is something that matches your needs!
mixins.js:
export default {
name: 'Mixin',
methods: {
sayHello() {
console.log("Hello")
}
}
}
component.js:
export default {
name: 'whatever',
mixins: [Mixin],
created() {
this.$super(Mixin).sayHello();
}
}
EDIT2: As you requested, an simple example to pass arguments.
mixins.js:
export default {
methods: {
sayHello(foo, bar) {
console.log(foo + bar)
}
}
}
component.js:
export default {
name: 'whatever',
mixins: [Mixin],
data () {
return {
baz: 'Say'
}
},
created() {
this.sayHello(this.baz, " Hello");
}
}

Mixins are not useful in this case as there cannot be more than one instance of a mixin. It is like copy and pastes the code from the mixin into the other file.
Sharing code between Vue components can be done by many savers with standard js classes. All that is needed is that the class is declared with export default class.

Related

How to properly split a Vuejs component

So, I've got a huge Vue file which do more than 1500 lines CSS excluded.
This file is a wrapper for a lot of other components but mostly can't be breaked into smaller components.
The things is I still got some logics like groups of methods which could be extracted. However it has impact on some data or watched properties.
Is it possible to export differents parts of my file and use them as a more "global import".
Something which would be like :
// components/component.logicX.vue/js?
export default {
data() {
return {
....
}
},
methods: {
aFewMethods(){ this.data = "something" },
...
},
watch: {
...
}
}
// component/component.vue
import logicX from '.component.logicX.vue'
export default {
components: {
logicX
},
data() {
data: '' //this data is modified from a logicX component method.
}
}

Calling a method inside Vue component from an outside class

I'm pretty new to Vue/JS and am currently trying to build an app.
I currently have a component set up as follows (there's obviously more to it but hopefully the below will help with my question):
<template>...</template>
<script>
export default {
data() {
return {...}
},
methods: {
method1() {
const Class1 = new Class1;
},
method2() {
...
}
}
}
class Class1 {}
class Class2 {
...want to use above method2() here
}
</script>
<style>...</style>
Now I am able to use Class1 from inside method1() but is there any way I can easily call method2() from Class2?
Many thanks in advance.
Class with method foo as an example
export default class Class1 {
function foo() {};
}
Calling a function from another class could be like this:
import Class1 from './class1';
<template>...</template>
<script>
export default {
data() {
return {
methods: {
method1() {
const x = new Class1;
return x.foo()
}
}
}
}
}
</script>
Posting a better elaborated answer:
In vue.js you can use an Event Bus method to comunicate with unrelated components. Basically is a component that is used to pass an event to other components. It can be very useful in this case.
event-bus.js:
import Vue from 'vue';
export const EventBus = new Vue();
Component that will emit the event:
<template>
<div #click="functionToEmitTheEvent()"></div>
</template>
<script>
// Import the EventBus we just created.
import { EventBus } from './event-bus.js';
export default {
data() {
return {
clickCount: 0
}
},
methods: {
functionToEmitTheEvent() {
this.clickCount++;
// Send the event on a channel (customClick) with a payload (the click count.)
EventBus.$emit('customClick', this.clickCount);
}
}
}
</script>
Script that will listen the event:
// Import the EventBus.
import { EventBus } from './event-bus.js';
// Listen for the customClick event and its payload.
EventBus.$on('customClick', clickCount => {
console.log(`Oh, that's nice. It's gotten ${clickCount} clicks! :)`)
});
All code written was copied from here: https://alligator.io/vuejs/global-event-bus/
I hope it helped!
Create Class1 and Class2 outside of your component as a ES6 class and export them. Then import the classes to your component. So something like this:
Class 1
export default class Class1 {
...
...
}
Class 2
export default class Class2 {
...
...
}
And then import those classes to your Vue component
<template>...</template>
<script>
import Class1 from './class1';
import Class2 from './class2';
export default {
...
data() {
return {
// your data
}
},
methods: {
method1() {
const Class1 = new Class1();
},
method2() {
const Class2 = new Class2();
..
return 'something';
},
method3() {
// call method 2
method2();
....
}
...
....
Thanks for the answers all - think my original question must not have been clear but I've managed to find the solution (stumbled across just now, after spending hours last night searching!) Here it is for anyone else wondering.
To make use of a method outside of a component, register the Vue instance globally when created (e.g. in main.js):
app = new Vue({
...
})
window.App = app;
And then any methods can be called by referencing the window.App e.g.
App.method2();
Full working code:
<template>...</template>
<script>
export default {
data() {
return {...}
},
methods: {
method1() {
const Class1 = new Class1;
},
method2() {
...
}
}
}
class Class1 {}
class Class2 {
App.method2();
}
</script>
<style>...</style>

How can I export all functions from a file in JS?

I'm creating a unit converter, and I want to put all of the conversion functions into their own file. Using ES6 export, is there any way to export all of the functions in the file with their default names using only one line? For example:
export default all;
The functions are all just in the file, not within an object.
No, there's no wildcard export (except when you're re-exporting everything from another module, but that's not what you're asking about).
Simply put export in front of each function declaration you want exported, e.g.
export function foo() {
// ...
}
export function bar() {
// ...
}
...or of course, if you're using function expressions:
export var foo = function() {
// ...
};
export let bar = () => {
// ...
};
export const baz = value => {
// ...
};
I think there are a lot of solutions to this. And as has been answered, there's no wildcard export. But, you can 'wildcard' the import. So, I much prefer the one putting export before each of the functions you want to expose from the file:
//myfile.js
export function fn1() {...}
export function fn2() {...}
and then import it like so:
import * as MyFn from './myfile.js'
Afterwards you could use it like so:
MyFn.fn1();
MyFn.fn2();
You can also use module.exports as follows:
function myFunction(arg) {
console.debug(arg);
}
function otherFunction(arg) {
console.error(arg);
}
module.exports = {
myFunction: myFunction,
otherFunction: otherFunction,
};
Then you can import it:
import {myFunction, otherFunction} from "./Functions.js";
In my use case, I do have three reusable functions in one file.
utils/reusables.js
export const a = () => {}
export const b = () => {}
export const c = () => {}
In order to point the root folder instead of individual file names, I created a file called index.js which will comprise of all the functions that are listed in individual files.
utils/index.js
export * from './reusables'
Now, when I want to use my a function, I will have to simply import it like this
import { a } from '../utils'
Rather than calling it from its individual files
import { a } from '../utils/reusables'
You could also export them at the bottom of your script.
function cube(x) {
return x * x * x;
}
const foo = Math.PI + Math.SQRT2;
var graph = {
options: {
color:'white',
thickness:'2px'
},
draw: function() {
console.log('From graph draw function');
}
}
export { cube, foo, graph };
You can also aggregate submodules together in a parent module so that they are available to import from that module.
// In parentModule.js
export { myFunction, myVariable } from 'childModule1.js';
export { myClass } from 'childModule2.js';
// In top-level module
import { myFunction, myVariable, myClass } from 'parentModule.js'
For Node.js environment, what I did to export functions was this.
UserController.js
module.exports = {
signUp: () => {
return "user"
},
login: () => {
return "login"
}
}
UserRouter.js
const UserController = require('./UserController')
then login and signUp functions could be used inside UserRouter as UserController.signUp() and UserController.login()
I think there's a missing common solution, which is exporting in index.js file:
myModule/myFunctions.js
export const foo = () => { ... }
export const bar = () => { ... }
then in myModule/index.js
export * from "./myFunctions.js";
This way you can simply import and use it with:
import { foo, bar } from "myModule";
foo();
bar();
functions.js
function alpha(msj) {
console.log('In alpha: ' + msj);
}
function beta(msj) {
console.log('In beta: ' + msj);
}
module.exports = {
alpha,
beta
};
main.js
const functions = require('./functions');
functions.alpha('Hi');
functions.beta('Hello');
Run
node main.js
Output
In alpha: Hi
In beta: Hello
In case anyone still needs an answer to this in modern JavaScript:
const hello = () => "hello there"
const bye = () => "bye bye"
export default { hello, bye }
What I like to do is to export all functions within an object:
//File.js
export default {
testFunction1: function testFunction1(){
console.log("Hello World")
},
//a little bit cleaner
testFunction2: () => {
console.log("Nothing here")
}
}
Now you can access the functions with calling the key value of the object:
//differentFile.js
import file from 'File.js'
file.testFunction1()
//Hello World
file.testFunction2()
//Nothing here

Pass prop as module name when mapping to namespaced module

I'm trying to pass the store module namespace via props to a component. When I try and map to getters with the prop, it throws this error,
Uncaught TypeError: Cannot convert undefined or null to object
If I pass the name as a string it works.
This Works
<script>
export default {
props: ['store'],
computed: {
...mapGetters('someString', [
'filters'
])
}
}
</script>
This does not work
this.store is defined
this.store typeof is a String
<script>
export default {
props: ['store'],
computed: {
...mapGetters(this.store, [
'filters'
])
}
}
</script>
I used this style utilising beforeCreate to access the variables you want, I used the props passed into the component instance:
import { createNamespacedHelpers } from "vuex";
import module from '#/store/modules/mymod';
export default {
name: "someComponent",
props: ['namespace'],
beforeCreate() {
let namespace = this.$options.propsData.namespace;
const { mapActions, mapState } = createNamespacedHelpers(namespace);
// register your module first
this.$store.registerModule(namespace, module);
// now that createNamespacedHelpers can use props we can now use neater mapping
this.$options.computed = {
...mapState({
name: state => state.name,
description: state => state.description
}),
// because we use spread operator above we can still add component specifics
aFunctionComputed(){ return this.name + "functions";},
anArrowComputed: () => `${this.name}arrows`,
};
// set up your method bindings via the $options variable
this.$options.methods = {
...mapActions(["initialiseModuleData"])
};
},
created() {
// call your actions passing your payloads in the first param if you need
this.initialiseModuleData({ id: 123, name: "Tom" });
}
}
I personally use a helper function in the module I'm importing to get a namespace, so if I hadmy module storing projects and passed a projectId of 123 to my component/page using router and/or props it would look like this:
import { createNamespacedHelpers } from "vuex";
import projectModule from '#/store/project.module';
export default{
props['projectId'], // eg. 123
...
beforeCreate() {
// dynamic namespace built using whatever module you want:
let namespace = projectModule.buildNamespace(this.$options.propsData.projectId); // 'project:123'
// ... everything else as above with no need to drop namespaces everywhere
this.$options.computed = {
...mapState({
name: state => state.name,
description: state => state.description
})
}
}
}
Hope you find this useful.
I tackled this problem for hours, too. Then I finally came up with one idea.
Add attachStore function in a child vue component. A function nama is not important. Any name is ok except vue reserved word.
export default {
:
attachStore (namespace) {
Object.assign(this.computed, mapGetters(namespace, ['filters']))
}
}
When this vue component is imported, call attachStore with namespace parameter. Then use it at parent components attributes.
import Child from './path/to/child'
Child.attachStore('someStoresName')
export default {
name: 'parent',
components: { Child }
:
}
The error you're encountering is being thrown during Vue/Vuex's initialization process, this.store cannot be converted because it doesn't exist yet. I haven't had to work with namespacing yet, and this is untested so I don't know if it will work, but you may be able to solve this problem by having an intermediary like this:
<script>
export default {
props: ['store'],
data {
namespace: (this.store !== undefined) ? this.store : 'null',
},
computed: {
...mapGetters(this.namespace, [
'filters'
])
}
}
</script>
That ternary expression will return a string if this.store is undefined, if it isn't undefined then it will return the value in this.store.
Note that there is also a discussion about this on Vue's Github page here: https://github.com/vuejs/vuex/issues/863
Until Vue formally supports it, I replaced something like
...mapState({
foo: state => state.foo
})
with
foo () {
return this.$store.state[this.namespace + '/foo'] || 0
}
Where namespace is passed to my child component using a prop:
props: {
namespace: { type: String, required: true }
}

ES6 circular dependency

This is an issue I run into fairly frequently, and I was hoping to discover the correct way to handle it.
So I have a setup like this:
parent.js:
export default {
x: 1
}
a.js:
import parent from 'parent.js'
export default parent.extend(a, { title: 'a' })
b.js:
import parent from 'parent.js'
export default parent.extend(b, { title: 'b' })
Cool, now I've got some children.
But I decide I would like to have a function in parent.js that checks if an object is an instance of a or b.
So I might do this:
parent.js:
import a from 'a'
import b from 'b'
export default {
x: 1,
checkType (obj) {
if (obj instanceof a) {
return 'a'
} else if (obj instanceof b) {
return 'b'
}
}
}
Well now that's a circular dependency. Is there an elegant way to handle this?
Having logic in the parent class that is aware of the subclasses is a serious anti-pattern. Instead, add methods in the subclasses that return the type. for instance, in a.js:
import parent from 'parent.js';
export default parent.extend(a, { title: 'a', checkObj() { return 'a'; }});
If the desired return from checkObj is always the value of the title property, then of course just:
// parent.js
export default {
x: 1,
checkObj() { return this.title; }
}
I don't know exactly what extend is doing here. I'm assuming it is some kind of subclassing mechanism.
In general, circular import dependencies, although there are ways to deal with them when truly necessary, are the universe trying to tell you that there is something wrong with the way you've structured your code.
If you're able to use es6 classes, then you can take advantage of the super() call in the constructor. I'll often do something like this:
Parent.js
export default class {
constructor(options, child) {
this.child = child;
this.x = 1;
}
checkType() {
return this.child;
}
}
A.js
import Parent from './Parent';
export default class extends Parent {
constructor(options) {
super(options, 'a');
}
}
B.js
import Parent from './Parent';
export default class extends Parent {
constructor(options) {
super(options, 'b');
}
}
If you don't want to use classes, maybe want a more FP style. You could make parent a function:
parent.js
export default function(child) {
return {
x: 1,
checkType (obj) {
return child;
}
extend (something) {
// assuming the returns something as you said
}
}
}
a.js
import parent from 'parent.js'
export default parent('a').extend(a, { title: 'a' })
b.js
import parent from 'parent.js'
export default parent('b').extend(b, { title: 'b' })

Categories

Resources