ag grid not retrieving data when mounted with vue using axios - vue.js

I have this strange case when trying to retrieve data from mongoDB using axios not showing on grid. It should be already successful given the data can already loaded into the view (already tested it), but it's nowhere inside beforeMount, mounted, or ready hook.
I already tried with
this.gridOptions.onGridReady = () => {
this.gridOptions.api.setRowData(this.ticketData)
}
but only yields partial success (unreliable),
here's a code snippet to show what I mean,
<template>
<div class="ticketing">
<ag-grid-vue style="width: 100%; height: 350px;"
class="ag-fresh"
:gridOptions="gridOptions"
>
</ag-grid-vue>
{{testData}} <!--testData can be loaded-->
<input type="button" #click.prevent="showData" value="test"> </div>
</template>
<script>
//import stuff
//header and url stuff
export default {
//component stuff
data () {
return {
gridOptions: null,
ticketData: [],
testData: [] // only for testing purpose
}
},
methods: {
showData () {
console.log('data shown')
this.testData = this.ticketData // this is working
}
},
beforeMount () {
var vm = this
axios.get(ticketingAPIURL, {'headers': {'Authorization': authHeader}})
.then(function (response) {
vm.ticketData = response.data
}) // this is working
.catch(function (error) {
console.log(error)
})
this.gridOptions = {}
this.gridOptions.rowData = this.ticketData // this is not working
this.gridOptions.columnDefs = DummyData.columnDefs
}
// mount, ready also not working
}
</script>
To be more specific, I still can't determine what really triggers onGridReady of ag-grid in conjunction with Vue component lifecycle, or in other words, how can I replace button to show testData above with reliable onGridReady/Vue component lifecycle event?

You define vm.ticketData and after you call it like this.ticketData
You can change it by: this.rowData = vm.ticketData

You are setting this.gridOptions.rowData outside of the axios callback, so this.ticketData is still empty.
Set it inside the callback:
mounted() {
var vm = this
axios.get(ticketingAPIURL, {'headers': {'Authorization': authHeader}})
.then(function (response) {
vm.ticketData = response.data
vm.gridOptions = {}
vm.gridOptions.rowData = vm.ticketData
vm.gridOptions.columnDefs = DummyData.columnDefs
})
.catch(function (error) {
console.log(error)
})
}

it is due to overlapped intialization between axios, ag-grid, and vue.
after much tinkering, I am able to solve it with using Vue's watch function:
watch: {
isAxiosReady(val) {
if (val) {
this.mountGrid() // initiate gridOptions.api functions
}
}
}

Related

How to access a injected repository from a component's method

