I am trying to create a web app based on a database. Setup: NodeJS and a Vuejs 2 app generated with the CLI (with Webpack). Currently, I am using axios to retrieve records into an object. Based on that object I want to draw some svg lines from certain points to other points. The method works completely as designed when running it from an #click (v-on directive). However, when I try to add it to the created hook it doesn't work. No errors displayed. It's just not running. Does anyone no why? Code example below.
<template>
<div class="holder">
<step v-for="item in steps"></step>
<events v-for="point in points"></events>
<button #click= "createArrows">Test</button>
</div>
</template>
<script>
import axios from 'axios'
import Step from './Step.vue'
import Events from './Events.vue'
export default {
name: 'Graph',
data () {
return {
steps: '',
events: '',
points: []
},
components: {
Step, Events
},
methods: {
getSteps: function() {
let getsteps = this
axios.get('localhost:8000/api/steps')
.then(function (response) {
getsteps.steps = response.data
})
.catch(function (error) {
getsteps.steps = "Invalid request"
})
},
getEvents: function() {
let getevents = this
axios.get('localhost:8000/api/events')
.then(function (response) {
getevents.events = response.data
})
.catch(function (error) {
getevents.events = "Invalid request"
})
},
createArrows: function() {
},
created() {
this.getSteps(),
this.getEvents(),
this.createArrows()
}
}
EDIT: Promises are already included in the axios library. Since I am new to this concept I missed this one. Refactored code below:
methods: {
getData: function() {
let getdata = this
axios.all([
axios.get('localhost:8000/api/steps'),
axios.get('localhost:8000/api/events')
])
.then(axios.spread(function (stepResponse, eventResponse) {
console.log('success')
getdata.steps = stepResponse.data
getdata.events = eventResponse.data
getdata.createArrows()
}))
.catch(function (error) {
console.log("Invalid request")
})
},
createArrows: function() {
}
},
created() {
this.getData()
}
}
</script>
I think it's a classic async issue.
With v-on, your call to createArrows is "timewise after" getSteps and getEvents: meaning that getSteps and getEvents have finished executing their internal ajax promises, have populated the relevant data into the component instance for createArrows to find and access.
However, inside the created() hook, if you think about it, the calls fall through to createArrows() instantaneously (before the promisy things inside getSteps and getEvents have finished).
You'll have to refactor the call to createArrows inside created() as promise resolve for it work there correctly.
Related
I'm trying to implement Laravel's authorization & policy in Vue, by implementing a mixin which sends a GET request to a controller in the backend.
The problem is the v-if directive is receiving a Promise, which obviously does not resolve
Below is a very simplified version of what I'm trying to do:
The global mixin, auth.js
import axios from "axios"
export default {
methods: {
async $can (permission, $model_id) {
let isAuthorized = false;
await axios.get(`/authorization?${permission}&${model_id}`)
.then(function (response) {
isAuthorized = response.data.isAuthorized
})
.catch((error) => {
isAuthorized = false;
});
return isAuthorized;
}
}
}
The main entry file, app.js
import Auth from '#/auth';
Vue.mixin(Auth);
...
new Vue({...})
Component.vue
<template>
<div>
<div v-if="$can('do-this', 12)">
Show Me
</div>
</div>
</template>
<script>
export default {}
</script>
Is there any way to 'await' the async $can operation in v-if? Or am I approaching this from a totally wrong direction?
You don't need async/await there because axios returns a promise. I think you can call that function from the created hook. Instead of returning a value, change the related data attribute, and use it in v-if like so:
<div v-if="permissions['do-this__12']">
data() {
return {
permissions: {
'do-this__12': false,
'or-this__13': false,
},
}
}
methods: {
getPermissions() {
for (const key in this.permissions) {
this.can(key.split('__')[0], key.split('__')[1])
}
},
can(permission, model_id) {
axios.get(`/authorization?${permission}&${model_id}`)
.then(response => {
this.permissions[`${permission}__${model_id}`] = response.data.isAuthorized
})
.catch(error => {
this.permissions[`${permission}__${model_id}`] = false;
});
},
}
created() {
this.getPermissions();
}
I didn't try my code, let me know if it fails. BTW, extracting this implementation to a mixin will be a better idea. If you like to do that, just leave "permissions" object in the component and move everything else to the mixin.
But that approach isn't effective when you need multiple API calls for permissions. That's why I think you should pass the whole permissions object to the backend and make the work in the server:
iPreferThisBecauseOfSingleAPICall() {
axios.get(`/authorization`, this.permissions)
.then(({ data }) => this.permissions = data)
}
// AuthorizationController
public function index(Request, $request)
{
$permissions = [];
foreach($request->all() as $permission) {
// run your backend code here
}
return $permissions;
}
One final note, instead of asking for permission each time, loading all permissions at one can be the best idea.
There is such a code:
<template>
<div class="wrapper">
</div>
</template>
<script>
import axios from 'axios';
export default{
created () {
console.log('222');
this.getTrackerIdData();
this.getTrackerData();
},
methods: {
getTrackerIdData () {
return axios.get("https://seo-gmbh.eu/couriertracker/json/couriertracker_api.php?action=tracking_new.create" , {
})
.then(response => {
this.$store.commit('tracker/setTrackingKeyId', response.data.data.tracking_new_key_id);
this.$store.commit('tracker/setQrCodeUrl', response.data.data.filename_qr_code_tracking_new);
})
.catch(function (error) {
console.log(error);
});
},
getTrackerData () {
setInterval(()=>myTimer(this), 60000);
function myTimer(th) {
return axios.get("https://seo-gmbh.eu/couriertracker/json/couriertracker_api.php?action=get_tracking_data&key_id=" + th.$store.state.tracker.trackingKeyId , {
})
.then(response => {
th.$store.commit('tracker/setTrackingServerData', response.data.data.tracking_data);
})
.catch(function (error) {
console.log(error);
});
}
},
}
}
</script>
When starting such a solution in the project, the server-side developer informed me that at least the request method getTrackerIdData () on its side works twice!
Having placed the code (console.log ('222');) in the hook of the created lifecycle (where the method calls), I found that it is displayed twice in the firebug:
Question:
Why is this happening and what approach is right in this case from the point of view of the implementation of receiving data from the server?
P.S. If everything is called in the mounted hook, then the code works, including on the server side, only 1 time.
It is important to know that in any Vue instance lifecycle, only beforeCreate and created hooks are called both from client-side and server-side. All other hooks are called only from the client-side.
so thats why created hook called 2 times and executing the console.log ('222'); twice
for reference you can read from here
im really going through hard times trying to figure out how to get my API data through Vuex, is there some body whom has accurate bibliography of how to do this step by step, or even better help me with this code?
Formerly without using Vuex , but Vue all request worked perfectly, but now i dont understand clearly what i should do, here sharing part of my code:
data() {
return {
testArray: []
};
methods: {
getJsonData() {
fetch(
"https://app.ticketmaster.com/discovery/v2/events.json?countryCode=" +
this.countriesDrop +
"&apikey=xxxxxxxxxxxxxxxxxxxxxxxx",
{
method: "GET"
}
)
.then(response => {
return response.json();
})
.then(test => {console.log(this.testArray)
this.testArray = test._embedded.events;
})
.catch(err => {
console.log(err);
});
},
watch: {
countriesDrop: function(val) {
this.getJsonData();
}
},
As you can see in the request also is included an external element which make it changes attuning with the watcher and the value the user might asign.
I already got set Vuex and all else pluggins...just dont know how to act like , thus would appreciate an accurate link or tutorial either help with this basic problem resolved on detail step by step, .....thanks!
In your code there's nothing with Vuex. I guessed you want to set the state so that the getJsonData() method is called according to what's in the store.
Here's a snippet as an example of handling async in a Vuex environment.
const store = new Vuex.Store({
state: {
testArray: []
},
mutations: {
setTestArray(state, data) {
state.testArray = data
}
},
actions: {
getJsonData({
commit
}, countriesDrop) {
if (countriesDrop && countriesDrop !== '') {
fetch(`https://jsonplaceholder.typicode.com/${countriesDrop}`, {
method: "GET"
})
.then(response => {
return response.json();
})
.then(json => {
commit('setTestArray', json)
})
.catch(err => {
console.log(err);
});
}
}
}
})
new Vue({
el: "#app",
store,
computed: {
getDataFromStore() {
return this.$store.state.testArray
}
},
methods: {
getData(countriesDrop) {
this.$store.dispatch('getJsonData', countriesDrop)
}
}
})
<script src="https://cdn.jsdelivr.net/npm/es6-promise#4/dist/es6-promise.auto.js"></script>
<script src="https://unpkg.com/vuex#3.1.2/dist/vuex.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<button #click="getData('todos')">GET TODOS</button>
<button #click="getData('albums')">GET ALBUMS</button>
<ol>
<li v-for="data in getDataFromStore">{{data.title}}</li>
</ol>
</div>
The point is that Vuex is a central element in a Vue-Vuex application. You can store app state, handle async and sync functions (actions, mutations) with it, and all your Vue components can rely on the state - that should be the "single source of truth".
So, you get your input from a component (the Vue instance in this snippet), and dispatch an action that is available in the Vuex store. If the action needs to modify the state, then you call a mutation to do that. With this flow you keep reactivity for all your components that use that state.
I used a computed to get data from the Vuex store, but getters can be set also.
This way you don't "pollute" your components with functions and data that should be in the store.
How do I fetch data once a component has been mounted? I start my vue instance and then load in the component, the component template loads in fine but the function calls in mounted are never run so the stats object remains empty, in turn, causing errors in the component/template that requires the data.
So how do I run a certain function on component load?
For what its worth... the functions I want to call will all make REST requests but each component will be running different requests.
Vue.component('homepage', require('./components/Homepage.vue'), {
props: ["stats"],
mounted: function() {
this.fetchEvents();
console.log('afterwards');
},
data: {
loading: true,
stats: {}
},
methods: {
fetchEvents: function() {
this.$http.get('home/data').then(function(response) {
this.stats = response.body;
this.loading = false;
}, function(error) {
console.log(error);
});
}
}
});
const vue = new Vue({
el: '#main',
mounted: function() {
console.log('main mounted');
}
});
You are already doing it fine by putting all the initialization stuff into mounted. The reason your component is not refreshing is probably because of binding of this, as explained below:
In your fetchEvents function, your $http success handler provides a response, which you are attempting to assign to this.stats. But it fails because this points to that anonymous function scope and not to Vue component.
To overcome this issue, you may use arrow functions as shown below:
fetchEvents: function() {
this.$http.get('home/data').then(response => {
this.stats = response.body;
this.loading = false;
}, error => {
console.log(error);
});
}
Arrow functions do not create its own scope or this inside. If you use this inside the arrow function as shown above, it still points to Vue component, and therefore your component will have its data updated.
Note: Even the error handler needs to use arrow function, so that you may use this (of Vue component) for any error logging.
I built an app on Laravel 5.3 using vue.js and im starting to move over to vue.js to make the pages dynamic. I got everything working on a single page so want to convert that over to a component but after doing so I get the following error:
[Vue warn]: Error when rendering component <homepage> at C:\xampp\htdocs\.......
TypeError: Cannot read property 'nxt_weekly' of undefined
I was passing data to the view like so:
const app = new Vue({
el: '#app',
mounted: function () {
this.fetchEvents();
},
data: {
loading: true,
stats: []
},
methods: {
fetchEvents: function () {
this.$http.get('home/data').then(function (response) {
this.stats = response.body;
this.loading = false;
}, function (error) {
console.log(error);
});
}
});
In stats[] is where I hold the JSON response from the API and then call them all in my view like so:
<span class="stat" v-text="'stats.nxt_today'"></span>
....
....
This works but when I switch over to creating a component the errors listed above show, my new code is:
Vue.component('homepage', require('./components/Homepage.vue'),{
mounted: function () {
this.fetchEvents();
},
data: function () {
return{
loading: true,
stats: []
}
},
methods: {
fetchEvents: function () {
console.log('running here');
this.$http.get('home/data').then(function (response) {
this.stats = response.body;
this.loading = false;
}, function (error) {
console.log(error);
});
}
}
});
What am I doing wrong? How come my stats[] object is now empty when the component is trying to access it?
You need to pass your stats as a property to the component using v-bind, as shown below:
<homepage v-bind:stats="stats"></homepage>
The above needs to be done in your root template. And in your homepage component, you can receive the value in props as follows:
Vue.component('homepage', {
props: ["stats"],
mounted: function () {
// ...
},
// ...
}
Within the template of this component, you will be able to access stats.nxt_today normally.
By the way, is your stats an array or an object? You have defined it as an empty array initially, but accessing it as an object.
If you still have issues, you can use vue-devtools to view all the objects available within a component.