Vue3 Emit Event not triggering method in parent - vue.js

After making an API call I want to stop a loading spinner from displaying. Before calling the child component I set the this.showLoader property to true. This displays the spinner graphic. However once the API call has been made the graphic does not disapear. The updateLoader method never gets called.
child.vue
export default {
methods: {
fetchData() {
fetch(url, options)
.then((response) => response.json())
.then(this.$emit('hideLoaderEvent', false));
}
}
}
parent.vue
<template>
<MyComponent #hideLoaderEvent="updateLoader" />
</template>
export default {
data() {
return {
showLoader: false,
},
methods: {
updateLoader() {
this.showLoader = false;
}
}
}

Related

Nuxt Loader - Throttle for Custom Loader

I'm using a custom loader component for my project, and my nuxt config looks like this:
loading: '~/components/common/loading.vue'
The problem is that this component doesn't throttle a few milli-seconds and with every page change, this flickers and causes a bad user experience. Is there any way to add a throttle as we'd normally add for the default component like throttle: 200 inside the loading object like,
loading: { throttle: 200 }
Since my loading option doesn't have an object, instead has a string/path to my custom loading component, I'm not sure what to do here.
Reference: https://nuxtjs.org/docs/2.x/features/loading
This is how I use a custom loading component using Vuetify overlay component with a throttle:
<template>
<v-overlay :value="loading">
<v-progress-circular
indeterminate
size="64"
/>
</v-overlay>
</template>
<script>
export default {
data: () => ({
loading: false
}),
methods: {
clear () {
clearTimeout(this._throttle)
},
start () {
this.clear()
this._throttle = setTimeout(() => {
this.loading = true
}, 200)
},
finish () {
this.clear()
this.loading = false
}
}
}
</script>
This is inspired by the Nuxt default loading component.
You could add a setTimeout within your start() method in your custom loader component ~/components/common/loading.vue.
methods: {
start() {
setTimeout(() => {
this.loading = true;
}, 2000);
},
finish() { ... }
}

Show HTML content with events, loaded from the backend in Vue template

I need to show an image and HTML content with events in the template.
The HTML of the template comes in part from the backend and I need to do a treatment on the front end.
I need to put an image in the new HTML.
I'm doing it this way, but it doesn't work.
The image is always empty.
<template>
<div
v-html="resultado"
></div>
</>
data: ()=>({
resultado:null
}),
mounted(){
fillElement();
},
computed:{
getImage() {
return require("#/assets/pdf.png");
},
},
methods:{
fillElement(){
//get html from backend
const ohtml=getHtmlFrmBackEnd();
let p1 = `<div>Image<img :src='getImage()'></img>${ohtml}</div>`;
this.resultado = p1;
},
}
Solution:
<template>
<div>
<component :is="resultado"></component>
</div>
</template>
<script>
import Vue from "vue";
export default {
data: () => {
return {
resultado: null
};
},
computed: {
compiledData() {
return {
resultado: null
};
}
},
methods: {
delay() {
//making a backend call
return new Promise(resolve => {
setTimeout(() => {
resolve(
"<input type='button' name='btnVoltar' id='btnVoltar' value=' Voltar ' class='button' v-on:click='fVoltar()'>"
);
}, 1000);
});
},
replace(content) {
this.resultado = Vue.component("template-from-server", {
template: content,
methods: {
fVoltar() {
console.log("click");
}
}
});
},
async fillElement() {
//get html from backend
const ohtml = await this.delay();
let p1 = `<div>Image<img src='${require("#/assets/logo.png")}'></img>${ohtml}</div>`;
this.replace(p1);
}
},
mounted() {
this.fillElement();
}
};
</script>
Working Code Example
You can see I loaded the image directly into the src and called fillElement() with this keyword in the mounted() hook.
I also added a delay function to demonstrate a request to the backend.
Edit:
In order to handle events coming with the template from the backend, I created a mini component within the current component that will get rendered once the content is passed. For that, I had to locally import Vue.
Please keep in mind that you will need to replace onclick with #click or v-on:click. You can use regex for that as you have done so already.

vuejs $emit in confirm dialog invalid

I tried to get the request sent by the child page from the parent page, but it was unsuccessful。
1.parent code
<do-more #onreloadtab="reloadTab" :selectFolder="selectFolder"></do-more>
methods: {
reloadTab:function(){
console.log('reload')
}
}
2.child code
methods: {
async delete (row) {
let that = this
await this.$confirm("Are you sure to delete?", "confirm")
.then((config) => {
that.$emit('onreloadtab')
return
})
.catch(() => {
});
}
why parent cannot get emit message?
You don't need to use a then method after using await. The then method is called after Promises but await returns the value inside of a Promise, rather than a Promise. So, try calling emit after the await statement without the then method block.
In order to catch an event in your parent, Your need created() here is how you log the event in your parent
<template>
<do-more :selectFolder="selectFolder"></do-more>
</template>
<script>
export default {
data(){
return{
}
},
created() {
reloadTab(event){
console.log(event)
}
}
}
</script>

The prop object's property is undefined in refresh function

I use Vue.js and have a component. I pass a prop "request" to that component:
<adjustments-list
v-if="request"
:request="request"
/>
In the component I'm able to do this:
<text-input
:value="request.id"
/>
It works that is the value of "id" is displayed.
In props section of component:
props: {
request: Object
In mounted hook of component:
async mounted () {
await this.refresh()
},
In refresh function of component:
async refresh () {
console.log('this.request.id =', this.request.id)
if (this.request.id) {
const data = await requestApi.getRequestResultAdjustmentByReqId(this.request.id)
}
},
The this.request.id is undefined.
I'm not sure why.
If the request property is asynchronously available to the component then, you have to use combination of watchers like:
// adjustments-list component
new Vue({
props: {
request: Object
},
data() {
return {
apiData: null
}
},
watch: {
request(newValue, _oldValue) {
this.refresh(newValue);
}
},
mounted: function () {
// Do something here
},
methods: {
refresh (request) {
if (request.id) {
// Using promise instead of async-await
requestApi.getRequestResultAdjustmentByReqId(request.id)
.then(() => this.apiData = data);
}
}
}
});
Also, note that, mounted should be a plain old JS function and not an async function. That's the lifecycle method of the component supposed to behave in particular way.

Vue: Make a child component be aware of a change in a property modified by its parent

I have a child component that's basically a search box. When the user types something and presses enter, an event is fired that goes to the parent with the search topic:
export default {
name: "SearchBar",
methods: {
searchRequested(event) {
const topic = event.target.value;
this.$emit('searchRequested', topic);
}
}
};
The parent receives the event and updates a prop connected to other of its children (an image gallery):
<template>
<div id="app">
<SearchBar #searchRequested="onSearchRequested($event)" />
<Images :topic="topic" />
</div>
</template>
<script>
import SearchBar from './components/SearchBar.vue'
import Images from './components/Images.vue'
export default {
name: 'app',
components: {
SearchBar,
Images
},
data() {
return {
topic: ''
};
},
methods: {
onSearchRequested(topic) {
this.topic = topic;
}
}
}
</script>
So far, so good. But now I want the child component load itself with images related to the searched topic whenever the user performs a new search. For that, the child component Images must be aware of a change on its property topic, so I created a computed one:
import { ImagesService } from '../services/images.service.js';
export default {
data() {
return {
topic_: ''
};
},
methods: {
updateImages() {
const images = new ImagesService();
images.getImages(this.topic_).then(rawImages => console.log(rawImages));
}
},
computed: {
topic: {
get: function() {
return this.topic_;
},
set: function(topic) {
this.topic_ = topic;
this.updateImages();
}
}
}
};
But unfortunately, the setter never gets called. I have to say I'm new in Vue, so probably I'm doing something wrong. Any help will be appreciated.
You don't need to create computed in the main component. Images component is already aware of the changes in the topic prop.
You need to watch the changes of topic and do an async operation in 'Images.vue'. It's possible with Vue's watchers.
Vue docs watchers
'./components/Images.vue'
<template>...</template>
<script>
export defult {
props: ['topic'],
data(){
return {
images: []
}
},
watch: {
topic(newVal){
// do async opreation and update data.
// ImageSerice.get(newVal)
// .then(images => this.images = images)
}
}
}
</script>