Lets say we injected this repository on a plugin/service-container.js
import nodeFetch from 'node-fetch'
import { AbortController as NodeAbortController } from 'node-abort-controller'
import HttpClient from '#/services/httpClient'
import PostRepository from '#/repositories/posts'
export default ({ app }, inject) => {
if (!process.client || app.context.env.NUXTJS_DEPLOY_TARGET === 'server') {
inject('postRepository', postRepository)
}
}
I have always acceded to API repositories from the asyncData method, like so:
export default {
async asyncData ({ $postRepository, }) {
const posts = await $postRepository.getAllPaginated(page, 11)
return {
posts,
}
}
}
But I need to access to it in a method, this is actually working but:
I doesn't look the right way because i'm caching in the component's data()
It fires this lint error:
Async method 'asyncData' has no 'await' expression.eslintrequire-await
What's the right way? I Can't find it online (the only examples I found involved using the Store)
export default {
async asyncData ({ $postRepository }) {
this.$postRepository = $postRepository
},
methods: {
async loadMore () {
if (this.page < this.posts.numPages) {
const posts = await this.$postRepository.getAllPaginated(this.page + 1, 11)
}
}
}
}
The error is coming from here
async asyncData ({ $postRepository }) {
this.$postRepository = [missing await here] $postRepository
},
From the documentation
This hook can only be used for page-level components. Unlike fetch, asyncData cannot access the component instance (this). Instead, it receives the context as its argument. You can use it to fetch some data and Nuxt will automatically shallow merge the returned object with the component data.
Hence, you cannot use any kind of this.loadMore in asyncData because it doesn't have access to the instance yet. So, inject is indeed the proper way of doing things.
With a plugin like that
export default ({ _ }, inject) => {
inject('customTest', async () => {
const response = await fetch('https://jsonplaceholder.typicode.com/todos/1')
return await response.json()
})
}
And a page like this
<template>
<div>
<pre>item: {{ item }}</pre>
</div>
</template>
<script>
export default {
async asyncData({ $customTest }) {
const item = await $customTest()
return { item }
},
}
</script>
It is not calling a method but you could totally use this.$nuxt.refresh() to fetch it again and increment the index of the repository call after an update in the store.
Which could be referenced like
await fetch(`https://jsonplaceholder.typicode.com/todos/${indexFromVuex}`)
You could of course keep it local too
<template>
<div>
<pre>item: {{ item }}</pre>
<button #click="fetchNewItem">fetch new item</button>
</div>
</template>
<script>
export default {
async asyncData({ $customTest }) {
const item = await $customTest()
return { item }
},
data() {
return {
index: 1,
}
},
methods: {
async fetchNewItem() {
this.index += 1
this.item = await this.$customTest(this.index)
},
},
}
</script>
So yeah, I don't think that there are other possible approaches with asyncData.
The fetch() hook is a bit more flexible but it's also totally different too regarding how it is working.
Anyway, with those 2 approaches you could totally have enough to solve the issue of your HTTP call.
It seems that an injected dependency can be accessed (in this case) with simply this.$postRepository inside any method so I didn't even need that asyncData

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.

How to access Vue $refs in a plugin?

methods: {
async create () {
this.disableSubmit = true;
await this.$firestore
.collection('collectionName')
.add(this.item)
.then(() => {
this.$refs.createForm.reset();
this.$notify('positive', 'Item successfully created!');
})
.catch(error => {
this.$notify('negative', 'ERROR! Try again later!', error);
});
this.disableSubmit = false;
},
}
If I use the code above inside the methods property, then everything works fine, but I would like to access that ref from outside the Vue component, for example a plugin, but it gives me an error.
TypeError: "_this.$refs is undefined"
Even when I just import it as a function, the error is the same, so I would like to know how to access the ref outside vue?
Bellow is the code for my plugin, and I would also like to point that I am using the quasar framework.
export let plugin = {
install (Vue, options) {
Vue.prototype.$plugin = async (collection, item) => {
return await firestore
.collection(collection)
.add(item)
.then(() => {
this.$refs.createFrom.reset();
notify('positive', 'Booking successfully created!');
})
.catch(error => {
notify('negative', 'ERROR creating booking! Try again later!', error);
});
};
}
};
I hope my question makes sense, and thanks in advance for any help
you could pass the context of your component, to apply the reset form from your plugin:
// plugin declaration
Vue.prototype.$plugin = async (collection, item, ctx) {
...
ctx.$refs.createFrom.reset()
...
}
then when u call to your plugin from yours components can do it like this:
// your component
methods: {
myFunction () {
this.$plugin(collection, item, this)
}
}
this is the reference of the context of your current component that will be used inside of your plugin
for example:
Vue.component('my-form', {
methods: {
resetForm() {
console.log('the form has been reset')
}
}
})
Vue.prototype.$plugin = (item, ctx) => {
console.log('item passed:', item)
ctx.$refs.refToMyForm.resetForm()
}
new Vue({
el: '#app',
data: {
item: 'foo'
},
methods: {
submit() {
this.$plugin(this.item, this)
}
}
})
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<div id="app">
<my-form ref="refToMyForm"></my-form>
<button #click="submit">submit</button>
</div>

Cannot set property after Get reques - Axios and HTML5 datalist

