Passing the result of a Fetch function to Parent Prop - vue.js

I'm aware there is many questions about passing between Child to Parent Components, however, all the questions and guides I've seen use a Form Input to display how the $emit function works, and I have had no success in translating this to the result of an API call.
My question is, how do I refactor this code to pass the 'data' upwards into my parent components 'TranslatedText' Prop? It confuses me as I have no input, just the data response. Any tips appreciated.
ParentComponent.vue
<template>
<translation-button v-model="translatedText" />
</template>
props: {
translatedText: '',
},
ChildComponent.Vue
<template>
<b-button type="is-primary" #click="loadTranslations()">Übersetzen</b-button>
</template>
<script>
export default {
name: "TranslationButton",
props: {
TranslatedText: ''
},
methods: {
loadTranslations() {
fetch('http://localhost:3000/ccenter/cc_apis')
.then(function(response) {
return response.text();
})
.then(function(data) {
console.log(data);
})
.finally(() => {
this.$emit('loadTranslations', this.TranslatedText);
console.log('event is a success');
});
}
}
}
</script>

there are several ways to communicate between parent and child.
if you want to pass data from child to parent, you need to use $emit like the below code
child:
<template>
<b-button type="is-primary" #click="loadTranslations()">Übersetzen</b-button>
</template>
<script>
export default {
name: "TranslationButton",
props: {
TranslatedText: ''
},
methods: {
loadTranslations() {
fetch('http://localhost:3000/ccenter/cc_apis')
.then(function(response) {
return response.text();
})
.then(function(data) {
console.log(data);
})
.finally((payload) => { // change added
this.$emit('changeTitle','payload ') // change added
console.log('event is a success');
});
}
}
}
</script>
parent:
<template>
<translation-button #changeTitle="ChangeT" />
</template>
methods:{
ChangeT(title)
{
console.log(title)
},
}
this page is a simple example related to your problem

Related

Props arguments are not reactive in setup

I'm trying to develop a component that compiles the given html; I succeeded with constant html texts, but I can't make this work with changing html texts.
main.js
app.component("dyno-html", {
props: ["htmlTxt"],
setup(props) {
watchEffect(() => {
console.log(`htmlText is: ` + props.htmlTxt);
return compile(props.htmlTxt);
});
return compile(props.htmlTxt);
},
});
Home.js
<template>
<div class="home">
<dyno-html
:htmlTxt="html2"
:bound="myBoundVar"
#buttonclick="onClick"
></dyno-html>
-------------
<dyno-html
:htmlTxt="html"
:bound="myBoundVar"
#buttonclick="onClick"
></dyno-html>
</div>
</template>
<script>
export default {
name: "Home",
components: {},
data: function() {
return {
html: "",
html2: `<div> Static! <button #click="$emit('buttonclick', $event)">CLICK ME</button></div>`
};
},
mounted() {
// get the html from somewhere...
setTimeout(() => {
this.html = `
<div>
Dynamic!
<button #click="$emit('buttonclick', $event)">CLICK ME</button>
</div>
`;
}, 1000);
},
methods: {
onClick(ev) {
console.log(ev);
console.log("You clicked me!");
this.html2 = "<b>Bye Bye!</b>";
},
},
};
</script>
Outcome:
Console:
It seems the changes of htmlText arrives to setup function, but it doesn't affect the compile function!
This is the expected behaviour because prop value is read once and results in static render function.
Prop value should be read inside render function. It can be wrapped with a computed to avoid unneeded compiler calls:
const textCompRef = computed(() => ({ render: compile(props.htmlTxt) }));
return () => h(textCompRef.value);

Vue received a Component which was made a reactive object

