Vue components hierarchy and passing data - vuejs2

I'm writing an app in Vue and I have a really hard time understanding the component hierarchy, namely the parent-child relationships and how to pass data around.
I have a view that contains a map which in turn has some navigation controls and options that are overlayed on top of the map. Now, I want these controls to manipulate the map WITHOUT having to nest the buttons inside the actual maps as it will cause severe display issues (for example, clicking on a zoom button falls through the button and also clicks the next element under it).
My app looks like this:
Mapview
Map
Controls
Options
Optionpanel1
Optionpanel2
...
Now, a HTML input element in Optionpanel1 needs to control something in the Map, which is not actually it's parent component. Also, some data from Map needs to be passed down to Optionpanel1 so it would know what to control. To make matters worse, something in Options also needs to pass something down to Optionpanel1, so, even though event bus would allow communication upwards, it would not solve that I need to pass data from parents to indirect children and also components that are not it's children.
I can actually pass the required property down the line if I nest the Options inside Map and pass it down with :myProp="prop" and then in Options, declare it in props and bind to Optionpanel1, where it is again declared as a prop. But as I mentioned earlier, nesting elements that I do not want to be nested in a visual sense causes massive issues like mouse click falling through. Do elements even need to be nested inside eachother in order to define parent-child relationship?
I would want components to exchange read-only data without Y being a child of X in the DOM. Also, even if nesting components like this would not cause visual issues, does it not seem very annoying to have to register and bind it in every component along the way?
I don't understand why is it so difficult to simply read something from another component. It's starting to seem that Vue is causing a problem that it's supposed to solve? Am I missing something here or am I looking at this in a completely wrong way?
Thanks in advance.

Basically you have 2 options to control complex components:
Handle the actions in your so-called "smart component" (in terms of React), which is the common ancestor for the controlling and controlled components. It's a pretty good solution for small components, but that's not the case.
To separate common logic and data in a Vuex store. I'd recommend you doing this.

Related

Vue slot is not working in rare and unpredictable cases (potential vue bug?)