I am trying to do a GET request using Axios , but get the following error in console:
TypeError: Cannot set property 'films' of undefined
at eval (SearchBar.vue?e266:26)
SearchBar.vue
<template>
<section>
<input v-model='film' type='text' list='films'>
<datalist id='films'>
<option v-for='film in films' :key='film.episode_id'>{{film}}</option>
</datalist>
</section>
</template>
<script>
import axios from "axios";
export default {
name: "SearchBar",
data() {
return {
film: "",
films: []
};
},
created() {
axios
.get("https://swapi.co/api/films/")
.then(function(response) {
// handle success
//console.log(response);
this.films = response.data.results;
})
.catch(function(error) {
// handle error
console.log(error);
});
}
};
</script>
Anyone can tell me why I get the error? Note: I am running this locally for instant prototyping via Vue-Cli
One way is to use Arrow function:
created() {
axios
.get("https://swapi.co/api/films/")
.then((response) => {
// handle success
//console.log(response);
this.films = response.data.results;
})
.catch(function(error) {
// handle error
console.log(error);
});
}
2. Another way that = this & then use that inside promise callback
created() {
const that = this; // <-- assign this in that
axios
.get("https://swapi.co/api/films/")
.then(function (response) {
// handle success
//console.log(response);
that.films = response.data.results;
})
.catch(function(error) {
// handle error
console.log(error);
});
}

mounted method is fired before data loaded - VueJS

I'm using Vue Resource to retrieve an images collection from a REST API. The request is sent in the created hook of my Vue component.
The problem is, I'm trying to access the retrieved data in the mounted hook, but the data isn't loaded.
I get this error in the console:
[Vue warn]: Error in mounted hook: "TypeError: Cannot read property 'forEach' of undefined"
Here is my component:
<script>
export default {
data() {
return { imgs : '' };
},
created() {
// the full url is declare in my main.js
this.imgs = this.$resource('acf/v3/pages/4');
this.imgs.query().then((response) => {
console.log('success', response);
this.imgs = response.data.acf.gallery;
}, (response) => {
console.log('erreur', response);
});
},
mounted() {
// get the ref="image" in my dom template
let imgs = this.$refs.image;
imgs.forEach((img) => {
// I do some stuff with imgs
});
}
}
</script>
If I wrap a setTimeout around the content of mounted, everything works fine.
So, I don't understand how I can wait for my data to load before the mounted hook is executed. Isn't this the role of the Vue lifecycle hooks?
Since the this.imgs.query() call is async, your mounted hook is being called before the then handler is setting this.imgs (which I'm assuming is being bound with v-for to an element in your template with an attribute ref="image"). So, even though the component has been mounted to the DOM, the $refs have not been set up yet.
I would make a method to "do some stuff with imgs" and then call that method in a $nextTick callback in the then handler of the async call. The callback passed to $nextTick will be "executed after the next DOM update cycle", meaning the $refs will be set up at that point.
<script>
export default {
data() {
return { imgs: '' };
},
created() {
// the full url is declare in my main.js
this.imgs = this.$resource('acf/v3/pages/4');
this.imgs.query().then((response) => {
console.log('success', response);
this.imgs = response.data.acf.gallery;
this.$nextTick(() => this.doStuffWithImgs());
}, (response) => {
console.log('erreur', response);
});
},
methods: {
doStuffWithImgs() {
// get the ref="image" in my dom template
let imgs = this.$refs.image;
imgs.forEach((img) => {
// I do some stuff with imgs
});
}
}
}
</script>
As shown in the Lifecycle Diagram of Vue instance. After Mounted Hook (which means we can access DOM), there is also beforeUpdate and updated hooks. These hooks can be used when data is changed. I think beforeUpdate or update hook can be used after getting data in created hook.
<script>
export default {
data() {
return { imgs : '' };
},
created() {
// the full url is declare in my main.js
this.imgs = this.$resource('acf/v3/pages/4');
this.imgs.query().then((response) => {
console.log('success', response);
this.imgs = response.data.acf.gallery;
}, (response) => {
console.log('erreur', response);
});
},
// here we can use beforeUpdate or updated hook instead of mounted
beforeUpdate() {
// get the ref="image" in my dom template
let imgs = this.$refs.image;
imgs.forEach((img) => {
// I do some stuff with imgs
});
}
}
I hope this helps.