The problem I need to solve: I am writing a little vue-app based on VueJS3.
I got a lot of different sidebars and I need to prevent the case that more than one sidebar is open at the very same time.
To archive this I am following this article.
Now I got a problem:
Vue received a Component which was made a reactive object. This can lead to unnecessary performance overhead, and should be avoided by marking the component with markRaw or using shallowRef instead of ref. (6)
This is my code:
SlideOvers.vue
<template>
<component :is="component" :component="component" v-if="open"/>
</template>
<script>
export default {
name: 'SlideOvers',
computed: {
component() {
return this.$store.state.slideovers.sidebarComponent
},
open () {
return this.$store.state.slideovers.sidebarOpen
},
},
}
</script>
UserSlideOver.vue
<template>
<div>test</div>
</template>
<script>
export default {
name: 'UserSlideOver',
components: {},
computed: {
open () {
return this.$store.state.slideovers.sidebarOpen
},
component () {
return this.$store.state.slideovers.sidebarComponent
}
},
}
</script>
slideovers.js (vuex-store)
import * as types from '../mutation-types'
const state = {
sidebarOpen: false,
sidebarComponent: null
}
const getters = {
sidebarOpen: state => state.sidebarOpen,
sidebarComponent: state => state.sidebarComponent
}
const actions = {
toggleSidebar ({commit, state}, component) {
commit (types.TOGGLE_SIDEBAR)
commit (types.SET_SIDEBAR_COMPONENT, component)
},
closeSidebar ({commit, state}, component) {
commit (types.CLOSE_SIDEBAR)
commit (types.SET_SIDEBAR_COMPONENT, component)
}
}
const mutations = {
[types.TOGGLE_SIDEBAR] (state) {
state.sidebarOpen = !state.sidebarOpen
},
[types.CLOSE_SIDEBAR] (state) {
state.sidebarOpen = false
},
[types.SET_SIDEBAR_COMPONENT] (state, component) {
state.sidebarComponent = component
}
}
export default {
state,
getters,
actions,
mutations
}
App.vue
<template>
<SlideOvers/>
<router-view ref="routerView"/>
</template>
<script>
import SlideOvers from "./SlideOvers";
export default {
name: 'app',
components: {SlideOvers},
};
</script>
And this is how I try to toggle one slideover:
<template>
<router-link
v-slot="{ href, navigate }"
to="/">
<a :href="href"
#click="$store.dispatch ('toggleSidebar', userslideover)">
Test
</a>
</router-link>
</template>
<script>
import {defineAsyncComponent} from "vue";
export default {
components: {
},
data() {
return {
userslideover: defineAsyncComponent(() =>
import('../../UserSlideOver')
),
};
},
};
</script>
Following the recommendation of the warning, use markRaw on the value of usersslideover to resolve the warning:
export default {
data() {
return {
userslideover: markRaw(defineAsyncComponent(() => import('../../UserSlideOver.vue') )),
}
}
}
demo
You can use Object.freeze to get rid of the warning.
If you only use shallowRef f.e., the component will only be mounted once and is not usable in a dynamic component.
<script setup>
import InputField from "src/core/components/InputField.vue";
const inputField = Object.freeze(InputField);
const reactiveComponent = ref(undefined);
setTimeout(function() => {
reactiveComponent.value = inputField;
}, 5000);
setTimeout(function() => {
reactiveComponent.value = undefined;
}, 5000);
setTimeout(function() => {
reactiveComponent.value = inputField;
}, 5000);
</script>
<template>
<component :is="reactiveComponent" />
</template>

How make json data available for my Vue dynamic routes

I have a List component where I fetch my date from db/blogs.json:
created() {
fetch('http://localhost:3000/blogs')
.then(response => {
return response.json();
})
.then(data => {
this.blogs = data;
})
},
In my BlogDetail.vue I have:
<script>
export default {
data: () => {
return {
blogId:this.$route.params.id
}
},
computed: {
blog() {
return this.blogs.find(
blog => blog.id === this.blogId
)
}
}
}
</script>
But how do I get the blogs data in this component, which I fetched in the List component?
Because now in the <template> section of my BlogDetail.vue I cannot access e.g. {{ blog.name }}
Update:
I try passing blogs with props:
Now I am accepting a prop in BlogDetails.vue:
props: {
blogs: {
type: Array
}
},
But from where (which component), I have to registering the prop like :blogs="blogs"?
Update 2:
This is what I have so far, link to the sandbox
Here is the working sandbox.
Firstly you need to import JSON data from your JSON file correctly. As:
<script>
import ListItem from "./ListItem";
import Blogs from "../../public/db/blogs.json";
export default {
name: "List",
components: {
ListItem
},
data() {
return {
blogs: Blogs.experiences
};
},
created() {}
};
</script>
Have to send props in the router-link as :
<router-link
:to="{ name: 'BlogDetails', params: { id: blog.id,blog:blog }}">More information
</router-link>
You can send props to the child component in the tag name, in your case:
//LIST component(PARENT)
<tamplate>
<BlogDetail :blogs="blogs"></BlogDetail> //CHILD component
</template>

