How to Two-way Data Binding Between Parents and grandchildren in Vue.js - data-binding

I faced a problem, I solve it by cookies but I want to solve the problem without cookies. I have a component which called app-header and It has another component which called outmodal.
Now, My first Vue instance require component app-header.
var vue = new Vue({
el : "html",
data : {
title : "Site Title",
description : "description of page",
keywords : "my keywords",
view : "home",
login : "login"
},
components:{
"app-header" :require("../../components/header"),
"app-footer" :require("../../components/footer"),
"home" :require("../../views/home")
},
});
code of app-header
var Vue = require("vue");
Vue.partial("login",require("../../partials/login.html"));
Vue.partial("logged",require("../../partials/logged.html"));
module.exports = {
template : require("./template.html"),
replace : true,
components : {
outmodal : require("../outmodal")
},
props : ['login']
}
code of outmodal
var Vue = require("vue");
Vue.partial("loginModal",require("../../partials/loginModal.html"));
module.exports = {
template : require("./template.html"),
replace : true,
props : ['name'],
data : function () {
return {
userLogin : { mail : "", password : "", remember : ""}
}
},
methods : {
formSubmit : function(e){
e.preventDefault();
this.$http.post("http://example.com/auth/login",{ "email": this.userLogin.mail , "password": this.userLogin.password },function(data,status,request){
$.cookie("site_token",data.token,{expires : 1})
}).error(function(data,status,request){
});
}
}, ready : function(){
console.log("it works")
}
}
In outmodal component I connect the API and I check the login, If login will be succesfull, I want to change value of login variable in my Vue instance. I use web pack to build all requires. So I don't know how can I data binding between these files.
How can I solve It? I

The Best Solution which I found
For 0.12
http://012.vuejs.org/guide/components.html#Inheriting_Parent_Scope
for 1.0
http://v1.vuejs.org/guide/components.html#Parent-Child-Communication
for 2.0
https://v2.vuejs.org/v2/guide/components.html#Composing-Components (use props to one-way bind data from parent to child)

There are several ways of doing it, and some are mentioned in other answers:
Use props on components
Use v-model attribute
Use the sync modifier (for Vue 2.0)
Use v-model arguments (for Vue 3.0)
Use Pinia
Here are some details to the methods that are available:
1.) Use props on components
Props should ideally only be used to pass data down into a component and events should pass data back up. This is the way the system was intended. (Use either v-model or sync modifier as "shorthands")
Props and events are easy to use and are the ideal way to solve most common problems.
Using props for two-way binding is not usually advised but possible, by passing an object or array you can change a property of that object and it will be observed in both child and parent without Vue printing a warning in the console.
Because of how Vue observes changes all properties need to be available on an object or they will not be reactive.
If any properties are added after Vue has finished making them observable 'set' will have to be used.
//Normal usage
Vue.set(aVariable, 'aNewProp', 42);
//This is how to use it in Nuxt
this.$set(this.historyEntry, 'date', new Date());
The object will be reactive for both component and the parent:
I you pass an object/array as a prop, it's two-way syncing automatically - change data in the
child, it is changed in the parent.
If you pass simple values (strings, numbers)
via props, you have to explicitly use the .sync modifier
As quoted from --> https://stackoverflow.com/a/35723888/1087372
2.) Use v-model attribute
The v-model attribute is syntactic sugar that enables easy two-way binding between parent and child. It does the same thing as the sync modifier does only it uses a specific prop and a specific event for the binding
This:
<input v-model="searchText">
is the same as this:
<input
v-bind:value="searchText"
v-on:input="searchText = $event.target.value"
>
Where the prop must be value and the event must be input
3.) Use the sync modifier (for Vue 2.0)
The sync modifier is also syntactic sugar and does the same as v-model, just that the prop and event names are set by whatever is being used.
In the parent it can be used as follows:
<text-document v-bind:title.sync="doc.title"></text-document>
From the child an event can be emitted to notify the parent of any changes:
this.$emit('update:title', newTitle)
4.) Use v-model arguments (for Vue 3.0)
In Vue 3.x the sync modifier was removed.
Instead you can use v-model arguments which solve the same problem
<ChildComponent v-model:title="pageTitle" />
<!-- would be shorthand for: -->
<ChildComponent :title="pageTitle" #update:title="pageTitle = $event" />
5.) Use Pinia (or Vuex)
As of now Pinia is the official recommended state manager/data store
Pinia is a store library for Vue, it allows you to share a state across components/pages.
By using the Pinia store it is easier to see the flow of data mutations and they are explicitly defined. By using the vue developer tools it is easy to debug and rollback changes that were made.
This approach needs a bit more boilerplate, but if used throughout a project it becomes a much cleaner way to define how changes are made and from where.
Take a look at their getting started section
**In case of legacy projects** :
If your project already uses Vuex, you can keep on using it.
Vuex 3 and 4 will still be maintained. However, it's unlikely to add new functionalities to it. Vuex and Pinia can be installed in the same project. If you're migrating existing Vuex app to Pinia, it might be a suitable option. However, if you're planning to start a new project, we highly recommend using Pinia instead.

