Wrong component structure - prop-problems - vue.js

I'm a noob and I'm trying to get my head around vue.js. In a particular route I have a view that looks like this
<template>
<div class="section">
<h1 class="title">Maintenance</h1>
<app-tabs></app-tabs>
<div class="columns">
<div class="column is-one-third">
<app-taglist></app-taglist>
</div>
<div class="column is-two-thirds">
<app-form></app-form>
</div>
</div>
</div>
</template>
<script>
import Taglist from "#/components/Taglist.vue";
import Tabs from "#/components/Tabs.vue";
import Form from "#/components/Form.vue";
export default {
}
</script>
Now I do understand the basics of passing data from a component to a child component, but what I'm actually trying to do is to pass data from app-tabs to app-taglist and from app-taglist to app-form. I'm starting to think that I'm attacking this all wrong, but if I do change my structure so that app-taglist is a child of app-tabs and app-form is a child of app-taglist - I can't really make proper use of the simple bulma, responsive, styling ...?
BTW: all components is registered globally at this time.
What kind of aproach would you advise me to look into - keeping in mind that I'm a noob.

Once you start getting to the point where you need to handle passing data between multiple components then I would consider using Vuex in order have a global component state that is accessible from all of your components.
Basically you can then create a totally separate "store" to hold your app's state (data):
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
increment (state) {
state.count++
}
}
})
And then from within each component, you can:
Use "getters" to read data from the store
Use "actions" to dispatch async functions that will "mutate" the store
e.g:
store.commit('increment') // this is a mutation
console.log(store.state.count) // -> 1

Related

How to change data in default layout from nested components in Nuxt.js

I have layout default.vue:
<template>
<div>
<Header></Header>
<div class="body-wrapper">
<Nuxt :numberThird="numberThird" />
</div>
<Footer></Footer>
</div>
</template>
<script>
export default {
data() {
return {
numberThird: 3
};
},
};
</script>
Here I am trying to pass prop numberThird.
I want to be able to change this value in the future through components that are deeply nested.
But there is a problem: my pages don't accept this prop (numberThird), they treat it as $parent.$attr.
The question is: can I somehow change this value through deeply nested child components?
As stated here: https://stackoverflow.com/a/67817642/8816585
Props and listeners are not the most friendly on either <nuxt> nor <nuxt-link>.
Kinda confirmed here by Alexander too: https://github.com/nuxt/nuxt.js/issues/8669#issuecomment-764006062
You better off using Vuex in this case, especially if you are aiming to something deep. Props drilling is not recommended most of the time.

Vue3 component using v-if from vuex state

I'm limited with the code I could show, but I am trying to use a V-If statement based on a value in the vuex store.
We're working with component API and typescript.
The partial code from the DataStore.ts:
export const store = createStore<state>({
state: {
errorOrderPending: false
},
mutations: {
setErrorOrderPending(state, newVale: boolean) {
state.errorOrderPending = newVale;
}
}
});
export function useStore() {
return baseUseStore(key);
}
In the component, I'm importing the store and useStore.
<template>
<div class="modal" >
<p>Show modal</p>
</div>
</template>
I tried to do:
<div v-if="$store.state.errorOrderPending" class="modal" >
But it's not working.
I wish I can share the code but I can't but this is basically what is happening.
Thanks
Imported the store into my component with the module, but I had to export the store from the component's API setup. After I did that, state.store.errorOrderPending worked.

Fetching data from parent-component after reload a route with vue and vue-router

