I want to make separate files for data and methods, and access them in a third file where I could just display them. There will be a lot of variables and math going on, and keeping everything in a single Vue file makes the code hard to read. That kind of 'data-store' for both variables and functions would help me keep my code clean. Thank you in advance!
One thing I've done in the past is put those files in my assets folder. For example, I have a static set of data in a json file I need in my component. I do the following:
<template>
<div></div>
</template>
<script>
export default {
name: 'FetchData',
data() {
return {
retrievedData: require('../assets/data.json'),
}
},
created() {
this.emitFormattedData(this.retrievedData);
}
methods: {
emitFormattedData(rawData) {
const formattedData = this.formatData(rawData);
this.$emit('fetch-products-event', formattedData);
},
formatData(rawData) {...}
}
}
You could do the same for variables by using exportable functions (I dont think you can export variables).
// helper js file
export function myImportantMathVariable() {
//return some value
}
and in your Vue file
<script>
import {myImportantMathVariable} from '../assets/helper.js';
export default {
name: 'MyAwesomeMathComponent,
data() {
return {
someProp: number
}
},
methods: {
someSuperMethod() {
return 5 + myImportantMathVariable();
}
}
}
...
Related
I can't understand why data passed as props is not updating when data changes. I tried different ways, but for some reason, the frontend knows nothing about data manipulation. Here is my code base:
For Loop
<SingleElement v-for="addon of addons" :key="addon.id"
:id="addon.id"
:title="addon.name"
:description="addon.description"
:active="addon.active" // This is the part I need to update
></SingleElement>
The SingleElement Template
<template>
<div class="column is-4">
<b-field>
<b-switch v-model="isActive"></b-switch>
</b-field>
</div>
</template>
<script>
import { store, methods } from '../../../store';
export default {
name: "single-element",
props: {
id: Number,
title: String,
description: String,
active: Boolean,
docURL: String,
category: String
},
data() {
return {
"isActive" : this.active
}
},
}
</script>
The Store
import axios from "axios";
export const store = Vue.observable({
"addons": [],
"activatedAddons": [],
"settings": {}
});
export const methods = {
activateAllAddons() {
store.activatedAddons = [];
for (let addon of store.addons) {
store.activatedAddons.push(addon.id);
addon.active = true;
}
},
deactivateAllAddons() {
store.activatedAddons = [];
for (let addon of store.addons) {
addon.active = false;
}
},
}
The process in short: On click, I call the store method activateAllAddons or deactivateAllAddons. These methods are changing the store.activatedAddons and store.addons data. For example I change the active boolean. But my switch do not react to this changes. Why?
*What I tried
I tried to change the data in different ways.
Way 1
-> Changing the addon boolean in the store
:active="addon.active"
Way 2
-> Creating an additional method to check the status:
:active="isActive(addon)"
and:
isActive(addon) {
return store.activatedAddons.includes(addon.id);
}
But no way. The frontend seems to know nothing about the changes. When I log the variables in the console, the changes definitely are there. What's the problem here?
I have two modules. One load statically, the other dynamically.
StaticLoadingStore.js:
export default {
namespaced: false,
state() {
return {
propertySL: 'Some value from a statically loaded module',
}
},
getters: {
getPropertySL(state) {
return state.propertySL
},
},
}
DynamicLoadingStore.js
export default {
namespaced: true,
state() {
return {
propertyDL: 'Some value from a dynamically loaded module',
}
},
getters: {
getPropertyDL(state) {
return state.propertyDL
},
},
}
Dynamically loaded module shows that it is empty. Why?
HelloWorld.vue:
<template>
<div>
<h1>SL</h1>
<h5>propertySL:</h5>
<p>{{ propertySL }}</p>
<h5>stateSL:</h5>
<code>{{stateSL}} </code>
<h1>DL</h1>
<h5>propertyDL:</h5>
<p>{{ propertyDL===undefined?'undefined':propertyDL }}</p>
<!-- return undefined -->
<h5>stateDL:</h5>
<code>{{stateDL}} </code>
<!-- return {} -->
</div>
</template>
<script>
import SLModule from '../StaticLoadingStore'
const DLModule = () => import('../DynamicLoadingStore.js');
export default {
data: () => ({
stateSL: '',
stateDL: '',
}),
computed: {
propertySL() {
return this.$store.getters['getPropertySL']
},
propertyDL() {
return this.$store.getters['dlModule/getPropertyDL']
},
},
created() {
this.$store.registerModule('slModule', SLModule);
this.stateSL = JSON.stringify(this.$store.state['slModule'], null, 2);
this.$store.registerModule('dlModule', DLModule());
this.stateDL = JSON.stringify(this.$store.state['dlModule'], null, 2);
}
}
</script>
My knowledge in vue and js is very limited, and I ask the question through Google translator, so I apologize in advance for incompetence.
Without waiting for an answer, he began to experiment.
That's how it worked.
DynamicLoadingStore.js
...
async created() {
const moduleLoader = await DLModule();
this.$store.registerModule('dlModule', moduleLoader.default);
...
But why this is not as recommended in the examples is not clear.
New problem. Reactivity does not work. alert(this.$store.getters['dlModule/getPropertyDL'])
gives expected data.
But the propertySL in template is empty. Tell me what's wrong, please.
But why this is not as recommended in the examples is not clear.
If you talking about this official guide Dynamic Module Registration. I think the author doesn't want to specify how to get the module since there are a lot of ways to do.
In your example I think both modules should call dynamic module, static module is the module that declared at store creation.
But you import it with different methods which are static import and dynamic import. You can read more about import from MDN.
To use dynamic import, there is no need to wrap import statement with function:
...
await import('../DynamicLoadingStore.js')
...
...
// This will useful when you use dynamic component
() => import('../DynamicLoadingStore.js')
...
New problem. Reactivity does not work.
alert(this.$store.getters['dlModule/getPropertyDL']) gives expected
data.
But the propertySL in template is empty. Tell me what's wrong, please.
If you register slModule before dlModule, the propertySL should still work fine but not propertyDL.
The reason is this is the how computed property works, since you are using async created instead of created, the computed property doesn't wait until async created finished. So when Vue try to compute the dependency of the property it cannot do correctly because your getters will return undefined.
You can solve this problem by use another data to trigger computed property to recompute like this:
this.dlModuleReady && this.$store.getters["dlModule/getPropertyDL"];
See example.
In almost all guides, tutorial, posts, etc that I have seen on vuex module registration, if the module is registered by the component the createNamespacedHelpers are imported and defined prior to the export default component statement, e.g.:
import {createNamespacedHelpers} from 'vuex'
const {mapState} = createNamespacedHelpers('mymod')
import module from '#/store/modules/mymod'
export default {
beforeCreated() {
this.$store.registerModule('mymod', module)
}
}
this works as expected, but what if we want the module to have a unique or user defined namespace?
import {createNamespacedHelpers} from 'vuex'
import module from '#/store/modules/mymod'
export default {
props: { namespace: 'mymod' },
beforeCreated() {
const ns = this.$options.propData.namespace
this.$store.registerModule(ns, module)
const {mapState} = createNamespacedHelpers(ns)
this.$options.computed = {
...mapState(['testVar'])
}
}
}
I thought this would work, but it doesnt.
Why is something like this needed?
because
export default {
...
computed: {
...mapState(this.namespace, ['testVar']),
...
},
...
}
doesnt work
This style of work around by utilising beforeCreate to access the variables you want should work, I did this from the props passed into your 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 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
}
}
Hope you find this useful.
All posted answers are just workarounds leading to a code that feels verbose and way away from standard code people are used to when dealing with stores.
So I just wanted to let everyone know that brophdawg11 (one of the commenters on the issue #863) created (and open sourced) set of mapInstanceXXX helpers aiming to solve this issue.
There is also series of 3 blog posts explaining reasons behind. Good read...
I found this from veux github issue, it seems to meet your needs
https://github.com/vuejs/vuex/issues/863#issuecomment-329510765
{
props: ['namespace'],
computed: mapState({
state (state) {
return state[this.namespace]
},
someGetter (state, getters) {
return getters[this.namespace + '/someGetter']
}
}),
methods: {
...mapActions({
someAction (dispatch, payload) {
return dispatch(this.namespace + '/someAction', payload)
}
}),
...mapMutations({
someMutation (commit, payload) {
return commit(this.namespace + '/someMutation', payload)
})
})
}
}
... or maybe we don't need mapXXX helpers,
mentioned by this comment https://github.com/vuejs/vuex/issues/863#issuecomment-439039257
computed: {
state () {
return this.$store.state[this.namespace]
},
someGetter () {
return this.$store.getters[this.namespace + '/someGetter']
}
},
Actually I am following Douglas Crockford jslint .
It give warning when i use this.
[jslint] Unexpected 'this'. (unexpected_a)
I can not see any solution around for the error . Don't say add this in jslist.options and mark it true.
Is there is any approach without using this?
EDIT
ADDED CODE
// some vue component here
<script>
export default {
name: "RefereshPage",
data() {
return {
progressValue: 0
}
},
methods:{
getRefreshQueue(loader){
console.log(this.progressValue); // ERROR come here [jslint] Unexpected 'this'. (unexpected_a)
}
}
}
</script>
Check out this jsfiddle. How can you avoid using this?
https://jsfiddle.net/himmsharma99/ctj4sm7f/5/
as i already stated in the comments:
using this is an integral part of how vue.js works within a component. you can read more about how it proxies and keeps track of dependencies here: https://v2.vuejs.org/v2/api/#Options-Data
As others have said, you're better off just disabling your linter or switching to ESLint. But if you insist on a workaround, you could use a mixin and the $mount method on a vue instance to avoid using this altogether ..
let vm;
const weaselMixin = {
methods: {
getdata() {
console.log(vm.users.foo.name);
}
},
mounted: function () {
vm.getdata();
}
};
vm = new Vue({
mixins: [weaselMixin],
data: {
users: {
foo: {
name: "aa"
}
}
}
});
vm.$mount('#app');
See the modified JSFiddle
As you can see, this only complicates what should be a fairly simple component. It only goes to show that you shouldn't break the way vue works just to satisfy your linter.
I would suggest you go through this article. Particularly important is this part ..
Vue.js proxies our data and methods to be available in the this context. So by writing this.firstName, we can access the firstName property within the data object. Note that this is required.
In the code you posted, you appear to be missing a } after getRefreshQueue definition. It's not the error your linter is describing, but maybe it got confused by the syntax error?
It is possible using the new Composition API. Vue 3 will have in-built support, but you can use this package for Vue 2 support.
An example of a component without this (from vuejs.org):
<template>
<button #click="increment">
Count is: {{ state.count }}, double is: {{ state.double }}
</button>
</template>
<script>
import { reactive, computed } from 'vue'
export default {
setup() {
const state = reactive({
count: 0,
double: computed(() => state.count * 2)
})
function increment() {
state.count++
}
return {
state,
increment
}
}
}
</script>
How can I load my static data in a single location within a nuxt/vue app?
Ideally I would have a JSON file holding all this data which would get loaded into vuex and would be accessible anywhere...
I've been suggested 2 options, but I don't find them clean...
When using the webpack template (not webpack-simple), you can use environment variables for that: http://vuejs-templates.github.io/webpack/env.html.
Alternatively, you can always just create some file - constants.js - anywhere in your project folder, which you use to store your static data (e.g. export const API_URL = 'https:/my-api.com' ).
Import the data from that file anywhere you need it (e.g. import { API_URL } from 'path/to/constants' ).
I've found an elegant solution using vue prototype
Hence with Nuxt.js
1) Create a plugin at ~/plugins/globals.js
import Vue from 'vue'
import globals from '~/globals.json'
import _get from 'lodash/get'
Vue.prototype.$g = (key) => {
let val = _get(globals, key, '')
if (!val) console.warn(key, ' is empty in $g')
return val || key
}
2) Create your json file at ~/global.json
{
"website_url": "https://www.company.com",
"social": {
"facebook": {
"url": "https://www.facebook.com/company"
},
"twitter": {
"url": "https://www.twitter.com/company"
}
}
}
3) Use these in every .vue file
<template>
<div>
<p>URL: {{ $g('website_url') }}</p>
<p>Facebook: {{ fburl }}</p>
<p><a :href="$g('social.twitter.url')">twitter</a></p>
</div>
</template>
<script>
export default {
data () {
return {
fburl: this.$g('social.facebook.url')
}
}
}
</script>
I found one easiest way to set configs (json) file and and use in any component.
Steps -
Create setting.json file in root directory.
in middleware, create a file settings.js (or keep any name) and paste this code
import settings from '../settings.json'
export default function ({ store, route, redirect, req}) {
process.env = settings
}
3 in nuxt.config.js - add settings in
router: {
middleware: ['users', 'settings']
},
uses -
In any component or page -
data(){
return {
settings: process.env
}
},
Now you can use settings anywhere in component.