Vue trigger request to fetch data from API - javascript

I'm fresh in Vue so this question can be dumb. I want to display data in Vue from my backend Rails API. The data should shows up each time a user enters the site. To do so I'm calling GET endpoint which is located below:
imports.js
const fetchSyncedProductsResultRequest = (self) => {
const jwtToken = self.$store.state.idToken;
return axios
.get(`/api/v1/imports/products/sync_result`, {
headers: {
Authorization: `Bearer ${jwtToken}`,
}
})
.then(response => {
self.unsyncedProducts = response.data['products']
})
};
export {
fetchSyncedProductsResultRequest
};
Expected JSON response from this GET will be:
{:products=>
[{:id=>"611ea9a7392ab50013cf4713", :name=>"2-Tone Hoodie", :code=>"SS22CH013", :result=>nil, :last_sync_at=>nil},
{:id=>"60ec84062f25d400150b351c", :name=>"5-Pocket Denim", :code=>"SS22WP014", :result=>nil, :last_sync_at=>nil},
{:id=>"61966dc83e81dd001731ccd7", :name=>"Zip Shirt Jacket", :code=>"FW22WT001", :result=>nil, :last_sync_at=>nil},
{:id=>"61d5cab6b41408001b0e9376", :name=>"Yankees Satin Varsity Jacket", :code=>"FW22WJ021", :result=>nil, :last_sync_at=>nil}]}
Inside my Vue file I've got:
sync_products.vue
<template>
<div>
<div class="panel">
<div class="panel-body">
<h4>Synchronize products</h4>
<div v-for="product in fetchedProductSyncStatus" :key="product" class="status">
{{product}}
</div>
</div>
</div>
</div>
</template>
<script>
import {
fetchUnsyncedProductsRequest,
} from '../../api/imports'
export default {
name: 'BackboneSyncProducts',
data(){
return{
fetchedProductSyncStatus: []
}
},
}
</script>
<style scoped>
</style>
Looks like request is not sent because nothing shows up and I don't see it in Network tab. What determines the sending of this request?

You need to hook the fetchUnsyncedProductsRequest function to Vue's lifecycles, like created or mounted. See: https://vuejs.org/guide/essentials/lifecycle.html
I would also change the function to just return the data.
const fetchSyncedProductsResultRequest = (token) => {
return axios
.get(`/api/v1/imports/products/sync_result`, {
headers: {
Authorization: `Bearer ${token}`,
}
})
.then(response => {
return response.data['products']
})
};
export {
fetchSyncedProductsResultRequest
};
Then add the created hook and add the response to fetchedProductSyncStatus
export default {
name: 'BackboneSyncProducts',
data() {
return{
fetchedProductSyncStatus: []
}
},
created () {
const jwtToken = this.$store.state.idToken;
fetchUnsyncedProductsRequest(jwtToken).then(data => {
this.fetchedProductSyncStatus = data
})
}
}
Edit: Fixed the self reference error you commented about. On that note it is bad practice to store token in the client like a store

Related

When data is passed from pages to layouts, it is rendered on the client side

I'm using Nuxt.js#2.15.8.
When passing data retrieved with asyncData to layouts, is there a way to render the passed data on the server side?
I was able to pass data from pages to layouts and display the data in LAYOUTS using the following method, but it was CSR.
/pages/index.vue
<script>
export default {
name: 'IndexPage',
layout: 'index',
async asyncData({ $api }) {
const { data } = await $api.fetchData() // data = 'foo'
return { data }
},
created() {
this.getData()
},
methods: {
getData() {
this.$nuxt.$emit('getData', this.data)
},
},
}
</script>
/layouts/index.vue
<template>
<div>
<Nuxt />
<p>{{ data }}</p>
</div>
</template>
<script>
export default {
data() {
return { data: '' }
},
created() {
this.$nuxt.$on('getData', this.setData)
},
methods: {
setData(data) {
this.data = data
},
},
}
</script>
Since it is a CSR, the screen will show empty data for a moment.
Is there any way to avoid CSR when passing data in $emit?
If not, I would like to know how to pass data from pages to layouts without using $emit.

FireBase dispatch function doesn't save to realtime DB