I have a component showing multiple routes (Step1, Step2, Step3...) on after each other. I navigate and pass properties like
<router-view
#next-button-step1="
$router.push({
name: 'Step2',
params: { myprop: thisprop },
})
"
[...]
</router-view>
with the routes defined like
const routes = [
{
path: "/s2",
name: "Step2",
component: Step2,
props: true,
},
This works well, until I reload a page/route, because the the data is lost. So I kind of want to fetch the data from the parent, after the component is loaded.
One of my ideas was using the local storage, but this does not feel right.
I am a newbie to Vue and I would like to ask what's the best practice here. Is it vuex like described here Reload component with vue-router? I'd appreciate a hint.
If localStorage is suitable depends on the use case.
You say that data is lost when you reload the page/route. It's perfectly possible to store this data to prevent data-loss on route change/reload. But changing/reloading the page will only be solved either by storing data in localStorage (and retrieving from localStorage on page init), or storing it on the server and retrieving it on page init...
Vuex may help you with the route change/reload part, but even Vuex won't help you with the page change/reload.
I will show you an example of how to save the data for the first case: route changes and reloads, because this may be achieved without adding Vuex. We will do this by having the data inside a parent component and passing this data to our routes. When a route changes the data it should emit an update-event (including the updated data) and the parent should store the changed data.
I'll show an example scenario in which the parent holds all the data. The routes
are responsible for editing different aspects of the data. Each time i switch between a route the parent supplies the data.
A working example can be found here: https://jsfiddle.net/b2pyv356/
// parent component / app.vue
<template>
<div>
<router-view
:state="state"
#updatestate="updateState"
></router-view>
<pre>Parent state: {{state}} </pre>
</div>
</template>
<script>
export default {
data() {
return {
state: {
name: 'name',
lastname: 'lastname'
}
}
},
methods: {
updateState(state) {
this.state = state;
}
}
};
</script>
// page1.vue
<template>
<div>
Route 1: State is:
<pre>{{state}}</pre>
<div>
Name: <input v-model="state.name">
<button #click="$emit('updateState', state)">Save</button><br>
</div>
<router-link to="/page2">Next step</router-link>
</div>
</template>
<script>
export default { props: ['state'] }
</script>
// page2.vue
<template>
<div>
Route 2: State is <pre>{{state}}</pre>
Name: <input v-model="state.name">
<button #click="$emit('updateState', state)">Save</button><br>
<router-link to="/">Previous page</router-link>
</div>
</template>
<script>
export default { props: ['state'] }
</script>
Persisting data:
On updateState you could store the data in localStorage
You could store some data in the request url ($router.params) or page query string. This has limits: some browsers enforce limits on how long a url may be. You are also responsible to validate/sanitize incoming data, do not trust that it won't be tempered with. Same applies to localStorage data btw. Common cases include storing a search query: if you refresh the page you still have the search query.
Let the backend save the data and retrieve the user's data on page load.

Can I debug the export/import of a mixin?

I'm pretty new to vue.js and I'm trying to figure out how to use mixins.
I wondered if it is possible to create components which are bare of markup/template and contain only logic. As far as I understood, this should be possible and these components are called "mixins":
https://blog.bitsrc.io/understanding-mixins-in-vue-js-bdcf9e02a7c1
I'm using router functionality.
I'm now just trying out the vary basics of this concept and created the following:
listData1.vue, where the data for a list is created and then exported:
<script>
export default {
name: "listData1",
data() {
return {
list1: {
listData1A : "listData1A",
listData1A : "listData1B",
listData1A : "listData1C"
}
}
}
}
</script>
then listBuilder.vue, which takes the data and then uses it to create a list of items.
<template>
<div>
<ul>
<li v-for="element in list1" v-text="element"></li>
</ul>
</div>
</template>
<script>
import listData1 from "#/components/complexComponent2/listData1.vue"
export default{
name: 'listBuilder'
}
</script>
And then myComplexView2.vue in my views folder:
<template>
<div>
<h1>Second Awesome List!</h1>
<listBuilder />
</div>
</template>
<script>
import listBuilder from "#/views/myComplexView2.vue"
export default{
name: 'myComplexView2',
components: {
listBuilder
}
}
</script>
Now the result I get is this:
https://imgur.com/hQit785
But it should look like this:
http://localhost:8081/myComplexView
I'm a bit clueless what to do, especially since the vue dev tools in firefox don't show me much: https://imgur.com/RHJNy47.
Am I accessing the imported incorrectly?
Should I store the data differently, with the "data : {}" syntax or should I go for props in the listData component, like this:
props:["listData1"]
And then add the actual data in the component where the list is constructed with v-for? Though this would kind of undermine my goal to accomplish separating the data from the logic injecting it into the markup.
You need to setup listData1 as mixin in listBuilder.
<template>
<div>
<ul>
<li v-for="element in list1" v-text="element"></li>
</ul>
</div>
</template>
<script>
import listData1 from "#/components/complexComponent2/listData1.vue"
export default{
name: 'listBuilder',
mixins: [listData1],
}
</script>
Otherwise the ListBuilder won't have any data.
There's a typo in the mixin data:
listData1A : "listData1A",
listData1A : "listData1B",
listData1A : "listData1C"
Should be:
listData1A : "listData1A",
listData1B : "listData1B",
listData1C : "listData1C"
Apart from this, I don't see anything at syntax level in your code that would prevent mixin and v-for for working.
However, it puzzles me that myComplexView2 is importing myComplexView2.vue as the listBuilder:
import listBuilder from "#/views/myComplexView2.vue"
I don't know if this is an error you made when pasting to SO. Otherwise, the problem is probably here, since you need to import the listBuilder component, not the complex view.

Adding props on runtime to Vue component

I try to create a highly dynamic wizard as a component in Vue. It contains out of three components: the Wizard component itself, a Step component and a single form "MyForm" component. The form can be in edit mode or in read only mode depending on the current step of the wizard.
After some trial and error I finally managed to create such a component and it works. One problem that I struggled to solve was to pass the information if the form is in edit mode or not from the Step component to the child form component.
MyForm.vue
<template>
<form>
<div v-if="inEditMode"><i>Form is in edit mode</i></div>
<div v-else><i>Form is in read only mode</i></div>
</form>
</template>
<script>
import Vue from "vue";
export default Vue.extend({
props: ["inEditMode"]
// mixins: [wizardStepMixin],
});
</script>
Wizard.vue
<Step>
<MyForm/>
</Step>
Step.vue
<slot :isInEditMode="true"/>
Passing/setting a prop to a slot like I did above did not work (prop did not change).
My solution to set the prop isInEdit on the MyForm is to call a function prepareSlot in the Step component before mounting and updating the Step.
prepareSlot() {
this.$slots.default.forEach(element => {
if (!element.data) return
element.componentOptions.propsData = {
...element.componentOptions.propsData,
inEditMode: this.stepNr === this.currentStep
}
})
}
You can find the complete project on https://codesandbox.io/embed/mzr10wzk0j.
Is there a better way to archive that? Is it safe to do it that way?