I have a service which extends EventEmitter.
services/service/service.js (relative to main.js)
import { EventEmitter } from "events";
class Service extends EventEmitter {}
Inside the Service class, I have the following method which returns a Promise:
/**
* Method to fetch the Active Survey by "surveySlug":
*/
fetchActiveSurvey(serviceSlug) {
return new Promise((resolve, reject) => {
axios.get(`${this.baseURL}/service/${serviceSlug}`, { headers: { Authorization: AuthStr } }).then(response => {
resolve(response.data);
}).catch(error => {
if (error.response && error.response.data.status != 200) {
Vue.$router.push({ name: 'home'});
}
});
});
}
I also have the usual routes.js in the same directory as main.js, which is working fine.
Services are then established as plugins:
import Service from "../services/service/service"; // <= Refrences `service.js`
export default {
install(Vue) {
Vue.prototype.$service = Service;
}
};
I have tried the following:
Vue.$router.push({ name: 'home'});
Vue.prototype.$router.push({ name: 'home'});
However, I seem to find that $router is not defined. What would be the best way to define the router push routes from inside this service?
You need to use the this keyword to access the router instance. Try:
this.$router.push({ name: 'home'});
Related
I have a locally hosted mongodb database with mongoose, express, axios, and a Vue front end. Right now I'm trying to access a single object from an exported array, but I'm missing the mark and getting "undefined" as the result.
vue.config.js:
module.exports = {
devServer: {
proxy: 'http://localhost:3000',
}
}
here's the front end Vue script meant to use the objects:
import axios from 'axios';
export default {
name: 'Game',
data () {
return {
pages: [],
currentPage: {},
pageTitle: "",
pageText: "",
options: [],
}
},
created () {
this.getPages();
},
methods: {
async getPages() {
try {
let res = await axios.get('/api/pages');
this.pages = res.data;
console.log(this.pages);
this.currentPage = this.pages[0];
console.log(this.currentPage);
return true;
} catch (error) {
console.log(error);
}
},
my "get" endpoint in pages.js:
router.get('/', async (req, res) => {
try {
let pages = await Page.find();
res.send({pages: pages}); //send result of search for pages as list of pages called "pages"
} catch (error) {
console.log(error);
res.sendStatus(500); //500 = server could not fulfill request
}
});
the route in server.js:
const pages = require('./routes/pages');
app.use('/api/pages', pages);
app.listen(3000, () => console.log('Server listening on port 3000!'));
module.exports = app;
and here's the console output, with the "pages" object from vue's data property and the "currentPage" that's supposed to be at pages[0] (printed to console in earlier example):
I can access the api at 'localhost:3000/api/pages' just fine, but how do I break into that array and access the first page object? I want to get an object from the list axios fetches from mongoose, then hold that object in a variable so I can access it's properties. The whole "pages > [[Target]] > pages > [ ]" is part of the problem I'm sure, but I don't know what to tell the code to open it.
Whoops! I realized my mistake. In pages.js I should have sent "res.send(pages);" After a whole couple days too XD
I am trying to use an instance of an object in a boot file where the instance is created in another boot file. The documentation [0] talks about using an object instance from a boot file and it works fine when using the instance in a component. I would like to access the instance in another boot file.
First boot file that creates the instance looks like this:
import { AuthService } from '../authorization/AuthService';
let oidc = null
export default ({ router, store, Vue }) => {
const OIDC = new AuthService();
router.beforeEach((to, from, next) => {
const allowAnonymous = to.matched.some(record => record.meta.allowAnonymous)
if (allowAnonymous) {
next()
} else {
var isAuthenticated = OIDC.isAuthenticated()
if (isAuthenticated) {
next()
} else {
OIDC.signIn()
}
}
})
Vue.prototype.$oidc = OIDC
oidc = OIDC
}
export { oidc }
And I am trying to use the oidc instance in another boot file like this:
import oidc from "boot/oidc-service"
import axios from 'axios'
let axiosInstance = null;
export default ({ app, router, store, Vue }) => {
const AxiosInstance = axios.create({
baseURL: window.env.BASE_URL
})
AxiosInstance.interceptors.request.use(function (config) {
return oidc.getAccessToken().then(token => {
config.headers.Authorization = `Bearer ${token}`
return config
})
}, (error) => {
return Promise.reject(error)
})
Vue.prototype.$axios = AxiosInstance
axiosInstance = AxiosInstance
}
export { axiosInstance }
I import them in the following order:
boot: [
'oidc-service',
'axios',
...
If I export the class instead of the instance, I can instantiate it and code works as expected. I would like for the oidc object to be a singleton however.
How can I use the instance of oidc in my axios setup?
[0] https://quasar.dev/quasar-cli/boot-files#Accessing-data-from-boot-files
Unless I'm missing something... if oidc is not null, return it, otherwise continue with the initialization:
import { AuthService } from '../authorization/AuthService';
let oidc = null
export default ({ router, store, Vue }) => {
if(oidc !== null) return oidc;
// else continue...
so I'm trying to get my Axios to do a get request with a param that'll end the url in
'/?user= {id}'
the id is passed in by my loggedInUser.id from Vuex. I know that async functions won't accept 'this' inside the call so I included store as a parameter. Something's still off with how I passed the data around thought I think. Would appreciate any help, thanks!
import { mapGetters } from "vuex";
export default {
computed: {
...mapGetters(["loggedInUser"])
},
head() {
return {
title: "Actors list"
};
},
components: {
EditProfile
},
async asyncData({ store }) {
try {
const body = { data: store.getters.loggedInUser.id };
const { actors } = await $axios.$get(`/api/v1/actors/`, {
params: {
user: body
}
});
return { actors };
} catch (e) {
return { actors: [] };
}
},
data() {
return {
actors: []
};
Edit
I got it to work when I removed the data: from 'const body' and removed the brackets as well around 'actor'
try {
const body = store.getters.loggedInUser.id;
const actors = await $axios.$get(`/api/v1/actors/`, {
params: {
user: body
}
});
You can access your params from Context.
Context is available in special nuxt lifecycle areas like asyncData, fetch, plugins, middleware and nuxtServerInit.
In Nuxt, with asyncData hook you can get query parameters from the route context key.
Please read the Nuxt.js Context documentation. The context provides additional objects/params from Nuxt to Vue components
With your-domain/?user=wonderman
asyncData({ route: { query: queryParams} }) {},
variable queryParams is an object:
{ user: "wonderman" }
I created a Vue component which exports an async function. This component acts as a wrapper for calling my API. It's based on axios with a caching component that relies on localforage for some short lived persistence.
import localforage from 'localforage'
import memoryDriver from 'localforage-memoryStorageDriver'
import { setup } from 'axios-cache-adapter'
export default {
async cache() {
// Register the custom `memoryDriver` to `localforage`
await localforage.defineDriver(memoryDriver)
// Create `localforage` instance
const store = localforage.createInstance({
// List of drivers used
driver: [
localforage.INDEXEDDB,
localforage.LOCALSTORAGE,
memoryDriver._driver
],
// Prefix all storage keys to prevent conflicts
name: 'tgi-cache'
})
// Create `axios` instance with pre-configured `axios-cache-adapter` using a `localforage` store
return setup({
// `axios` options
baseURL: 'https://my.api',
cache: {
maxAge: 2 * 60 * 1000, // set cache time to 2 minutes
exclude: { query: false }, // cache requests with query parameters
store // pass `localforage` store to `axios-cache-adapter`
}
})
}
}
Here is how I am importing and using this component in my views:
import api from '#/components/Api.vue'
export default {
data() {
return {
userId: this.$route.params.id,
userData: ''
}
},
methods: {
loadClient(userId) {
const thisIns = this;
api.cache().then(async (api) => {
const response = await api.get('/client/find?id='+userId)
thisIns.userData = response.data.data[0]
}).catch(function (error) {
console.log(error)
})
},
},
created() {
this.loadClient(this.userId)
},
}
I can import this component and everything appears to work. I get data back from my API. However, immediately after every call, I get an error:
TypeError: Cannot read property 'cache' of undefined
Which references this line:
api.cache().then(async (api) => {
I am unable to understand why this is happening, or what it means. The error itself indicates that the component I am importing is undefined, though that's clearly not the case; if it were, the API call would ultimately fail I would suspect. Instead, I am lead to believe that perhaps I am not constructing/exporting my async cache() function properly.
Upon further review, I don't actually understand why the author has implemented it the way he has. Why would you want to create an instance of localForage every single time you make an API call?
I've opted not to use a component and to only instantiate an instance of localForage once.
main.js
import localforage from 'localforage'
import memoryDriver from 'localforage-memoryStorageDriver'
import { setup } from 'axios-cache-adapter'
// Register the custom `memoryDriver` to `localforage`
localforage.defineDriver(memoryDriver)
// Create `localforage` instance
const localforageStore = localforage.createInstance({
// List of drivers used
driver: [
localforage.INDEXEDDB,
localforage.LOCALSTORAGE,
memoryDriver._driver
],
// Prefix all storage keys to prevent conflicts
name: 'my-cache'
})
Vue.prototype.$http = setup({
baseURL: 'https://my.api',
cache: {
maxAge: 2 * 60 * 1000, // set cache time to 2 minutes
exclude: { query: false }, // cache requests with query parameters
localforageStore // pass `localforage` store to `axios-cache-adapter`
}
})
the view
export default {
data() {
return {
userId: this.$route.params.id,
userData: ''
}
},
methods: {
loadClient(userId) {
const thisIns = this;
thisIns.$http.get('/client/find?id='+userId)
.then(async (response) => {
thisIns.userData = response.data.data[0]
})
.catch(function (error) {
console.log(error)
})
},
},
created() {
this.loadClient(this.userId)
},
}
I'm using Express Graphql server with react native and Relay. My device does connects to the subscription but it does not subscribe to it. Here's my index.js on the server
const subscriptionServer = SubscriptionServer.create(
{
execute,
subscribe,
schema,
onOperation: (message, params, webSocket) => {
console.log(params)
return params;
},
onConnect: () => {
// My device does connects
console.log("client connected")
}
},
{
server,
path: '/subscriptions'
},
);
app.use('/graphql', graphqlHTTP({
schema,
graphiql: true
}));
app.use('/graphiql', graphiqlExpress({
endpointURL: '/graphql',
subscriptionsEndpoint: `ws://127.0.0.1:8080/subscriptions`
}));
server.listen(PORT, ()=> {
console.log("Groceries running on port " + PORT)
console.log(
`subscriptions is now running on ws://localhost:${PORT}/subscriptions'}`
);
});
The resolver for subscription on the server, it was quite troublesome to figure out since everyone is using executable schema from apolloGraphql.
export default {
type: OrderEdges,
args: {
ShopId: {type: GraphQLID},
},
subscribe: withFilter(() => pubsub.asyncIterator('orderConfirmed'), (payload, variables) => {
console.log(payload)
console.log(variables)
return payload.orderConfirmed.node.ShopId == variables.ShopId;
}),
}
Now the react-native client. My subscription setup with relay environment.
const setupSubscriptions = (config, variables, cacheConfig, observer) => {
const query = config.text; //does console logs the query
const subscriptionClient = new SubscriptionClient(`ws://192.168.0.100:8080/subscriptions`, {reconnect:true});
subscriptionClient.request({query, variables}, (err, result) => {
console.log(err) // doesn't get call inside the request method
observer.onNext(data:result)
})
}
My subscription method,
export default function() {
const variables = {
ShopId: shop.getShop()[0].id
}
requestSubscription(
environment,
{
subscription,
variables,
onCompleted: (res, err) => {
console.log(res)
console.log(err)
},
updater: (store) => {...},
onError: error => console.error(error),
onNext: (response) => {console.log(response)}
});
}
the component where I'm calling to subscribe,
import subscription from '../../GraphQLQueries/subscriptions/orderConfirmed';
class OrdersBox extends React.Component {
constructor(props) {
super(props);
}
componentDidMount() {
//initializing subscription
orderSubscriptions();
}
When the device starts the app, my device is connected to the web socket as I can see the console.log statement inside the onConnect method in SubscriptionServer. But when the payload is published after a mutation, the subscribe method doesn't get called. I can't seem to figure out what I'm doing wrong. Maybe it's some react-native specific config that I'm missing cuz everything seems to work fine when I test it on graphiql.
I can't find any example of react-native and relay subscriptions used with express graphql.
note: Everything is working when I use subscription with graphiql. But not with react-native and relay.
Thanks in advance guys
....
I wasn't returning the subscriptionClient.request method. Adding a return statement solved the problem. You don't have to return when using subscribe method in subscriptions-transport-ws#0.8.3. But version 0.9.1 replaces the subscribe function with request which does require it to return.
try:
function setupSubscription(config, variables, cacheConfig, observer) {
const query = config.text;
const subscriptionClient = new SubscriptionClient(websocketURL, {
reconnect: true
});
const client = subscriptionClient.request({ query, variables }).subscribe({
next: result => {
observer.onNext({ data: result.data });
},
complete: () => {
observer.onCompleted();
},
error: error => {
observer.onError(error);
}
});
return {
dispose: client.unsubscribe
};
}
subscriptions-transport-ws#0.9.1