How to get data from firebase when page loading using vuejs? - javascript

I have been trying to get current user data from firebase to display details in profile page.
Here i am trying get data from firestore, when page loading. My table structure : users => Current user UID => email, firstname, lastname, businessname, etc.
I have added functionality to get data from firebase when profile page loading but does not work. error showing in console product.data().firstname is not function.
And also i did not get any console output firebase data retrieved or not?
here is my code:
<template>
<section class="what-we-do">
<div class="container-2" style="padding-top: 150px;">
<div class="row">
<div class="col-md-12">
<div class="saving-process-crd">
<div class="saving-process-inner">
<avatar :fullname="currentUser.email" size="96" >
</avatar>
<h4>Siva NSN</h4>
<h6 style="color:grey;">{{currentUser.email}}</h6><br><br>
<div class="card-columns" >
<div class="card" style="border: none; text-align: justify;">
<div class="card-body">
<h5 class="card-title">First Name:</h5><br>
<h5 class="card-title">Last Name:</h5><br>
<h5 class="card-title">Email ID:</h5><br>
</div>
</div>
<div class="card" style="border: none;">
<div class="card-body" style="float: left; text-align: left;" >
<h5 class="card-title">{{product.data().firstname}}</h5><br>
<h5 class="card-title">Mobility</h5><br>
<h5 class="card-title">{{currentUser.email}}</h5><br>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
</template>
<script>
import Avatar from 'vue-avatar-component'
import database from '#/database'
import firebase from 'firebase/app'
export default {
name: 'Profile',
computed:{
currentUser (){
return this.$store.state.currentUser
}
},
components: {
Avatar
},
data () {
return {
profileData:{
email:null,
firstname:null,
lastname:null,
secondaryEmail:null,
businessName:null
}
}
},
methods:{
readData(){
const firestore = database.firestore();
firestore.collection('users').doc(firebase.auth().currentUser.uid).
onSnapshot(function(doc){
console.log('current data:', doc.data())
var newData = doc.data()
this.profileData.push(newData)
})
}
}
}
</script>
main.js code:
here i am i have user authstatechanges of current user.
import Vue from 'vue'
import App from './App.vue'
import router from './router';
import 'bootstrap';
import 'bootstrap/dist/css/bootstrap.min.css';
import './assets/styles//base-style.css';
import store from '#/store'
import firebase from 'firebase/app'
import 'firebase/auth'
import 'firebase/firestore'
Vue.config.productionTip = false
let app
const initialize = () => {
if (!app) {
app = new Vue({
el: '#app',
router,
store,
render: h => h(App),
})
}
}
firebase.auth().onAuthStateChanged(user => {
if(user) {
store.commit('setCurrentUser', user)
} else {
store.commit('setCurrentUser', null)
}
initialize()
})
console output:
How to get data when page loading from firebase database. any help much appreicated pls..

There are number of things you have to adjust,
instead of fetching data using a method, try to add the code to a life cycle hook method, which will fire before you mount the data to dom, more precisely saying, use created lifecycle hook
https://vuejsexamples.net/vuejs-created/
Then you are populating the data to the template using currentUser which is taken from the vuex store,
return this.$store.state.currentUser, but in your firebase function you are setting the data you fetch to a data property which is profileData which is not used in the template.
You are pushing to profileData, but it's not an array it is a object and you cant push to an object.
So better flow is, fetch data using created lifecycle hook, then
either
store(mutate) the received data to the store.state.currentUser then it might work.
else update the profileData and replace the template with profileData instead of currentUser
Try this one,
Create a created lifecycle hook and move the firebase code to that. and assign the profileData object to the fetched Data.
created() {
const firestore = database.firestore();
firestore.collection('users').doc(firebase.auth().currentUser.uid).
onSnapshot(function(doc){
console.log('current data:', doc.data())
var newData = doc.data()
this.profileData = newData;
})
}
Then replace the currentUser in template to profileData.
ex : <h6 style="color:grey;">{{profileData.email}}</h6><br><br>

Related

how to use express.router to access data in url? (vue)