I need some help with a realtimeDB issue.Am using NuxtJS to store state and dispatch the state to DB.
My code is working fine regarding saving into to the DB. Whenever I want to edit it, I receive this 400 BAD Request error.
The same thing happens when i manually try to update info withing the Firebase realtime DB, I can't edit the line with the text.
ERROR:
vendor.js:387 PUT https://xxxx.firebaseio.com/posts.json-MI-Jym0mdX5jNNP89UH.json?auth=BIGKEY 400 (Bad Request)
My component
<template>
<div class="admin-post-page">
<section class="update-form">
<AdminPostForm :post="loadedPost" #submit="onSubmitted" />
</section>
</div>
</template>
<script>
import AdminPostForm from "#/components/Admin/AdminPostForm";
export default {
layout: "admin",
middleware: ['check-auth', 'auth'],
components: {
AdminPostForm
},
asyncData(context) {
return context.app.$axios
.$get(
process.env.baseUrl + "/posts/" +
context.params.postId +
".json"
)
.then(data => {
return {
loadedPost: { ...data, id: context.params.postId }
};
})
.catch(e => context.error());
},
methods: {
onSubmitted(editedPost) {
this.$store.dispatch("editPost", editedPost).then(() => {
this.$router.push("/admin");
});
}
}
};
</script>
The method from store is:
editPost(vuexContext, editedPost) {
return this.$axios
.$put(
"https://XXXX.com/posts.json" +
editedPost.id +
".json?auth=" +
vuexContext.state.token,
editedPost
)
.then(res => {
vuexContext.commit("editPost", editedPost);
})
.catch(e => console.log(e));
}
And my rules are:
{
"rules": {
".read": true,
".write": true
}
}
If you can help me with info regarding why I am not allowed, would owe you a lot!
Thank you!
Your URL contains two .json extensions, which won't work. You should only add .json after the full path of the JSON you are trying to write/update:
this.$axios
.$put(
"https://XXXX.com/posts/" +
editedPost.id +
".json?auth=" +
vuexContext.state.token,
editedPost
)

Accessing nuxt $store inside Dynamic Component

I'm developing a Promise-based modal component which provides the possibility of specifing a component as body of the modal itself. To achieve that result, I thought that a good solution would be using a dynamic component inside the modal template.
However, inside a NUXT application, if the component refers to the Vuex instance (this.$store), it turns out to be undefined (or better there is no $store object attribute). In the same way, any injection done inside plugins results undefined (e.g. inject('api', api) create the attribute $api, but it results undefined).
If I just use the component in the 'standard' way (e.g. placing it inside the page or another component template), everything works fine.
There should be some 'extra injection' that I should do before passing the component in a programmatic way.
Can anyone help me?
The NUXT project structure (simplified):
/pages/index.vue
/plugins/api.js
/store/auth.js
/components/HelloComponent.vue
/plugins/api.js
let api = {}
api.call = function (request, auth, unpack, axios = this.axios) {
if (!request) Error('backend.js:call invalid params:', request, auth, unpack, axios)
if (auth) {
if (request.headers)
request.headers['Authorization'] = 'Bearer ' + this.auth.accessToken
else
request.headers = { 'Authorization': 'Bearer ' + this.auth.accessToken }
}
return axios(request).then((response) => unpack ? response.data : response)
}
api.getAPI = function (api, params, auth = true, unpack = true) {
if (!api) Error('api.js:getAPI invalid params:', api)
console.log('api.js:getAPI api:', api)
return this.call({ method: 'get', url: api, params: params }, auth, unpack)
}
api.postAPI = function (api, params, data, auth = true, unpack = true) {
if (!api) Error('api.js:postAPI invalid params:', api, data)
console.log('api.js:postAPI api:', api)
return this.call({ method: 'post', url: api, params: params, data: data }, auth, unpack)
}
/*******************************************************/
/* NUXT plugin and reference injection */
/*******************************************************/
export default function (context, inject) {
console.log('[CALL] api.js')
/* assign global $axios instance */
api.axios = context.$axios
/* assign auth instance to access tokens */
api.auth = context.store.state.auth
/* inject backend reference into application instance */
inject('api', api)
}
/pages/index.vue
<template>
<div>
<span>
{{ $store.auth.state.name }} // -> Displays 'Chuck'
</span>
/* Object.keys(this).includes('$store): false'; Object.keys(this).includes('$auth): true' */
<component :is="cComponent" /> // -> this.$store is undefined; auth: undefined
<hello-component /> // -> Displays 'Chuck'; auth: Object {...}
</div>
</template>
<script>
import HelloComponent from '../components/HelloComponent.vue'
export default {
components: {
HelloComponent
},
created () {
this.$store.commit('auth/setName', 'Chuck')
},
computed: {
cComponent () {
return HelloComponent
}
}
}
</script>
/components/HelloComponent.vue
<template>
<span>
{{ $store.auth.state.name }}
</span>
</template>
<script>
export default {
created() {
console.log('auth:', this.$auth)
}
}
</script>
/store/auth.js
export const state = () => ({
accessToken: null,
refreshToken: null,
name: null,
})
export const mutations = {
setAccessToken(state, token) {
console.info('auth.js:setAccessToken', token)
state.accessToken = token
},
setRefreshToken(state, token) {
console.info('auth.js:setRefreshToken', token)
state.refreshToken = token
},
setName(state, name) {
console.info('auth.js:setName', name)
state.user = name
},
}
if you have no access of this pointer in Nuxt project, And you really need to access store, then simply use
window.$nuxt.$store instead of this.$store;
Hope it will solve your problem