I have this weird bug with a slot that is unreliable in certain unknown cases.
Components
There are 3 hierarchical components.
The grandchild (headlessTable), which offers a slot named arrayValue.
The child (collapsableCard), which passes the slot between grandchild and parent.
The parent (orderDataCard), who decides to render a link for that slot.
Problem: Instead of rendering the link of the parent, the default slot html of the child is being rendered when new data is loaded.
Datastructure (orderDetails)
process (obj)
mark (string)
common (obj)
additionalArguments (array)
category (string)
type (string)
name (string)
value (string)
salesOrganisation (obj)
invoices (array)
invoiceAgreementId (string)
paymentType (string)
Reproduction
Stackblitz or Codesandbox
Please look at the field additionalArguments, it contains a link.
Press ALT+M to simulate fetching new data. Now, instead of rendering a link, the default slot html for that named slot is rendered instead.
You can press ALT+J to load the original data, but this time there's no link.
Initial data (ALT+J)
Loaded data (ALT+M)
Type
Equal value
mark
str
false
common
common
obj
true
salesOrganisation
salesOrganisation
obj
true
invoices (empty)
invoices
arr
false
How 2 resolve
if you uncomment line 68 in app.js (or line 73 in App.vue if you're on codesandbox), which is the field called mark
if invoices is not initially empty in app.js
if mark is removed from html in orderDataCard
if salesOrganisation is removed from html in orderDataCard
if the html in the v-for template section for invoiceItems is empty in orderDataCard
Obviously, these are not solutions.
Notes
In any case, there is no dependence or anything between any of the fields, so it's hard for me to understand why this happens and I suspect this to be a bug with vue. I already created an issue for this. However, devs won't look at the reproduction, because they think it's not minimal as #lines > 100. As soon as I delete any more meaningful lines, the bug is resolved and the removed code is not faulty, so it's very frustrating to work on this. I could still remove lines that are not meaningful, but that would make it more difficult for everyone involved to understand what data is being rendered.
Is anyone able to acknowledge the fact that this is a problem with vue and that the code is not reducible OR (I would prefer this) is anyone able to fix this?
The problem is linked to Vue handling of multiple instances of the same component. In OrderDataCard.vue you have two instances of Collapsable-Card without unique keys. In this case:
Vue uses an algorithm that minimizes element movement and tries to
patch/reuse elements of the same type in-place as much as possible.
I don't quite know how these algorithms work, and why, apparently, it reused the second instance (without a defined slot content), but, setting a unique key for these components solved the issue.
See the working code sandbox: https://codesandbox.io/s/admiring-hamilton-5ytpp?file=/src/components/OrderDataCard.vue:133-149.
Note: I couldn't trigger keyboard events in my browser, so I triggered them on button click.
This may not be the solution, but could help find it:
Objects
I noticed you are working with objects and turning them into arrays. Objects properties can be problematic to work with, because unlike arrays updated properties are not propagated. This is a problem with JavaScript, not Vue. Vue was only possible because of observers introduced, but objects are still not part of that.
You might run into problems when an object is partially updated.
I would suggest looking at Vue.set.
Old code of mine invokes it explicitly by window.Vue.set() for changes in object properties so Vue can propagate them correctly.
That is kind of a bug in Vue, but again stems from JavaScript itself.
Computed arrays
I'm not entirely sure but the computed arrays don't save the above issue with working with objects.
I would go the safe route and use Vue.set() when updating objects and object properties. You can still use the computed arrays then.
Otherwise the obvious: Make real arrays out of the objects instead of working with objects half the time.
this.process
Is there a good reason you are using this.process explicitly instead of the component's props? Or is that a component from a library?
Slots
Have you tried the exact same code but without using the collapsable-card? Just output the link itself? It might point to slot problems in the collapsable-card component. Maybe also partially because of the objects thing from above.

Best solution to update component content from another component in Nuxt

I'm new to Nuxt and Vue in general, so I'm not sure, what would be the best solution for this use case.
I want to change the text (like "Home", "Settings", "Favourites" etc.) inside my Header.vue (always fixed on top of the page) from another component. like Favourites.vue for example.
Sometimes I want to hide the header completely or hide the title and display buttons instead,
so I need to pass more props than just the title.
I tried using different layouts, but that breaks the animation transitions (I haven't found the solution for this yet), but I think it's still better to have control from the page in what I'm passing into this component.
Should I pass props from Page.vue to parent and read it from there in Header.vue component?
Should I use Vuex to pass this through the store and update it when the route changes? Or is that too complex for this use case?
Maybe there's a simpler solution that I'm not aware of.
Folder structure:
/components
├──Header.vue
└──Nav.vue
/pages
├──Index.vue
├──Profile.vue
├──Settings.vue
└──Favourites.vue
Vuex is the answer here— don’t worry about the 'simple' use case. As soon as you notice you’re creating data that other components may rely on (is the header visible on this page? Is the text different? etc) it’s a good idea to move into Vuex and maintain a single source of truth.
Your app may seem simple to begin with, but it’ll inevitably grow and at that point you’ll appreciate having a single source of truth vs. trying to pass things between components via props.
Nuxt also makes implementing Vuex very straight forward. No doubt you’re capable of pulling up the docs!

List of same components with same functions - code bloat?

I'm using Vue and I wonder if I have a list of components (50 button with the very same function within each one of them) - will Vue recognize it as a repetetive code and reduce to one function that all those 50 button will use or each one will compile it's own function while bundling?
will Vue recognize it as a repetetive code and reduce to one..
No. Vue will not scan your code for similar code and try to optimize it.
However, when Vue is updating a list of elements rendered with v-for, by default it uses an “in-place patch” strategy. If the order of the data items has changed, instead of moving the DOM elements to match the order of the items, Vue will patch each element in-place and make sure it reflects what should be rendered at that particular index.
Maybe this is what you are confused with? This is not the same as the question you are asking, but the closest thing vue would do "magically".
If you have 50 similar buttons, I would advice you to rather take advantage of props, slots and slot scopes to only have one button component that you can tweek in place where you need them to be different. 50 alike buttons sounds like a bad pattern.

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.

Nesting ui components and accessing global data in Elm

I have an elm app designed with the Elm Architecture in mind. I've used it for all the samples in the tutorial and they work fine. I have the following components
ContainerListView
ContainerView
AddressView
RegistrationView
...
The ContainerView component is a very formatted div structure that is used to contain other views (but for now, only 1 at a time)
ContainerListView can contain multiple containerViews. It handles their presentation and positioning. You can think of it as an MDI surface
A menu from the main ui is used to add new container views to the container view list.
I'm presented with with three main questions. Two of them are
How do I create the components such that Container view can contain any other element is I pass in for example the init, update, and view functions and expect all things to be wired correctly? At the moment, the samle views I have are kinda hard-coded. They know exactly who the children is.
Some of the components require access to things like url, access token, etc. Does this always have to be passed in from main to the individual components or it can come from another source which will essentially be readonly and maybe updatable only from main?
I'm not sure if these two should be individual questions on their own. Any information on how to architect larger apps beyound hello world will also be appreciated.
I'm working on something similar! Nested controls. I too have a container object which knows about all the types that it can handle, and has basically case statements to handle each type. So I can't drop in a new control type and expect it to handle it, that requires altering the container.
As far as I know elm doesn't have type classes, which would be how I might try to handle that kind of abstraction in haskell or purescript. There's more about that here:
https://github.com/elm-lang/elm-compiler/issues/38
and here:
https://github.com/elm-lang/elm-compiler/issues/1039
The upshot appears to be that they don't know how they want to solve that problem yet, so they haven't.