Hi how do I initialize my quasar app once after I have established a connection to firebase. I need to do this because my route guards checks whether a user is logged in, but if a user refreshes this causes firebase to check whether a current user exists but since it's a async operation it initially returns null. I have previously achieved this using vue where I have done this in my main.js file, but I'm unsure how to do this is quasar boot file.
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import './assets/main.css'
import { authService } from './firebase/config'
let app
authService.onAuthStateChanged(() => {
if(!app){
app = createApp(App).use(router).mount('#app')
}
})
Here inside main.js standard vue app, we wait for Firebase Auth to initialize and only after .onAuthStateChanged() method fires, we create our Vue App.
In an quasar app there is no main.js but instead boot files which run some code before main app starts.
Below is what a standard boot files looks like but I am unsure how to convert my main.js to a executable boot file, seems like some properties are missing like mount, createApp & use.
// import something here
// "async" is optional!
// remove it if you don't need it
export default async ({ /* app, router, store */ }) => {
// something to do
}
Ideally you create a boot file for initializing Firebase in a Quasar project.
Boot files fulfill one special purpose: they run code before the App’s Vue root component is instantiated while giving you access to certain variables, which is required if you need to initialize a library, interfere with Vue Router, inject Vue prototype or inject the root instance of the Vue app.
Create a boot file using the following command:
quasar new boot firebase
This will create a boot file /src/boot/firebase.js
Install Firebase using npm (or yarn) and then add the following code in your boot file:
import firebase from 'firebase/app';
import 'firebase/auth';
const firebaseConfig = {...};
if (!firebase.apps.length) {
firebase.initializeApp(firebaseConfig);
}
export const auth = firebase.auth();
As mentioned earlier, boot file runs before Vue component is instantiated. Now you can access this instance of Firebase Auth in any component you want.
import {auth} from "#/boot/firebase"
auth.onAuthStateChanged((user) => {
//...
})
This got it working.
export default async ({ app, router, store }) => {
return new Promise(resolve => {
const unsubscribe = authService.onAuthStateChanged(() => {
resolve()
unsubscribe()
})
})
}
Here is also a useful article on quasar boot flow https://quasar.dev/quasar-cli/boot-files#quasar-app-flow
Related
After creating a CLI project, I need to add a Modal dialog, after a bit of searching I found what seems to be a good example at Create Reusable MODALS Using VUE JS, by Shmoji . The tutorial is well written and there is a YouTube as well.
In the modual.service.js file the following code exists:
import Vue from 'vue';
export default new Vue({
methods: {
open(component, props = {}) {
return new Promise((resolve, reject) => {
this.$emit('open', { component, props, resolve, reject });
});
}
}
})
Here is where I get the error that reads:
Uncaught TypeError: vue__WEBPACK_IMPORTED_MODULE_1__.default is not a constructor
It is pointing to the second line of code, export default new Vue({
In my main.js file, I already create Vue so it does not make sense to me why the modal author is trying to overwrite the Vue that already exists?
//main.js
import { createApp, reactive, computed, VueElement } from 'vue'
import App from './App.vue'
import router from './router'
import './assets/styles.css'
import moment from 'moment'
const app = createApp(App).use(router)
app.config.globalProperties.$moment=moment;
app.mount('#app')
Seems to me the global open method needs to be included in main.js but not sure how that is done or how to get around the above error if the modal service open method stays in its own file?
This Modal Dialog is not compatible with Vue3. I answered this question in more detail in this StackOverflow posting.
In Vue2, I was able to access my Vue instance to make use of components registered with Vue.
test.js
import Vue from 'vue'
export function renderLogin () {
Vue.toasted.show('Please login again', { type: 'error', duration: 2000 })
}
In the above code, I am able to access the toasted package as I have already registered it with Vue in my main.js. However, in Vue3 I'm unable to use the toasted package as I'm unable to access the Vue instance inside a js file.
Need help on how to access Vue instance('this') inside a js file.
After a day of searching, I was able to access the toasted component from the vue instance inside a js file.
First, we would have to export the app instance to be able to read it in a js file
main.js
export const app = createApp({
render() {
return h(AppWrapper);
},
});
Next, we would have to register our component in our globalProperties of our app's instance.
app.config.globalProperties.$toast = toast;
We can now import the app instance in our js file and access toast component
test.js
import { app } from '#/main.js'
app.config.globalProperties.$toast('Toast working fine', {
type: 'success',
duration: 2000,
})
Hope this helps someone out. Please let me know if there are other/better ways. Thank you
// Vue 3 Composition API
<script>
import { getCurrentInstance } from 'vue';
export default {
setup() {
const _instance = getCurrentInstance();
const vueInstance = _instance.appContext;
},
};
</script>
It's not exactly the way as in Vue2, but this will probably expose what you are looking for.
If you want to make a package globally available in Vue3 you probably need to add the following code to a plugin:
//* This will help for accessing the toasted instance in other files (plugins)
app.config.globalProperties.$toasted = toasted;
//* This will expose the toasted instance in components with this.$toasted
app.provide('$toasted', toasted);
With this you are able to get the toasted instance in the options api with: this.$toasted
And with the composition api:
const { $toasted } = _instance.appContext.app.config.globalProperties;
And in another plugin with:
constructor(app) { app.config.globalProperties; }
You can use provider/inject.
For example if you want to use axios across my components, provide axios in your main.js
import { createApp } from "vue";
import App from "./App.vue";
import axios from "axios";
const app = createApp(App);
app.provide("http", axios);
app.mount("#app");
Then in SFC component you could access by 2 ways:
// Composition API
<script>
import { inject } from 'vue'
export default {
setup() {
const http = inject("http");
http.get("https://jsonplaceholder.typicode.com/todos/1").then((response) => {
console.log(response.data);
});
}
}
</script>
// Vue 2 options API
<script>
export default {
inject: ["http"],
}
</script>
Original answer here.
I'm trying to learn nativescript-vue where I'm using the Nativescript's Playground to tryout my sample codes. I'm trying to use nativescript-localstorage package so that I can store some of the information into local storage:
Whenever I'm trying to save project it is giving use compilation error
and following is the error:
An error occurred while transpiling nativescript-localstorage/localstorage.js.
unknown: We found a path that isn't a NodePath instance. Possiblly due to bad serialisation.
I followed the tutorials and added the package as it was instructed my code looks like:
import Vue from 'nativescript-vue';
import Vuex from 'vuex';
import localStorage from 'nativescript-localstorage';
import userStore from './user-store';
//For local storage of vuex tools
const NSVuexPersistent = store => {
// Init hook.
let storageStr = localStorage.getItem('ns-vuex-persistent');
if (storageStr) {
store.replaceState(JSON.parse(storageStr))
}
store.subscribe((mutation, state) => {
// Subscribe hook.
localStorage.setItem('ns-vuex-persistent', JSON.stringify(state));
})
};
Vue.use(Vuex);
const debug = process.env.NODE_ENV !== 'production';
export default new Vuex.Store({
modules: {
userStore
},
strict: debug,
plugins: [NSVuexPersistent]
})
Since the project is not getting saved so I've not shared the link. Help me out with it. Thanks.
nativescript-vue is packaged within the Preview APK, so it can be imported as
import Vue from 'nativescript-vue'
But nativescript-localstorage is something you have installed in your project, so while at Playground you should use relative path to import the module, something like
import * as localStorage from "../nativescript-localstorage"
// Or
const localStorage = require("../nativescript-localstorage");
You can use import name from "package" only when the package has marked something as default in its exports, the syntax is commonly used in ES6 & Vue plugins. nativescript-localstorage has not exported anything as default.
I'm probably not seeing obvious, but after hours I don't get it still.
The problem: when I import my Vuex store into a non-component file (a api service), one module gets loaded properly while the other one only by name, but is otherwise empty.
// store.js
import * as collections from "./modules/collections";
import * as auth from "./modules/auth";
export default new Vuex.Store({
modules: {
auth,
collections
}
});
Both these modules are near-identical. Both have
export const getters = {}
export const actions = {}
export const mutations = {}
export const state = {}
Now when in some other non-component file I DON'T include the store, my vue store looks like this:
{"auth":{"authenticated":false,"authToken":"123","loginPending":false,"loginError":{}},"collections":{"collectionsPending":false,"collectionsError":null,"collections":[]}}
Now when I import the store to use it in my service like so:
import store from '../store'
// NO OTHER MENTIONS OF STORE, ONLY IMPORTING
Suddenly only my auth module is "empty"
{"auth":{},"collections":{"collectionsPending":false,"collectionsError":null,"collections":[]}}
It has something to do with module loading.
Order of loading by adding console.log statements:
Without the import:
INIT AUTH
INIT collections
INIT store
With the import:
INIT collections
INIT store
IMPORT STATEMENT -- INTO SERVICE
INIT AUTH
I'm using the Vue Webpack Boilerplate
Sigh, sorry. Circular dependency indeed. Was expecting a warning if I'd did that, but didn't.
Thanks Emile Bergeron.
I'm trying to use GraphQL to populate fake data for Styleguidist. I'm using Express to make my GraphQL server but I'm unsure how to connect Apollo into Styleguidist? The examples use the index.js file and wrap the root component in an tag for Apollo.
I am unsure how Styleguidist works, I don't know where the index.js file is.
There are ways to configure Styleguidist through webpack, but I don't know how to use webpack to use Apollo.
Each example in Styleguidist is rendered as an independent React tree, and the Wrapper component is the root component, so you need to override it as show in the Redux example like this:
// lib/styleguide/Wrapper.js
import React, { Component } from 'react';
import ApolloClient, { createNetworkInterface } from 'apollo-client';
import { ApolloProvider } from 'react-apollo';
const client = new ApolloClient({ /* ... */ });
export default class Wrapper extends Component {
render() {
return (
<ApolloProvider client={client}>
{this.props.children}
</ApolloProvider>
);
}
}
// styleguide.config.js
const path = require('path');
module.exports = {
styleguideComponents: {
Wrapper: path.join(__dirname, 'lib/styleguide/Wrapper')
}
};
So you can use Styleguidist in two ways, one by using Create React App then installing an NPM Styleguidist package. Then the other method that I found is starting from an example github registry and replacing the components as you go. I had done the first: where I used Create React App so Webpack was not installed in my main folder but was being used in the NPM module.
With that method I was getting the error:
"Module parse failed: Unexpected token (16:6)
You may need an appropriate loader to handle this file type."
Which means that I needed to configure Webpack. I didn't solve this, but it may just need to have styleguide.config.js file configured to work with Babel. (just a guess)
So, could not (so far), successfully use the Wrapper that solves the problem. So instead I downloaded an example of Styleguidist at https://github.com/styleguidist/example and started fresh. I'm not sure what the difference is, but when I used a wrapper it worked well to add an ApolloProvider wrapper to every component on my page.
To get Apollo 2 to work though you also need to use HttpLink and InMemoryCache. The have a general setup about this at: https://www.apollographql.com/docs/react/basics/setup.html. Under creating a client.
I was using a different port for my GraphQL server because it was using a GraphQL/Express server at port 4000 and Styleguidist by default is at port 6060. So I did two things: passed a uri to the new HttpLink and added a line to the express server to allow cors.
The ref for cors in Express GraphQl and Apollo server:
https://blog.graph.cool/enabling-cors-for-express-graphql-apollo-server-1ef999bfb38d
So my wrapper file looks like:
import React, { Component } from 'react';
import ApolloClient, { createNetworkInterface } from 'apollo-client';
import { ApolloProvider } from 'react-apollo';
import { HttpLink } from 'apollo-link-http';
import { InMemoryCache } from 'apollo-cache-inmemory';
const link = new HttpLink({
uri: 'http://localhost:4000/graphql'
});
const client = new ApolloClient({
link,
cache: new InMemoryCache()
});
export default class Wrapper extends Component {
render() {
return (
<ApolloProvider client={client}>
{this.props.children}
</ApolloProvider>
);
}
}
and my server file looks like:
const express = require('express');
const expressGraphQL = require('express-graphql');
const schema = require('./schema/schema');
const cors = require('cors');
const app = express();
app.use(cors());
app.use('/graphql', expressGraphQL({
schema: schema
, graphiql: true
}));
app.listen(4000, () => {
console.log('..listening');
});