Pass prop to parent in Vuetify and Vue JS - vue.js

How do I pass the dark mode value from navbar (child) to app.vue (parent)?
Within my navbar component I have a switch to enable/disable dark mode. I'd like to pass that dark data up to the parent (app.vue) to change the entire app.
Thank you!

You can use Vue's custom events interface. https://v2.vuejs.org/v2/guide/components-custom-events.html
In your child navbar component you can have a method:
handleThemeChange: function (mode) {
this.$emit('handle-theme-change', { mode });
}
And then in your parent App component watch for that event:
<App v-on:handle-theme-change="handleThemeChange" />
Then your app component can have a method handleThemeChange that actually handles the change. The handleThemeChange method in your app component will accept the object as a parameter.

Related

Detect Vue component' slot position and size changes

I have a component which receive named slots:
<Child>
<template #content>
<p>tooltip content</p>
</template>
<template #activator>
<button>hover me</button>
</template>
</Child>
I wanna know the position and size of the activator slot no matter where I'm gonna use it and what I'm gonna do with it. If I do like this:
<template>
<div style="margin-top: 13px;" :style="styleObject">
<Child>
<template #content>
<p>tooltip content</p>
</template>
<template #activator>
<button>hover me</button>
</template>
</Child>
</div>
</template>
<script setup lang="ts">
import {reactive, ref} from "vue";
import Child from "./components/Child.vue";
const styleObject = reactive({
marginLeft: '16px'
})
setTimeout(() => {
styleObject.marginLeft = '30px'
}, 2000)
</script>
Inside <Child> component I want to detect position changed after 2 seconds. I was able to get initial position and size with this:
const slots = useSlots()
const activatorStyles = reactive({
top: 0,
left: 0,
height: 0,
width: 0
})
const getActivatorStyles = () => {
if (slots?.activator) {
activatorStyles.top = slots.activator()[0]?.el?.offsetTop
activatorStyles.left = slots.activator()[0]?.el?.offsetLeft
activatorStyles.height = slots.activator()[0]?.el?.offsetHeight
activatorStyles.width = slots.activator()[0]?.el?.offsetWidth
console.log('activatorStyles', activatorStyles)
}
}
onUpdated(getActivatorStyles)
onMounted(getActivatorStyles)
but I'm not sure how to detect that in any of the parent components something changed which resulted in this <Child> component position or size change. For example this timeout from snippet above.
I was trying onUpdate but this seems to be working only on DOM Nodes changes (not styles). I was also trying to make this object as a computed property but no luck. Here is vue playground where initial size and position is correctly gathered but after timeout execution it doesn't detect that left changed and it stays 24.
My question is how can I can keep my activatorStyles object up-to-date no matter what will happen in parent components?
EDIT: I tried MutationObserver on parent but problem is that I don't know from where the changes of position / size might come. If I observer parentElement as suggested it works very well if the styles binding are on direct parent. If you I have more <div> nested and style binding is happening somewhere deeper the mutationObserver is not triggering anymore. To make it work I would need to pass document.body to observer which is not best performance, isn't it? playground example?
A component will only update if its props/data/computed changed. What happens there is that the update happens on the parent.
If you simply just want to access the parent from child, just use the $parent property and check/watch the property that holds the style.
Docs: https://vuejs.org/api/component-instance.html#parent
NOTE:
$parent is a reference to whatever Vue component rendered your component.
<A>
<B />
</A>
In this example, B's $parent would be A.
If you're going to teleport/move the element manually to another element, then what you want is
$el.parentElement
Example: https://www.w3schools.com/jsref/prop_node_parentelement.asp
Another option would be to check DOM changes via MutationObserver or using library like https://popper.js.org/
Docs: https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver
Example: https://stackoverflow.com/a/20559787/10975709
My opinionated answer though would be to suggest encapsulating the idea of styling the parent as part of your component that way your component can safely check that prop always.
Your example looks similar to some of Vuetify components like the Dialog for example (because of the activator slot).
Vuetify encapsulates the responsibilities everything on its own and doesn't rely on the code of whoever uses it.
Docs: https://vuetifyjs.com/en/components/dialogs/#usage

Control Vue component from nested layout using Inertia JS