Access the variables of a component

I'd like to access a component variable. This variable is modified by an html input.
Here is my current code
Parent :
<template>
<component>Child</component>
</template>
<script>
export default {
data () {
return {
dataChild : '',
}
</script>
Child :
<template>
<input type="text" name="data" v-model="data" class="form-control">
</template>
<script>
export default {
data () {
return {
data: ''
}
}
}
</script>
I've looked all over the internet but nothing works on my side or I'm doing wrong :(
Thanks in advance
you can use like below:
Parent
<template>
<component #input-child="childData"></component>
</template>
<script>
import Component from "#/components/Component";
export default {
components: { Component },
data() {
return {
dataChild: ""
};
},
methods: {
childData(data) {
console.log(data);
// you can assign it to dataChild variable
}
}
};
</script>
Child
<template>
<input
type="text"
name="data"
v-model="data"
#input="$emit('input-child', data)"
class="form-control"
/>
</template>
<script>
export default {
data() {
return {
data: ""
};
}
};
</script>
The standard way to send data from child component to parent in vue is to emit that inside child and listen for event inside parent. Like this:
Child:
export default {
data: () => ({ dataChild: 'TEST' }),
methods: {
send () {
this.$emit('data', this.dataChild)
}
},
mounted () { this.send() }
}
Parent:
<div>
<child #data="get"></child>
</div>
export default {
data: () => ({ data: '' }),
methods: {
get(value) {
this.data = value
}
}
}

VueJs Nested props coming through undefined

I am trying to access an array which is part of a prop (event) passed into a component, but when in created() or mounted() the array part of the event prop (the rest is fine) comes through as undefined.
As can be seen below, when I inspect the props in the vue chrome plugin, the registration_fields are there.
I can add a watcher to the event prop and can access the registration_fields that way, but this seems very awkward to have to do this to access already passed in data.
This is from the Chrome vue inspector:
event:Object
address1_field:"Some Address 1"
address2_field:"Some Address 2"
approved:true
registration_fields:Array[1]
This is what part of my vue file looks like:
export default {
props: ['event'],
data() {
return {
regFields: []
}
},
created() {
this.regFields = this.event.registration_fields // Undefined here!
},
watch: {
event() {
this.regFields = this.event.registration_fields //Can access it here
});
}
}
}
I am using Vue 2.4.4
This is how the component is called:
<template>
<tickets v-if="event" :event="event"></tickets>
</template>
<script>
import tickets from './main_booking/tickets.vue'
export default {
created() {
var self = this;
this.$http.get('events/123').then(response => {
self.event = response.data
}).catch(e => {
alert('Error here!');
})
},
data: function () {
return {event: {}}
},
components: {
tickets: tickets
}
}
</script>
Thank you
It actually works fine without the watcher.
new Vue({
el: '#app',
data: {
event: undefined
},
components: {
subC: {
props: ['event'],
data() {
return {
regFields: []
}
},
created() {
this.regFields = this.event.registration_fields // Undefined here!
}
}
},
mounted() {
setTimeout(() => {
this.event = {
registration_fields: [1, 3]
};
}, 800);
}
});
<script src="//cdnjs.cloudflare.com/ajax/libs/vue/2.4.2/vue.min.js"></script>
<div id="app">
<sub-c v-if="event" :event="event" inline-template>
<div>
{{regFields}}
</div>
</sub-c>
</div>
If, as Belmin Bedak suggests in the comment below, event is populated asynchronously, it comes in as undefined because it's undefined. In that case, you need a watcher, or, somewhat more elegantly, use a computed:
new Vue({
el: '#app',
data: {
event: {}
},
components: {
subC: {
props: ['event'],
computed: {
regFields() {
return this.event.registration_fields;
}
}
}
},
// delay proper population
mounted() {
setTimeout(() => { this.event = {registration_fields: [1,2,3]}; }, 800);
}
});
<script src="//cdnjs.cloudflare.com/ajax/libs/vue/2.4.2/vue.min.js"></script>
<div id="app">
<sub-c :event="event" inline-template>
<div>
{{regFields}}
</div>
</sub-c>
</div>