i found this one to be more accurate.
https://v2.vuejs.org/v2/guide/components.html#sync-Modifier
only in 2.3.0+ tho.
and honestly it's still not good enough. should simply be a easy option for 'two-way' data binding. so none of these options is good.
try using vuex instead. they have more options for such purpose.
https://vuex.vuejs.org/en/state.html

I would prefer event-driven updates as recommended in the documentation. However, I was limited by the existing ("third-party") component already using props and $emit. This component is my grandchild. The following is my solution (passing value through child using props, sync and computed value with $emit.
Comments are welcome.
Value can be modified in parent and grandchild without error:
Grandchild (simplified third-party component):
<template>
<div v-show="value">{{ value}}</div>
<button #click="closeBox">Close</button>
</template>
<script>
export default {
props: {
value: null
},
methods: {
closeBox() {
this.$emit('update:value', null);
}
}
}
</script>
Child:
<template>
<grandchild-component :value.sync="passedValue" />
</template>
<script>
export default {
props: {
value: null
},
computed: {
passedValue: {
get() {
return this.value;
},
set(newVal) {
this.$emit('update:value', newVal);
}
}
}
}
</script>
Parent:
<template>
<child-component :value.sync="value" />
</template>
<script>
export default {
data() {
return {
value: null,
}
},
// ... e.g. method setting/modifying the value
}
</script>

Related

Vue3 dynamically watching child component data

I'm working in Nuxt3 and I've got a slightly unusual setup trying to watch or retrieve data from child components in a complex form that is structured as a multi-step wizard. It's obviously Vue underneath and I'm using the composition API.
My setup is that I have a page the wizard component is on, and that component has a prop that is an array of steps in the wizard. Each of these steps is some string fields for titles and labels and then a component type for the content. This way I can reuse existing form blocks in different ways. The key thing to understand is that the array of steps can be any length and contain any type of component.
Ideally, I'd like each child component to be unaware of being in the wizard so it can be reused elsewhere in the app. For example, a form that is one of the steps should handle its own validation and make public its state in a way the wizard component can read or watch.
The image below explains my basic setup.
                             
The page includes this tag:
<Wizard :steps="steps" :object="project" #submit="createProject"/>
The Wizard loops over the steps to create each component.
<div v-for="(step) in steps" :key="step.name">
<component v-if="step.status === 'current'" :is="step.content.component" />
</div>
The data to setup the component with the right props for the wizard itself and the child component props.
const steps = ref([
{
name: 'overview',
title: t('overview'),
subTitle: t('projectCreateOverviewDescription'),
status: 'current',
invalid: true,
content: {
component: Overview,
props: null,
model: {}
}
},
{
name: 'members',
title: t('members'),
subTitle: t('projectCreateMembersDescription'),
status: 'upcoming',
invalid: false,
content: {
component: ThumbnailList,
props: {
objects: users,
title: t('users'),
objectNameSingular: t('user'),
objectNamePlural: t('users'),
So far I've tried to dynamically create references in the wizard component to watch the state of the children but those refs are always null. This concept of a null ref seems to be the accepted answer elsewhere when binding to known child components, but with this dynamic setup, it doesn't seem to be the right route.
interface StepRefs {
[key: string]: any
}
let stepRefs: StepRefs = {}
props.steps.forEach(step => {
stepRefs[step.name] = ref(null)
watch(() => stepRefs[step.name].value, (newValue, oldValue) => {
console.log(newValue)
console.log(oldValue)
}, { deep: true })
})
Can anyone direct me to the right approach to take for this setup? I have a lot of these wizards in different places in the app so a component approach is really attractive, but if it comes to it I'll abandon the idea and move that layer of logic to the pages to avoid the dynamic aspect.
To handle changes in child components I'd recommend to use events. You can have the children emit an event on change or completion, and the wizard is listening to events from all children and handling them respectively.
On the wizard subscribe to the event handler of the step component, and process the data coming from each step on completion (or whatever stage you need).
This way you don't need any special data type for the steps, they can just be an array. Simply use a ref to keep track of the current step. You don't even need a v-for, if you just display one step at a time. For a wizard navigation you might still need a v-for, but it would be much simpler. Please see a rough example below.
<div>
<stepComponent step="currentStep" #step-complete="handleStepComplete"/>
<div>
<wizardNavigationItemComponent v-for="step in steps" :active="step.name === currentStep.name" />
</div>
</div>
<script setup lang="ts">
const steps = step[/*your step data here*/]
const currentStepIndex = ref(0)
const currentStep = ref(steps[currentStepIndex.value])
function handleStepComplete(data) {
/* handle the data and move to next step */
currentStepIndex.value = currentStepIndex.value + 1 % steps.length
}
</script>
In the component you just need to define the event and emit it when the data is ready, to pass along the data:
<script setup lang="ts">
const emit = defineEmits<{
(event: "stepComplete", data: <your data type>): void
}>()
/* call emit in the component when its done / filled */
emit("stepComplete", data)
</script>
I hope this helps and can provide a viable path forward for you!

Vue3 prop updates title attribute but not computed prop

I'm using an external library rendered using Vue3. It has the following component from a third part library [Edit: I realize the GitHub repo for that library is out of date, so updating with the actual code in my node_modules.]
<template>
<div class="socket" :class="className" :title="socket.name"></div>
</template>
<script>
import { defineComponent, computed } from "vue";
import { kebab } from "./utils";
export default defineComponent({
props: ["type", "socket"],
setup(props) {
const className = computed(() => {
return kebab([props.type, props.socket.name]);
});
return {
className
};
}
});
</script>
It renders based on a Socket object passed as a prop. When I updated the name property of the Socket, I see the title updated accordingly. However, the CSS/class does not update. I've tried $forceRefresh() on its parent, but this changes nothing.
Update: I was able to move the rendering code to my own repo, so I can now edit this component if needed.
Based on this updated code, it seems the issue is that the class is computed. Is there any way to force this to refresh?
The only time it does is when I reload the code (without refreshing the page) during vue-cli-service serve.
For reference, the | kebab filter is defined here:
Vue.filter('kebab', (str) => {
const replace = s => s.toLowerCase().replace(/ /g, '-');
return Array.isArray(str) ? str.map(replace) : replace(str);
});
Do filtered attributes update differently? I wouldn't think so.
I was also wondering if it could be a reactivity issue, and whether I needed to set the value using Vue.set, but as I understand it that's not necessary in Vue3, and it's also not consistent with the title properly updating.
Computed properties are reactive, however Vue does not expect you to mutate a prop object.
From the documentation:
Warning
Note that objects and arrays in JavaScript are passed by reference, so
if the prop is an array or object, mutating the object or array itself
inside the child component will affect the parent state and Vue is
unable to warn you against this. As a general rule, you should avoid
mutating any prop, including objects and arrays as doing so ignores
one-way data binding and may cause undesired results.
https://v3.vuejs.org/guide/component-props.html#one-way-data-flow
I know that this says, that you should not mutate it in the child, but the general rule is, that you should not mutate properties at all, but instead create new object with the modified data.
In your case the computed function will look for changes in the properties itself, but not the members of the properties, that is why it is not updating.

Automatic case transformation for events in vue3

I have a Vue3 component that emits multiple events. I want to pass the callbacks in a dedicated callback object like this:
// template section
<!-- emits event-a and event-b in kebap-case -->
<my-component v-on="myCallbacks" />
// script section
const myCallbacks={
'event-a': function() { // kebap-case does *not* work
...
},
eventA: function() { // camel-case works !
...
},
eventB: function() {
...
}
}
The docs state that in Vue3, unlike in Vue2, there is an automatic event name transformation (apparently into kebap-case).
Why can't I use the expected kebap-case in the callback?
Did I miss a more appropriate design pattern that could replace such a callback object and is still concise?
Is this name transformation on a callback object standard behavior or subject to change? Is it documented somewhere?
I hardly found anything in the docs about using v-on without the event specifier (i.e. v-on="callback" instead of v-on:event-a="eventA" v-on:event-b="eventB").
// Update
In the Vue2 doc and Vue3 doc, they mention that this is called the "object syntax" - however, nothing is written about case transformation.
<!-- object syntax (2.4.0+) -->
<button v-on="{ mousedown: doThis, mouseup: doThat }"></button>

Apply global variable to Vuejs

I have a javascript variable which I want to pass globally to Vue components upon instantiation thus either each registered component has it as a property or it can be accessed globally.
Note:: I need to set this global variable for vuejs as a READ ONLY property
Just Adding Instance Properties
vue2
For example, all components can access a global appName, you just write one line code:
Vue.prototype.$appName = 'My App'
Define that in your app.js file and IF you use the $ sign be sure to use it in your template as well: {{ $appName }}
vue3
app.config.globalProperties.$http = axios.create({ /* ... */ })
$ isn't magic, it's a convention Vue uses for properties that are available to all instances.
Alternatively, you can write a plugin that includes all global methods or properties. See the other answers as well and find the solution that suits best to your requirements (mixin, store, ...)
You can use a Global Mixin to affect every Vue instance. You can add data to this mixin, making a value/values available to all vue components.
To make that value Read Only, you can use the method described in this Stack Overflow answer.
Here is an example:
// This is a global mixin, it is applied to every vue instance.
// Mixins must be instantiated *before* your call to new Vue(...)
Vue.mixin({
data: function() {
return {
get globalReadOnlyProperty() {
return "Can't change me!";
}
}
}
})
Vue.component('child', {
template: "<div>In Child: {{globalReadOnlyProperty}}</div>"
});
new Vue({
el: '#app',
created: function() {
this.globalReadOnlyProperty = "This won't change it";
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.1.3/vue.js"></script>
<div id="app">
In Root: {{globalReadOnlyProperty}}
<child></child>
</div>
In VueJS 3 with createApp() you can use app.config.globalProperties
Like this:
const app = createApp(App);
app.config.globalProperties.foo = 'bar';
app.use(store).use(router).mount('#app');
and call your variable like this:
app.component('child-component', {
mounted() {
console.log(this.foo) // 'bar'
}
})
doc: https://v3.vuejs.org/api/application-config.html#warnhandler
If your data is reactive, you may want to use VueX.
You can use mixin and change var in something like this.
// This is a global mixin, it is applied to every vue instance
Vue.mixin({
data: function() {
return {
globalVar:'global'
}
}
})
Vue.component('child', {
template: "<div>In Child: {{globalVar}}</div>"
});
new Vue({
el: '#app',
created: function() {
this.globalVar = "It's will change global var";
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.1.3/vue.js"></script>
<div id="app">
In Root: {{globalVar}}
<child></child>
</div>
If the global variable should not be written to by anything, including Vuejs, you can use Object.freeze to freeze your object. Adding it to Vue's viewmodel won't unfreeze it.
Another option is to provide Vuejs with a frozen copy of the object, if the object is intended to be written globally but just not by Vue: var frozenCopy = Object.freeze(Object.assign({}, globalObject))
you can use Vuex to handle all your global data
In your main.js file, you have to import Vue like this :
import Vue from 'vue'
Then you have to declare your global variable in the main.js file like this :
Vue.prototype.$actionButton = 'Not Approved'
If you want to change the value of the global variable from another component, you can do it like this :
Vue.prototype.$actionButton = 'approved'
https://v2.vuejs.org/v2/cookbook/adding-instance-properties.html#Base-Example
If you’d like to use a variable in many components, but you don’t want to pollute the global scope. In these cases, you can make them available to each Vue instance by defining them on the Vue prototype:
Vue.prototype.$yourVariable = 'Your Variable'
Please remember to add this line before creating your Vue instance in your project entry point, most of time it's main.js
Now $yourVariable is available on all Vue instances, even before creation. If we run:
new Vue({
beforeCreate: function() {
console.log(this.$yourVariable)
}
})
Then "Your Variable" will be logged to the console!
doc: https://v2.vuejs.org/v2/cookbook/adding-instance-properties.html#Base-Example
If you want to make this variable immutable, you can use the static method Object.defineProperty():
Object.defineProperty(Vue.prototype, '$yourVariable', {
get() {
return "Your immutable variable"
}
})
This method by default will prevent your variable from being removed or replaced from the Vue prototype
If you want to take it a step further, let's say your variable is an object, and you don't want any changes applied to your object, you can use Object.freeze():
Object.defineProperty(Vue.prototype, '$yourVariable', {
get() {
return Object.freeze(yourGlobalImmutableObject)
}
})
A possibility is to declare the variable at the index.html because it is really global. It can be done adding a javascript method to return the value of the variable, and it will be READ ONLY. I did like that:
Supposing that I have 2 global variables (var1 and var2). Just add to the index.html header this code:
<script>
function getVar1() {
return 123;
}
function getVar2() {
return 456;
}
function getGlobal(varName) {
switch (varName) {
case 'var1': return 123;
case 'var2': return 456;
// ...
default: return 'unknown'
}
}
</script>
It's possible to do a method for each variable or use one single method with a parameter.
This solution works between different vuejs mixins, it a really global value.
in main.js (or any other js file)
export const variale ='someting'
in app.vue (or any other component)
import {key} from '../main.js' (file location)
define the key to a variable in data method and use it.
Simply define it in vite configuration
export default defineConfig({
root:'/var/www/html/a1.biz/admin',
define: {
appSubURL: JSON.stringify('/admin')
}, ..../// your other configurations
});
Now appSubURL will be accessible everywhere

VueJs child component props not updating instantly

I have a parent/child component setup where the parent is loading data from the server and passing it down to children via props. In the child I would like to instantiate a jQuery calendar with some of the data it receives from the parent.
In order to wait for the data before setting up the calendar, I broadcast an event in the parent that I have an event listener setup for in the child.
The listener is being fired in the child but if I this.$log('theProp'), it's undefined. However, if I inspect the components with the VueJs devtools, the parent/child relationship is there and the child has received the prop in the meantime.
The prop is defined on the child as a dynamic prop :the-prop="theProp". Since the child does receive the prop in the end, I'm assuming my setup is correct but there seems to be some sort of delay. The parent sets the props in the return function of the ajax call and again: it's working, just with a slight delay it seems.
I also tried registering a watch listener on the prop in the child so I could setup the calendar then and be sure that the prop is there. However, the watch listener fires, but this.$log('theProp') is still undefined.
If I pass the data along with the the broadcast call, like this.$broadcast('dataLoaded', theData) the child receives it just fine. But it seems wrong to do it that way as I'm basically building my own prop handler.
I'm not posting any code because the components are rather large and the VueJs devtools are telling me the parent/child situation is working.
Am I missing some information? Is there a slight delay between setting a value in the parent and the child receiving it? What would be the proper way to wait for parent data in the child?
Normally, when you're just rendering the data out into the template, the timing doesn't matter so much since the data is bound to the template. But in this case, I really need the data to be there to setup the calendar or it will be wrong.
Thanks.
edit 1: here's a jsfiddle: https://jsfiddle.net/dr3djo0u/1/
It seems to confirm that the data is not available immediately after the broadcast. However, the watcher does work, though I could almost swear that sometimes this.$log('someData') returned undefined when I setup that testcase.
But I guess my problem might be somewhere else, I'll have a look tonight, don't have the project with me right now.
edit 2: did some more tests. My problem was that a) event listeners do not seem to receive the data instantly and b) I was also trying to init the calendar in the route.data callback if someData was already around (e.g. when coming from parent), but that route callback is called before the component is ready, so it wasn't working there either.
My solution is now this:
// works when the child route is loaded directly and parent finishes loading someData
watch: {
someData() {
this.initCalendar();
}
},
// works when navigating from parent (data already loaded)
ready() {
if (this.someData && this.someData.length) {
this.initCalendar()
}
}
As far as I know, you should not need events to pass data from parent to child.
All you need is, in the child component: props: ['theProp']
And when using the child component in the parent: <child :theProp="someData"></child>
Now, wherever in the parent you change someData, the child component will react accordingly.
You don't need events, you don't need "watch", you don't need "ready".
For example: after an AJAX call, in the parent's "ready", you load some data:
// at the parent component
data: function () {
return {
someData: {}
}
},
ready: function () {
var vm = this;
$.get(url, function(response) {
vm.someData = response;
});
}
Now, you do not need anything else to pass the data to the child. It is already in the child as theProp!
What you really need to do is to have, in the child, something which reacts to data changes on its own theProp property.
Either in the interface:
<div v-if="theProp.id > 0">
Loaded!
</div>
Or in JavaScript code:
// at the child component
computed: {
// using a computed property based on theProp's value
awesomeDate: function() {
if (!this.theProp || (this.theProp.length === 0)) {
return false;
}
if (!this.initialized) {
this.initCalendar();
}
return this.theProp.someThing;
}
}
Update 1
You can also, in the parent, render the child conditionally:
<child v-if="dataLoaded" :theProp="someData"></child>
Only set dataLoaded to true when the data is available.
Update 2
Or maybe your issue is related to a change detection caveat
Maybe you're creating a new property in an object...
vm.someObject.someProperty = someValue
...when you should do...
vm.$set('someObject.someProperty', someValue)
...among other "caveats".
Update 3
In VueJS 2 you are not restricted to templates. You can use a render function and code the most complex rendering logic you want.
Update 4 (regarding OP's edit 2)
Maybe you can drop ready and use immediate option, so your initialization is in a single place:
watch: {
someData: {
handler: function (someData) {
// check someData and eventually call
this.initCalendar();
},
immediate: true
}
}
It's because tricky behavior in Vue Parent and Child lifecycle hooks.
Usually parent component fire created() hook and then mount() hook, but when there are child components it's not exactly that way: Parent fires created() and then his childs fire created(), then mount() and only after child's mount() hooks are loaded, parent loads his mount() as explained here. And that's why the prop in child component isn't loaded.
Use mounted() hook instead created()
like that https://jsfiddle.net/stanimirsp5/xnwcvL59/1/
Vue 3
Ok so I've spent like 1.5h trying to find out how to pass prop from parent to child:
Child
<!-- Template -->
<template>
<input type="hidden" name="_csrf_token" :value="csrfToken">
<span>
{{ csrfToken }}
</span>
</template>
<!-- Script -->
<script>
export default {
props: [
"csrfToken"
]
}
</script>
Parent
<!-- Template -->
<template>
<form #submit.prevent="submitTestMailForm" v-bind:action="formActionUrl" ref="form" method="POST">
...
<CsrfTokenInputComponent :csrf-token="csrfToken"/>
...
</form>
</template>
<!-- Script -->
<script>
...
export default {
data(){
return {
...
csrfToken : "",
}
},
methods: {
/**
* #description will handle submission of the form
*/
submitTestMailForm(){
let csrfRequestPromise = this.getCsrfToken();
let ajaxFormData = {
receiver : this.emailInput,
messageTitle : this.titleInput,
messageBody : this.bodyTextArea,
_csrf_token : this.csrfToken,
};
csrfRequestPromise.then( (response) => {
let csrfTokenResponseDto = CsrfTokenResponseDto.fromAxiosResponse(response);
this.csrfToken = csrfTokenResponseDto.csrToken;
this.axios({
method : "POST",
url : SymfonyRoutes.SEND_TEST_MAIL,
data : ajaxFormData,
}).then( (response) => {
// handle with some popover
})
});
},
/**
* #description will return the csrf token which is required upon submitting the form (Internal Symfony Validation Logic)
*/
getCsrfToken(){
...
return promise;
}
},
components: {
CsrfTokenInputComponent
}
}
</script>
Long story short
This is how You need to pass down the prop to child
<CsrfTokenInputComponent :csrf-token="csrfToken"/>
NOT like this
<CsrfTokenInputComponent csrf-token="csrfToken"/>
Even if my IDE keep me telling me yeap i can navigate with that prop to child - vue could not bind it.
solution (testing ok)
In child component just using the props data, no need to re-assignment props's values to data, it will be cause update bug!
vue child component props update bug & solution
https://forum.vuejs.org/t/child-component-is-not-updated-when-parent-component-model-changes/18283?u=xgqfrms
The problem is not how to pass data with props, but rather how to do two things at almost the same time.
I have an user account component that can edit users (with an user id) and add users (without id).
A child component shows checkboxes for user<->company assignments, and needs the user id to prepare API calls when the user account is saved.
It is important that the child component shows before saving the user account, so that things can be selected before the user is saved and gets an id.
So it has no user id at first: the id is passed to the child component as 'null'.
It updates when the user is stored and gets an id.
But at this point, it takes a very short time for the child to get the new id into its model.
If you call a function in the child component that relies on data that was just changing, it might happen that the function executes before the data is updated.
For cases like this, nextTick() is your friend.
import { nextTick } from 'vue';
...
saveAccount() {
axios.post(URL, this.userModel).then((result)) {
// our model gets an id when persisted
this.userModel.id=result.data.id;
nextTick( () => {
this.$refs.childComponent.doSomething();
});
}
}

Resources