I have a file "app.vue" that implements a date picker for user to choose date:
//app.vue
<div id="q-app">
<div class="q-pa-md" style="max-width: 300px">
<q-input filled v-model="date" mask="date" :rules="['date']">
<template v-slot:append>
<q-icon name="event" class="cursor-pointer">
<q-popup-proxy>
<q-date v-model="date" ></q-date>
</q-popup-proxy>
</q-icon>
</template>
</q-input>
</div>
</div>
import { ref } from 'vue'
export default {
setup () {
return {
date: ref('2019/02/01')
}
}
methods: {
updateDate(){
this.$router.push({
name: this.$router.name,
query: this.date,
}
</script>
I tested "app.vue" successfully updates user selected date to the url.
I have another file "data.js" which is the backend, I want to get the user picked date, to do so, I try to access it like the following:
//data.js
const router = express.Router();
router.get("/", async function (req, res) {
...
console.log("user input date", req.query);
}
But the req.query is always returning empty.
Does anyone know why this is happening and how I can access the date data from "data.js"?

Vue component accessing data before it is available in Vuex store

I have a component named ProductArea which displays products loaded from the Prismic API. The products loaded are dependant on a category which is selected by the user in a sidebar.
I'm using Vuex and struggling to come up with a flow that avoids a situation where category is not yet available in my store (category is also loaded from Prismic).
Here is what the parent of ProductArea looks like:
<template>
<div>
<NavBar />
<!-- <Header /> -->
<main>
<div v-if="!$fetchState.pending" class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div class="flex-1 min-w-0 bg-white xl:flex">
<Sidebar :navigation="navigation" />
<ProductArea />
</div>
</div>
</main>
</div>
</template>
<script lang="ts">
import Vue from 'vue'
import NavBar from '#/components/NavBar.vue'
import Sidebar from '#/components/Sidebar.vue'
import Header from '#/components/Header.vue'
import CategoryHeader from '#/components/CategoryHeader.vue'
import ProductGrid from '#/components/ProductGrid.vue'
import { mapActions } from 'vuex'
import { mapGetters } from 'vuex'
export default {
name: 'App',
components: {
Sidebar,
NavBar,
Header,
CategoryHeader
},
data() {
return {
navigation: null
}
},
async fetch() {
const component = this
await this.fetchCategories()
.then(function(navigationResult) {
const navigation = component.$store.getters.navigation
component.navigation = navigation
})
},
fetchOnServer: true,
methods: {
...mapActions({ fetchCategories: 'fetchCategories', fetchProducts: 'fetchProducts' })
}
}
</script>
I assumed having v-if="!$fetchState.pending" would prevent ProductArea from being created until category has been loaded into the store, however this doesn't seem to be the case.
Here is ProductArea:
<template>
<div class="bg-white lg:min-w-0 lg:flex-1">
<CategoryHeader :category="this.category" :products="this.products" />
<div class="sm:p-6">
<ProductGrid :category="this.category.primary.category" :products="this.products" />
</div>
</div>
</template>
<script lang="ts">
import { mapActions } from 'vuex'
import { mapGetters } from 'vuex'
import Locale from '#/types/locale'
export default {
name: 'ProductArea',
data() {
return {
category: this.$store.getters.category,
products: Array
}
},
async fetch() {
const component = this
await this.fetchProducts(this.category)
.then(function(productsResult) {
const products = component.$store.getters.products
component.products = products
console.log(products)
})
},
fetchOnServer: true,
methods: {
...mapActions({ fetchProducts: 'fetchProducts' })
}
}
</script>
Here's the error I'm receiving:
Error in fetch(): TypeError: Cannot read property 'products' of undefined
This error is referring to the undefined category within the fetchProducts called via fetch on the ProductsArea component.
Can anyone point me in the right direction? What would be the optimal flow here to prevent category being accessed before it is available?
You could set a default category. If you don't want to do that, bring the Vuex category into the parent and only show <ProductArea> when it's defined:
Parent
<ProductArea v-if="category" />
computed: {
...mapGetters(['category'])
}
This is necessary because your v-if on $fetchState.pending only tests whether all the categories are loaded, but for the child component you also need to test that a category has been selected.
In fact, you can simplify all your code by mapping the getters instead of storing getters in variables, which is not a good practice. Those variables wouldn't be updated reactively when the getter changes. Instead, completely remove the data options from both components:
Parent
async fetch() {
await this.fetchCategories();
}
computed: {
...mapGetters(['category', 'navigation'])
}
Child
async fetch() {
await this.fetchProducts();
}
computed: {
...mapGetters(['category', 'products'])
}
Other improvements:
You can shorten the mapActions calls a bit:
Parent: ...mapActions(['fetchCategories'])
Child: ...mapActions(['fetchProducts'])

VueJS; wait for element before running local JavaScript File

I have a few components, javascript, and elements that needs to be ran in a certain order.
1st - opensheetmusicdisplay.min.js which I have in my index.html file. This isn't an issue.
2nd - <div id="xml">
3rd - xml-loader.js which depends on both the "xml" div and opensheetmusicdisplay.min,js
This is the index.html:
<!DOCTYPE html>
<html lang="en">
<head>
<script rel="preload" src="<%= BASE_URL %>js/osmd/opensheetmusicdisplay.min.js"></script>
</head>
<body>
<div id="xml2">words go here</div>
<div id="app"></div>
</body>
</html>
And this is the JavaScript part I'm attempting to test:
window.onload = function() {
alert("xx == ", document.getElementById("xml2"));
}
alert("xx2 == ", document.getElementById("xml2"));
alert(JSON.stringify(opensheetmusicdisplay, null, 1));
When I run this, they both instances of "xml2" show blanks. The opensheetmusicdisplay does show data, which means it is reading from the source in the head section in index.html
It was pointed out to me in the comments that alert only take one argument. That's a mistake that I'm going to let sit for the moment. The error in the console is TypeError: document.getElementById(...) is null.
Now, this is the main.js. There are a lot of comments because of my various ideas:
// vue imports and config
import Vue from 'vue'
import App from '#/App'
import VueRouter from 'vue-router'
Vue.use(VueRouter)
Vue.config.productionTip = false
// page imports
import Notation from '#/components/Notation'
import HomePage from '#/components/HomePage'
// component imports and registration
import { FoundationCSS } from '#/../node_modules/foundation-sites/dist/css/foundation.min.css'
Vue.component('foundation-css', FoundationCSS)
import SideNav from '#/components/SideNav'
Vue.component('side-nav', SideNav);
// import * as Osmd from '#/../public/js/osmd/opensheetmusicdisplay.min.js'
// Vue.component('osmd-js', Osmd)
// import { OsmdJs } from '#/components/Osmd'
import * as XmlJs from '#/../public/js/osmd/xml-loader.js'
Vue.component('xml-js', XmlJs)
// import XLoad from '#/components/XmlLoader'
const router = new VueRouter({
mode: 'history',
routes: [
{ path: '/',
components: {
maininfo: HomePage
}
},
{ path: '/chromatic-scales/c-chromatic-scale',
components: {
maininfo: Notation// ,
// xmlloader: XLoad
}
}
]
})
new Vue({
el: '#app',
router,
template: '<App/>',
components: { App }
})
I registered XmlJs as global because this is the only way out of 100 things that actually works. I then embed it in Notation.vue like so:
<template>
<div>
<div id="xml">
{{ notation.data }}
</div>
<xml-js />
</div>
</template>
<script>
import axios from 'axios'
export default ({
data () {
return {
notation: null,
}
},
mounted () {
axios
.get('http://localhost:3000/chromatic-scales/c-chromatic-scale')
.then(result => (this.notation = result))
}})
</script>
<style scoped></style>
The last file is the meat and potatoes of what I'm trying to do. The xml-loader.js slurps the data from <div id="xml"> and does whatever magic the program does in order to render the output I want. The issue is that there doesn't seem to be anyway to wait for the stuff in {{ notation.data }}.
I am new to using vuejs and front-end javascript frameworks in general. I do recognize the code is probably not optimal at this time.
There is race condition where DOM element is not available at the time when it's accessed. The solution is to not access DOM elements created by Vue outside of it. DOM element is ready for use only after asynchronous request:
<template>
<div>
<div ref="xml" id="xml">
{{ notation.data }}
</div>
<xml-js />
</div>
</template>
<script>
import axios from 'axios'
export default ({
data () {
return {
notation: null,
}
},
async mounted () {
const result = await axios
.get('http://localhost:3000/chromatic-scales/c-chromatic-scale')
this.notation = result;
this.$nextTick(); // wait for re-render
renderXml(this.$ref.xml); // pass DOM element to third-party renderer
}})
You can import xml-loader.js into the Notation.vue as a function. Then you can simply do something like this:
mounted () {
axios.get(PATH).then(result => {
this.notation = result
let xmlResult = loadXML(result)
doSomethingWithResult(xmlResult)
}
},
methods: {
doSomethingWithResult (result) {
// do something
}
}

vue.js post list not updating after form submission

In my vue app I have two components one which is a form that posts the form data to my api. And the other gets and displays these posts in a section on the page. My issue is that when I submit a new post the posts lists aren't updated. The data stays the same unless I refresh the page. How can I get my posts list to update when I submit the form?
My Code:
client/src/App.vue
<template>
<div id="app">
<MainHeader :modalVisability="modal" v-on:showModal="toggleModal" />
<div id="content_wrap">
<Summary />
</div>
<OppForm :modalVisability="modal" />
</div>
</template>
<script>
import MainHeader from './components/MainHeader.vue';
import OppForm from './components/oppForm.vue';
import Summary from './components/Summary.vue';
export default {
name: 'App',
components: {
MainHeader,
Summary,
OppForm
},
data () {
return {
modal: false
}
},
methods: {
toggleModal (modalBool) {
this.modal = modalBool;
}
}
}
</script>
client/src/components/oppForm.vue
<template>
<div id="opp_form_modal" >
<form #submit.prevent="SubmitOpp" v-if="modalVisability">
<input type="text" name="company_name" v-model="company_name">
<button type="submit">Submit</button>
</form>
</div>
</template>
<script>
import axios from 'axios';
export default {
name: 'oppForm',
props: {
modalVisability: Boolean,
},
data () {
return {
company_name: ''
}
},
methods: {
SubmitOpp () {
axios.post('http://localhost:5000/', {
company_name: this.company_name,
})
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});
}
}
}
</script>
client/src/components/Summary.vue
<template>
<div id="summary_section">
<h2>Summary</h2>
<div id="summary_board">
<div class="column">
<div class="head">
<h3>Opportunities</h3>
</div>
<div class="body">
<div class="post"
v-for="(post, index) in posts"
:key="index"
>
<p class="company">{{ post.company_name }}</p>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import axios from 'axios';
export default {
data() {
return{
posts: []
};
},
created() {
axios.get('http://localhost:5000/')
.then(res => {
// console.log(res);
const data = res.data;
this.posts = data;
})
.catch(error => console.log(error));
}
}
</script>
The problem is that you're actually fetching your posts only on the app creation (i.e. inside the created() method).
You should wrap your axios call inside a function updatePosts() and then call it whenever you add a new post successfully, or you could create a custom event that is triggered whenever a new post is added.
created() is called only once (see vue lifecycle) so you fetch API before submitting form.
Try to add some console.log to understand what is called when.
You could use an global event bus and send form value as event data to summary. I could imagine also a solution where event is used to "tell" summary that form was submitted (just boolean, not data itself). In summary you then call API each time you receive event.
Or simple add an "update" button to summary to manually call API.
See Communication between sibling components in VueJs 2.0
or global vue instance for events for detailed examples.

How to click on a user & show the user details in another component/view with Vuejs router?

I am doing a project with Vuejs and I need to the following:
Use an API & fetch a list of users (just the names of the users) in the home page.
Create a custom search filter to find users by name.
When clicking on a user's name, I need to redirect to another component & output that user's details in that component (only the details of the user that I clicked).
I have accomplished the first two tasks. However, I have no idea how doing the other third task. I was reading the documentation for vue-router, but I am not able to figure it out.
I used axios to fetch the list of users & jsonplaceholder.
User List Component:
<template>
<div>
<!-- List Rendering the response data stored in posts[] array -->
<b-input id="inline-form-input-name" class="my-3 col-10 col-sm-10 col-md-4 col-lg-4" type="text" v-model="searchUsers" placeholder="Search Users..."
></b-input>
<h2>Our users:</h2>
<div v-for="user in filteredUsers" :key="user.id">
<p v-b-tooltip.hover.right='"Click on user to know more"' class="users pr-2"><span>{{ user.id }}</span> - {{ user.name }}</p>
</div>
</div>
</template>
<script>
// import axios
import axios from 'axios'
export default {
name: 'UsersList',
data() {
return {
users: [],
searchUsers: ''
}
},
computed: {
// custom search box for user names
filteredUsers() {
return this.users.filter(user => {
return user.name.match(this.searchUsers)
})
}
},
// life cycle hook - calls axios
created(){
axios.get('https://jsonplaceholder.typicode.com/users').then(response => {
console.log(response.data)
this.users = response.data
// console.log an error if get() method is unsuccessful
}).catch(err => {
console.log(err)
})
},
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped lang="scss">
.users {
cursor: pointer;
display: inline-block;
}
</style>
The User list component's name is UserList.vue
I need to ouput the user detail in this component called UsersDetails.vue
<template>
<div class="user-details-wrapper">
<h1>I am the user details component</h1>
</div>
</template>
<script>
export default {
name: 'UserDetails',
data(){
return {
}
},
}
</script>
<style lang="scss">
.user-details-wrapper {
h1 {
background: #000;
color: #fff;
padding: 10px;
margin-top: 30px;
display: inline-block;
}
}
</style>
Screenshot user list & custom search filter
Any help will be truly appreciated it!
You can use Dynamic Route Matching
Add a route
const router = new VueRouter({
routes: [
// dynamic segments start with a colon
{ path: '/user/:id', component: User }
]
})
A dynamic segment is denoted by a colon : When a route is matched, the value of the dynamic segments will be exposed as this.$route.params in every component.
In the Single User component do an AJAX call in mounted
mounted() {
axios.get("https://jsonplaceholder.typicode.com/users/" + this.$route.params)
.then(res => console.log(res))
}

Categories