I'm using Inertia JS using Vue and nested Layouts.
My main layout looks something like this:
<template>
<div>
<app-bar title="App title" type="back|dismiss|sidebar">
<!-- Slot for icons in the top right corner -->
</app-bar>
<slot />
</div>
</template>
So, an AppBar component accepting a title, a link with a back icon, dismiss icon or sidebar icon, and a slot (optionally) to provide icon links relevant to the current page.
<script>
import Layout from '#/Pages/Messenger/Layout';
export default {
metaInfo: { title: 'Report new problem' },
layout: [Layout],
...
</script>
This is a Page that is nested in the Layout.
So my question is: what is the best/preferred way to control the props and slot of the AppBar from nested Pages?
A bit like as you would do using Blade templates in Laraval or as Vue Meta does for the document page title as seen in the example above.
Maybe this is not even the best approach, in that case also let me know :)
If you are trying to pass information from your child component to your parent component such as a title, you can use $emit.
Here is a article describing how: https://hvekriya.medium.com/pass-data-from-child-to-parent-in-vue-js-b1ff917f70cc
And another SO question: https://stackoverflow.com/a/52479745/4517964
The fast way I found to pass data to persistent layouts is by:
in the child
use this:
layout: (h, page) => { return h(Layout, () => page) }
instead of:
layout: Layout,
and in the parent layout you can access your child with this.$slots.default()[0]

Is it possible to globally define links to use a specific component?

I'm currently trying to use Nav with react-router. The default behavior reloads the page, so I'm trying to use the Link component from react-router-dom.
It's quite difficult to preserve the default styling when overriding linkAs.
Is there any global way to override link navigation behavior?
Like defining a global link render function, which I can then set to render the Link component from react-router-dom?
Yes, it's possible!
2 things are required:
Make a wrapper component that translates the Nav API to react-router-dom links.
Specify the linkAs prop to the Nav component.
Wrapper component
This is a simple component that creates a react-router-dom link while using styles from Fabric:
import { Link } from "react-router-dom";
const LinkTo = props => {
return (
<Link to={props.href} className={props.className} style={props.style}>
{props.children}
</Link>
);
};
Specify component for use in Nav
<Nav groups={links} linkAs={LinkTo} />
Have also created a full working example at https://codesandbox.io/s/xenodochial-wozniak-y10tr?file=/src/index.tsx:605-644

How to submit a form from another component when the modal OK button is clicked (bootstrap vue)

In my Vue app, I have a component that handles a simple form named TodoForm.
Using bootstrap-vue, i would like to submit this form when the OK button of a bootstrap modal is pressed.
The code looks like this:
<b-modal id="todo-form-modal">
<todo-form />
</b-modal>
I don't want to put the modal component inside the TodoForm component since the TodoForm component only handles the form behavior, not the container where it is displayed.
I could also disable the OK button and put a button inside the form myself, but i'm sure there is a proper, a better way to submit this form (it is more like an exercise than a real project with an actual deadline).
I found the #ok event in the doc (triggered when the OK button is pressed), which is nice but i'm struggling to understand how i could use it to call a onSubmit() method inside the TodoForm.
For instance, it looks like this:
<b-modal id="todo-form-modal" #ok="something">
<todo-form />
</b-modal>
Ideally, the #ok="something" should call a method inside the TodoForm component.
How can I achieve this the right way ?
Expanding on #mapawa's answer:
<template>
<b-modal ... #ok="handleOk">
<todo-form ref="todoform" ...></todo-form>
</b-modal>
</template>
<script>
import TodoForm from 'somewhere/todoform'
export default {
components: { TodoForm },
methods: {
handleOk(bvEvt) {
// This assumes the root element of the todo form is the <form>
this.$refs.todoform.$el.submit()
// Alternatively, if your Todo Form exposes a submit method
this.$refs.todoform.submit()
}
}
}
</script>
What you want to do is to reference the parent component in the child component. You can use the ref attribute for this. I can't possibly explain this better than the official docs, so take a look at this.

Rendering stenciljs stateless components

I've been using StencilJS for some time now,
and coming from React background, my immediate instinct for writing some components is to write them stateless.
However, the stencil documentation doesn't mention the stateless components at all.
That's why I am writing here to learn other people experience with it
You should look at functional components: https://stenciljs.com/docs/functional-components to create stateless components, and they:
aren't compiled into web components,
don't create a DOM node,
don't have a Shadow DOM or scoped styles,
don't have lifecycle hooks,
According to the doc, if a component has to hold state, deal with events, etc, it should probably be a class component. If a component's purpose is to simply encapsulate some markup so it can be reused across your app, it can probably be a functional component
You can write functional components inside Stencil elements. As an example:
#Component({
tag: 'my-app',
styleUrl: 'my-app.css',
shadow: true
})
export class MyApp {
render() {
return (
<div>
<Loading />
</div>
);
}
}
const Loading = () => {
return (
<div class="loading">
<h1>Activating Santa</h1>
<span>🎅🎄🎁</span>
</div>
);
};
In this case <Loading> is a stateless functional component similar to React's model - you can obtain its props and get children, etc.
Stateless Stencil components can not be exported as top-level Web Components - those must be defined as classes.