Vue 3 - How to change data on one of multiple chid components? - vue.js

I have a Vue 3 project where I have a parent component that loads other component that then loads another one that loads another one.
To give you an idea, I have a button component. The button is added multiple times in a set of numbers, the set goes into a card and the card goes into the main view component.
So 4 levels:
Parent Main view component with X cards (in this case I only have one)
Numbers Card component with X groups
Numbers Group component with X numbers
Number component used in Group
I have a binded var on the Number component that sets it active or not, by being true or false.
I want a way to set a function in my <script setup> tag in the parent top Main component (or one component inside of it) to turn one or multiple of the buttons components one and off (active true or false).
Does that makes sense?
I'm thinking if I need to use emit or just state data from my vuex data store. But I'm a bit confused on how to identify the specific button component (used multiple times) from the main parent 4 levels up component...
(In reality I want to add another type of button component there to activate that, toggle all other buttons on or off - But I think that if I understand how can from one function in one component I can access a variable in another set of components that will solve my issue...)
Let me know if this makes sense and if you can help me.
Feel free to point me to the Vue 3 Docs, I have been trying to find a solution but I'm not sure exactly where to look and because I'm using <script setup> new tag I think a lot of the examples there don't work for me.
Any little help is appreciated. Thanks so much!

Thanks #Matt. I ended up using the Vuex variable as an array.
Then in my Button component I have this in my <script setup> for the isPicked variable, for example:
const isPicked = computed(() => {
return store.state.session.btnsPicked.find(
(i) => i === props.btn
)
})
So my component variable now reacts to the Vuex states values.

Related

vuejs loading entire .vue file dynamically from 1 template into another