Is it possible to use startUndoable with custom action in react-admin?

I wondered if passing a custom action with a custom fetch and type (which is not update) to startUndoable is feasible.
Or is it possible that somehow define a pattern with values in meta and based on this pattern the view would be re-rendered?
In this case the IMPORT is updating only one property in the database with a fixed value.
This is the action:
export const importParcel = ({ id }) => ({
type: IMPORT_PARCEL,
payload: {
id
},
meta: {
resource: 'parcels',
fetch: IMPORT,
refresh: true,
onSuccess: {
notification: {
body: 'Parcel Imported',
level: 'info'
}
},
onFailure: {
notification: {
body: 'Error: Import failed',
level: 'warning'
}
}
}
});
This is the handler:
fetchUtils
.fetchJson(`/${resource}/import/${params.id}`, {
method: 'PUT',
headers: getAuthenticationHeaders()
})
.then(res => ({ data: res.json }));
Thanks for your help! :)
Sure, as explained in the Optimistic Rendering and Undo documentation you can create whatever action you want with startUndoable:
import { startUndoable as startUndoableAction } from 'ra-core';
class App extends Component {
handleImport = () => {
this.props.startUndoable(importParcel());
};
render() {
return <Button onClick={this.handleImport}>Import Parcel</Button>;
}
}
export default connect(null, { startUndoable: startUndoableAction })(App);
You action must have a onSuccess notification in order to display the undo button.
The rest should be implemented in your data provider.

VueJS throws errors because some datas are not ready yet

I'm rather new to VueJS, and I would like to add a picture loaded from an API as a background image. However, the image loading eventually works.
Here is the code:
<template>
<div id="bio" :style="{ backgroundImage: 'url(' + this.settings.bio_bg.url +')' }">
<h1>Biography</h1>
<router-link to="/">Home</router-link><br />
<span>Biography</span><br />
<router-link to="/shop">Shop</router-link><br />
<router-link to="/contact">Contact</router-link><br />
</div>
</template>
<style scoped>
</style>
<script>
export default {
data () {
return {
settings: {},
bio: {}
}
},
created () {
.catch(error => this.setError(error))
this.$http.secured.get('/settings')
.then(response => {
this.settings = response.data
console.log(this.settings)
})
.catch(error => this.setError(error))
}
}
</script>
The image is loaded, but my console returns two errors:
Error in render: "TypeError: Cannot read property 'url' of undefined"
Cannot read property 'url' of undefined
I guess that since the Axios call is asynchronous, everything arrives after the page is done loading, but is still loaded after.
What would the proper way be to correctly wait for data to be available? I tried a few things that I know from React, but it doesn't load at all (even though the errors stop showing up)
Thank you in advance
Yo need to be sure that this.settings.bio_bg.url exist from the component birth, so compiler doesn't broke trying to render it. In order to do so, just setup a 'fake' url in the original data of the component:
export default {
data () {
return {
settings: {
bio_bg: {
url: '',
}
},
bio: {}
}
},
created () {
this.$http.secured.get('/settings')
.then(response => {
this.settings = response.data
console.log(this.settings)
})
.catch(error => this.setError(error))
}
}
This not only prevent errors, also provides better intellisense since now your code editor can now that settings has a bio_bg member which in turn has an url.
If instead of ' ', you provide a real image url with a placeholder img, probably the UI will be nicer:
data () {
return {
settings: {
bio_bg: {
url: 'http://via.placeholder.com/350x150',
}
},
bio: {}
}
}

Categories