Sorry if this is a repeat - I have had a look around and spent most of today on this issue without getting result I am after.
https://github.com/vahidhedayati/micronaut-vuejs-cqrs/blob/master/frontend/src/components/sample/dynamicForm/PropertySplit.vue#L29
https://github.com/vahidhedayati/micronaut-vuejs-cqrs/blob/master/frontend/src/components/sample/dynamicForm2/Property2.vue#L31
I have two similar vue parent pages that load up dynamic form content, what I am trying to do is to get the actual form content which resides on a totally different .vue file in each of the sub folders above.
If I use Vue.component('form-content' this does work but I end up with 1 instance of the form for both different calls rather than a dynamic form per call. So one of the two forms gets loaded in and reused on both calls.
I have tried to use a const variable instead of Vue.component which works correctly locally on the main vue page that loads in the const variable but I haven't been able to get child page to load up the const value
//import Vue from 'vue'
//Vue.component('form-content', {
// components: { ActualForm },
// template: `<actual-form></actual-form>`
//});
//const content = {
// components: { ActualForm },
// template: `<actual-form></actual-form>`
//}
ActualForm.vue is another page and ideally I want to be able to pass this dyanmically per call on different page to the underlying DynamicForm.vue which will load title etc and then the form as per vue page that is passed in.
It seems trivial not quite sure why I am struggling with this.
You cannot pass a component to another component through a prop. For that you need to use a slot. So your code would look like:
<dynamic-form
:headingText="currentHeadingText"
:sectionHeadingText="currentSectionHeadingText"
>
<actual-form/>
</dynamic-form>
And in the <dynamic-form> component you would write:
<template>
[…]
<slot></slot>
[…]
</template
Then the <actual-form> component would be included at the position of the <slot>-tag. You can also use multiple slots and pass in multiple components / template segments. (Read the docs on vuejs.org for detailed information)
I am not completely sure what you are trying to do though. Maybe you need to structure your application in a different way. E.g. use the <dynamic-form> in the template of the <actual-form>. You could then still pass props to <dynamic-form> and fill slots therein from the <actual-form>.

Toggle components based on prop value in main Vue instance

I'm designing a page that returns a bunch of different hotels using one hotel component. Each hotel passes in unique values to the component such as name, star rating, hotel facilities etc.
On the main page in the Vue instance I need some basic filters that toggle each component true/false based on it's own prop value. I don't know how to go about pulling the prop value out of the hotel component into the main Vue instance and running a method on it?
As this is a small app, I'm using the CDN for ease of use.
Component example below.
<hotels
hoteltitle="Ryans"
hoteldesc="This hotel is situated on the popular waterfront with sweeping views
overlooking the marina."
loc="Ibiza"
star="4"
beachfront="true"
family="false"
gym="true"
pool="true"
single="true"
spa ="false"
hotelurl="/url.html"
price="74"
url="index2.html"
accomCode="30030"
></hotels>
If you want to emit data from child component to parent component you can use the $emit function. Check officiel doc.
Check this codepen example

is there A Communication Manager for vue components?

I was Vue.js now for a project and created and used a lot of components.
Now I started to have the problem of having too many eventemitters and props that I need to keep track of.
I guess to illustrate the problem the best I will use an example:
Lets say you have a main.vue and 2 or 3 Components.
One contains a button that should manipulate the other 2 components or switch them out.
Now I need to emit an event to the main.vue and then main.vue has to change a binded variable and pass props down to the other 2 components.
Alright: Now lets put the button in a component of of a component. You need to make sure that every link between a parent and a child is correct.
Now create a bit project and put a button in another components and you have to change everything.
So is there a good way to avoid this?
Something like a broadcast function so that every component is receiving the event?
Or a Manager that is handling the communication of all components?
use a flux pattern (vuex)
At first you may think that this does not really answer the question, since it deals with storage of data, and not handling of events. The flux pattern changes the architecture of your application by creating a single store (think database) that all components can read and write from. Coupled with the reactive nature of the reactive frameworks such as vue (or react), the components will react to a change in data. So instead of tightly coupling component A to D through B and C, you'd have component A listen to mutations in object X, and component D makes changes to object X. When the change happens, component A gets updated without having to listen to any of the children's $emit functions firing. At first it may seem daunting, but the investment is worthwhile.

VUE.JS: Passing Data From Parent To Child Component

I have a layout file in which I have some data. Besides that I have three components:
tags
students
actions
I want to declare the data in the layout file, but access it through the three child-components.
How is this done in Vue.js?
Thank you for your help.
One option can be to pass the props to all the child components which is the norm in vue when it comes to passing data to child component, as also explained here.
Given that you want to pass same data to all these components and there can be cases going forward when you want to change this data and get it reflected in parent as well, you can go for a centralised state management, which is explained here in more detail.

Preserve component state with vue-router?

I have a listing/detail use case, where the user can double-click an item in a product list, go to the detail screen to edit and then go back to the listing screen when they're done. I've already done this using the dynamic components technique described here: https://v2.vuejs.org/v2/guide/components.html#Dynamic-Components. But now that I'm planning to use vue-router elsewhere in the application, I'd like to refactor this to use routing instead. With my dynamic components technique, I used keep-alive to ensure that when the user switched back to the list view, the same selection was present as before the edit. But it seems to me that with routing the product list component would be re-rendered, which is not what I want.
Now, it looks like router-view can be wrapped in keep-alive, which would solve one problem but introduce lots of others, as I only want that route kept alive, not all of them (and at present I'm just using a single top level router-view). Vue 2.1 has clearly done something to address this by introducing include and exclude parameters for router-view. But I don't really want to do this either, as it seems very clunky to have to declare up front in my main page all the routes which should or shouldn't use keep-alive. It would be much neater to declare whether I want keep-alive at the point I'm configuring the route (i.e., in the routes array). So what's my best option?
You can specify the route you want to keep alive , like:
<keep-alive include="home">
<router-view/>
</keep-alive>
In this case, only home route will be kept alive
Vue 3
<router-view v-slot="{ Component }">
<keep-alive>
<component :is="Component" />
</keep-alive>
</router-view>
Exactly as is, you don't need a Component attribute in the App.vue. Also your this'll work even if your components don't have a name property specified.
I had a similar problem and looked at Vuex but decided it would require too much changes/additions in my code to add to the project.
I found this library https://www.npmjs.com/package/vue-save-state which solved the problem for me, keeping the state of 1 component synchronized with localStorage, and it only took a few minutes and a few lines of code (all documented in the Github page of the package).
One solution without localStorage:
import {Component, Provide, Vue} from "vue-property-decorator";
#Component
export default class Counter extends Vue {
#Provide() count = 0
/**
* HERE
*/
beforeDestroy() {
Object.getPrototypeOf(this).constructor.STATE = this;
}
/**
* AND HERE
*/
beforeMount() {
const state = Object.getPrototypeOf(this).constructor.STATE;
Object.entries(state || {})
.filter(([k, v]) => /^[^$_]+$/.test(k) && typeof v !== "function")
.forEach(([k]) => this[k] = state[k]);
}
}
What seems to me is you are looking for some kind of state management. If you have data which is shared by multiple components and you want to render component in different order, but dont want to load data again for each component.
This works like following:
Vue offers a simple state management, but I will recommend to use Vuex which is a standard for state management among vue community.