I am getting an error when trying to post data to firebase. Any idea what I am doing wrong? I have looked at a few tutorials and the official docs but I can't figure out what I'm missing.
Error in v-on handler: "TypeError: Cannot read property 'rsvp' of undefined"
Here are all of the relevant files:
HomePage.vue
<template>
<div>
<div>
<div>
<h3>RSVP</h3>
</div>
<div>
<form v-on:submit.prevent="addRsvp">
<div class="form-group">
<label>Name:</label>
<input type="text" class="form-control" v-model="newRsvp.name"/>
</div>
<div class="form-group">
<label>Number of guests:</label>
<input type="number" class="form-control" v-model="newRsvp.guests"/>
</div>
<div class="form-group">
<input type="submit" class="btn btn-primary" value="SUBMIT"/>
</div>
</form>
</div>
</div>
</div>
</template>
<script>
import { db } from '../config/db';
export default {
name: 'HomePage',
firebase: {
rsvp: db.ref('rsvp'),
},
data () {
return {
newRsvp: {
name: '',
guests: '',
},
}
},
methods: {
addRsvp() {
this.$firebaseRefs.rsvp.push({
name: this.newRsvp.name,
guests: this.newRsvp.guests,
})
this.newRsvp.name = '';
this.newRsvp.guests = '';
alert("Succeessfully added");
}
}
}
</script>
main.js
import Vue from 'vue'
import App from './App.vue'
import { firestorePlugin } from 'vuefire'
Vue.use(firestorePlugin)
Vue.config.productionTip = false
new Vue({
render: h => h(App),
}).$mount('#app')
App.vue
<template>
<div id="app">
<HomePage />
</div>
</template>
<script>
import HomePage from './components/HomePage.vue'
export default {
name: 'app',
components: {
HomePage,
}
}
</script>
<style lang="css">
#import '../node_modules/bootstrap/dist/css/bootstrap.min.css';
</style>
config.js
import Firebase from 'firebase'
const firebaseConfig = {
// my app config from firebase
};
const app = Firebase.initializeApp(firebaseConfig)
export const db = app.database()
Any help would be appreciated! Any help would be appreciated!
Since you do import { firestorePlugin } from 'vuefire', it means you are using the Firestore database and not the Realtime Database.
However, your vuefire code corresponds to the Realtime database, see https://vuefire.vuejs.org/vuefire/writing-data.html#updates-to-collection-and-documents
So I think that instead of
export default {
name: 'HomePage',
firebase: {
rsvp: db.ref('rsvp'),
},
//...
}
you should do
export default {
name: 'HomePage',
firestore: {
rsvp: db.collection('rsvp'),
},
//...
methods: {
addRsvp() {
this.$firebaseRefs.rsvp.add({
name: this.newRsvp.name,
guests: this.newRsvp.guests,
})
//....
}
}
}
since you use Firestore and not the Realtime Database.
Or it is the exact opposite: your database IS the Realtime database, and then you need to adapt your main.js as follows:
import Vue from 'vue'
import App from './App.vue'
import { rtdbPlugin } from 'vuefire'
Vue.use(rtdbPlugin)
new Vue({
render: h => h(App),
}).$mount('#app')
See the doc here: https://vuefire.vuejs.org/vuefire/binding-subscriptions.html#declarative-binding
Related
I'm trying to save the input values in the Pinia store, but the state is not updating. So I want onSubmit function to save the input values in a store.
My code :
Create.vue
<script setup>
import { reactive } from "vue";
import { useCounterStore } from '#/stores/counter';
const counter = useCounterStore();
const form = reactive({
first: "",
second: "",
email: ""
});
const onSubmit = (e) => {
e.preventDefault();
counter.$patch({ firstName: form.first.value });
counter.$patch({ secondName: form.second.value });
counter.$patch({ email: form.email.value });
}
</script>
<template>
<form #submit="onSubmit">
{{ counter.getFirst + 'MYFIRST' }} {{ counter.getSecond + 'MYSECOND' }} {{ counter.getEmail + 'MYEMAIL' }}
<div class="row mt-4">
<div class="col-6">
<label for="exampleFormControlInput1" class="form-label">First</label>
<input v-model="form.first" type="text" class="form-control" id="exampleFormControlInput1"
placeholder="First name">
</div> <div class="col-6">
<label for="exampleFormControlInput1" class="form-label">Second</label>
<input v-model="form.second" type="text" class="form-control" id="exampleFormControlInput1"
placeholder="Second name">
</div>
<div class="col-6 mt-2">
<label for="exampleFormControlInput1" class="form-label">Email</label>
<input v-model="form.email" type="email" class="form-control" id="exampleFormControlInput1"
placeholder="name#example.com">
</div>
<div class="col-12 mt-3">
<button #click="onSubmit" type="button" class="btn btn-dark push- right">Create</button>
<button type="button" class="btn btn-dark">All users</button>
</div>
</div>
</form>
</template>
Pinia store: counter.js
import { ref, computed, reactive } from 'vue'
import { defineStore } from 'pinia'
export const useCounterStore = defineStore('counter', () => {
const count = ref(0);
let firstName = ref('');
let secondName = ref('');
let email = ref('');
const getFirst = computed(() => firstName.value)
const getSecond = computed(() => secondName.value)
const getEmail = computed(() => email.value)
function increment() {
count.value++
}
return { count, getFirst, getSecond, getEmail, increment }
})
i am not sure how your store look like... but it should by something like:
`
import { defineStore } from 'pinia'
export const useCounterStore = defineStore('counter', {
state: () => {
return { count: 0 }
}
})`
in your component, change the name of the store instance, lets say counterStore.
const counterStore = useCounterStore();
and now you can access the state by name:
counterStore.counter++
while this will work, it is not the best practice...
you should use actions to manipulate the state...
export const useCounterStore = defineStore('counter', {
state: () => {
return { count: 0 }
},
actions: {
increment() {
this.count++
},
},
})
and then you can call the action like a regular method:
counterStore.increment()
I think you're in the process of learning Vue, don't give up!
Give a feedback in comment and put your post on "solved" status if this help you.
The solution :
In store file :
import { defineStore } from 'pinia';
export const useAuthStore = defineStore('auth', {
state: () => ({
loginForm: {
firstName: '',
secondName: '',
email: ''
}
})
});
In Create.vue :
<script setup>
import { reactive } from "vue";
import { useAuthStore } from '#/stores/auth';
const authstore = useAuthStore();
const form = reactive({
first: "",
second: "",
email: ""
});
...
const onSubmit = (e) => {
e.preventDefault();
authStore.loginForm.firstName = form.first;
authStore.loginForm.secondName = form.second;
authStore.loginForm.email = form.email;
}
...
<script/>
I'm learning Vue 3 composition API and Pinia. I'm making a todo.
When I submit a todo data through Pinia, I can submit to the DB, but it won't re-render until reload the page.
Do I need to use 'watch' to watch the state todos:[] and execute fetchTodos()?
any good solution?
here both codes, hope someone can help me. Thank you in advance.
----- VUE -----
<script setup>
import { ref, onMounted } from 'vue'
import { storeToRefs } = from 'pinia'
import { useTodoStore } from '../store/todo'
const store = useTodoStore()
const { getTodos } = storeToRefs(store)
onMounted(() => {
store.fetchTodos()
})
const todo = ref('')
const initForm = () => {
todo.value = ''
}
// submit via Pinia
const onSubmitToPinia = () => {
const payload = {
todo: todo.value,
}
store.addTodoFromPinia(payload)
initForm()
store.fetchTodo()
}
</script>
<template>
<h4>TODO</h4>
<!-- form addTodo -->
<form class="row g-4">
<div class="col-auto">
<input
class="form-control"
v-model="newName"
type="text"
placeholder="todo">
</div>
<div>
<button
class="btn btn-primary"
type="button"
#click="onSubmitToPinia(payload)">
submit through pinia</button>
</div>
</form>
<!-- render data from pinia -->
<div class="todo"
v-for="getTodo in getTodoss.todo"
:key="getTodo.id">
<b class="ms-2">{{ getTodo.todo }}</b>
</div>
</template>
---- PINIA ----
import { defineStore } from 'pinia'
import axios from "axios"
export const useAboutStore = defineStore('todo',{
state: () => {
return {
todos: []
}
},
getters: {
getTodos(state) {
return state.todos
}
},
actions: {
async fetchTodos() {
try {
const data = await axios.get('http://localhost:5000/todo')
this.todos = data.data
}
catch (error) {
alert(error)
console.log(error)
}
},
addTodoFromPinia(payload) {
const path = 'http://localhost:5000/todo'
axios.post(path, payload)
}
},
})
You don't need to use storeToRefs to accomplish what you want nor do you need to watch the state of the store.
<template>
<div class="todo"
v-for="getTodo in store.todos"
:key="getTodo.id">
<b class="ms-2">{{ getTodo.todo }}</b>
</div>
</template>
If for any reason the vue complains that the array is empty put a v-if checking if the store.todos.length is != 0.
And also fix your typos.
If the problem persists show me your new code and I help you again.
I'm making a captcha component using vue. I try to fetch the captcha when the component created. When I do this in a async manner, the page will not be reactive, although the state has already been updated. But I when do it in sync manner, everything is fine. So, I'm wondering why async manner won't work?
This works
<template>
<section>
<div class="captcha-container">
<div v-if="captcha" v-html="captcha.data"></div>
</div>
</section>
</template>
<script>
import { mapState, mapActions } from "Vuex";
export default {
created: function() {
this.$store.commit('setCaptcha', {id: 'xx', data:'Hi'});
},
computed: {
...mapState(["captcha"])
},
};
</script>
This does not work
<template>
<section>
<div class="captcha-container">
<div v-if="captcha" v-html="captcha.data"></div>
</div>
</section>
</template>
<script>
import { mapState, mapActions } from "Vuex";
export default {
created: function() {
setTimeout(() => {
this.$store.commit('setCaptcha', {id: 'xx', data:'Hi'});
}, 1000);
},
computed: {
...mapState(["captcha"])
},
};
</script>
Actions must be plain objects. Use custom middleware for async actions.
This is the error and this function is pointed
this.props.registerUser(newUser);
see the next code snippet!
redux-thunk is used for referring plain js object but not working
This is my action creator file :
import { GET_ERRORS } from "./types";
import axios from "axios";
// Register User
export const registerUser = userData => dispatch => {
axios
.post("/api/users/register", userData)
.then(res => console.log(res))
.catch(err =>
dispatch({
type: GET_ERRORS,
payload: err.response.data
})
);
};
This is my react registration page :
import React, { Component } from "react";
import PropTypes from "prop-types";
// import { withRouter } from 'react-router-dom';
import classnames from "classnames";
import { connect } from "react-redux";
import { registerUser } from "../../actions/authActions";
class Register extends Component {
constructor() {
super();
this.state = {
name: "",
email: "",
password: "",
password2: "",
errors: {}
};
this.onChange = this.onChange.bind(this);
this.onSubmit = this.onSubmit.bind(this);
}
componentWillReceiveProps(nextProps) {
if (nextProps.errors) {
this.setState({ errors: nextProps.errors });
}
}
onChange(event) {
this.setState({ [event.target.name]: event.target.value });
}
onSubmit(event) {
event.preventDefault();
const newUser = {
name: this.state.name,
email: this.state.email,
password: this.state.password,
confirm_password: this.state.password2
};
this.props.registerUser(newUser);
}
render() {
const { errors } = this.state;
return (
<div className="register">
<div className="container">
<div className="row">
<div className="col-md-8 m-auto">
<h1 className="display-4 text-center">Sign Up</h1>
<p className="lead text-center">
Create your SocioStalker account
</p>
<form noValidate onSubmit={this.onSubmit}>
<div className="form-group">
<input
type="text"
className={classnames("form-control form-control-lg", {
"is-invalid": errors.name
})}
placeholder="Name"
name="name"
value={this.state.name}
onChange={this.onChange}
/>
<div className="invalid-feedback">{errors.name}</div>
</div>
<div className="form-group">
<input
type="email"
className={classnames("form-control form-control-lg", {
"is-invalid": errors.email
})}
placeholder="Email Address"
name="email"
value={this.state.email}
onChange={this.onChange}
/>
<div className="invalid-feedback">{errors.email}</div>
<small className="form-text text-muted">
This site uses Gravatar so if you want a profile image, use
a Gravatar email
</small>
</div>
<div className="form-group">
<input
type="password"
className={classnames("form-control form-control-lg", {
"is-invalid": errors.password
})}
placeholder="Password"
name="password"
value={this.state.password}
onChange={this.onChange}
/>
<div className="invalid-feedback">{errors.password}</div>
</div>
<div className="form-group">
<input
type="password"
className={classnames("form-control form-control-lg", {
"is-invalid": errors.confirm_password
})}
placeholder="Confirm Password"
name="password2"
value={this.state.password2}
onChange={this.onChange}
/>
<div className="invalid-feedback">
{errors.confirm_password}
</div>
</div>
<input type="submit" className="btn btn-info btn-block mt-4" />
</form>
</div>
</div>
</div>
</div>
);
}
}
Register.propTypes = {
registerUser: PropTypes.func.isRequired,
auth: PropTypes.object.isRequired,
errors: PropTypes.object.isRequired
};
const mapStateToProps = state => ({
auth: state.auth,
errors: state.errors
});
export default connect(
mapStateToProps,
{ registerUser }
)(Register);
Store config file:
import { createStore, applyMiddleware, compose } from "redux";
import thunk from "redux-thunk";
import rootReducer from "./reducers";
import { composeWithDevTools } from "redux-devtools-extension";
const initialState = {};
const middleware = [thunk];
const store = createStore(
rootReducer,
initialState,
compose(
composeWithDevTools(),
applyMiddleware(...middleware)
)
);
export default store;
Your store setup logic is wrong. You've got this:
compose(
composeWithDevTools(),
applyMiddleware(...middleware)
)
Instead, it should be:
composeWithDevTools(
applyMiddleware(...middleware)
)
Please see the Redux docs page on "Configuring Your Store" for examples of how to correctly set up middleware and the DevTools Extension.
I'd also encourage you to try out our new redux-starter-kit package. It includes a configureStore function that does all that for you automatically.
Here's how you could simplify your startup file using configureStore:
import {configureStore} from "redux-starter-kit";
import rootReducer from "./reducers";
const store = configureStore({
reducer : rootReducer,
});
I tried to auth using vue.js and firebase.
and error occurs router.beforeEach function,
Anyone has any idea why it might happen?
console error
vue-router.esm.js?fe87:16 [vue-router] uncaught error during route navigation:
warn # vue-router.esm.js?fe87:16
abort # vue-router.esm.js?fe87:1904
iterator # vue-router.esm.js?fe87:1968
step # vue-router.esm.js?fe87:1717
runQueue # vue-router.esm.js?fe87:1725
confirmTransition # vue-router.esm.js?fe87:1972
transitionTo # vue-router.esm.js?fe87:1874
push # vue-router.esm.js?fe87:2181
(anonymous) # vue-router.esm.js?fe87:1960
(anonymous) # index.js?3672:44
router/index.js
import Vue from 'vue'
import Router from 'vue-router'
import addPost from '#/components/addPost'
import showPost from '#/components/showPost'
import Login from '#/components/Login'
import SignUp from '#/components/SignUp'
import firebase from 'firebase'
Vue.use(Router)
const router = new Router({
mode: 'history',
routes: [
{
path: '/',
name: 'app',
component: showPost
},
{
path: '/add',
component: addPost,
meta: {
requiresAuth: true
}
},
{
path: '/login',
name: 'Login',
component: Login
},
{
path: '/signup',
name: 'SignUp',
component: SignUp
}
]
})
router.beforeEach((to, from, next) => {
let currentUser = firebase.auth().currentUser;
let requiresAuth = to.matched.some(record => record.meta.requiresAuth);
if (requiresAuth && !currentUser) next('/login')
else if (!requiresAuth && currentUser) next('/')
else next()
})
export default router
main.js
import Vue from 'vue'
import App from './App'
import router from './router'
import VueFire from 'vuefire'
import firebase from 'firebase'
Vue.use(VueFire)
Vue.config.productionTip = false
let app;
firebase.auth().onAuthStateChanged(function(user) {
if(!app) {
app = new Vue({
el: '#app',
router,
template: '<App/>',
components: { App }
})
}
})
App.vue
<template>
<div id="app">
<app-header></app-header>
<router-view></router-view>
<button #click="logout">Logout</button>
</div>
</template>
<script>
import header from './components/header'
import firebase from 'firebase'
export default {
name: 'app',
components: {
'app-header': header
},
methods: {
logout: function() {
firebase.auth().signOut().then(() => {
this.$router.replace('login')
})
}
}
}
</script>
Login.vue
import firebase from 'firebase'
import db from '../firebaseInit'
const postRef = db.ref('posts')
export default {
name: 'login',
data: function() {
return {
email: '',
password: ''
}
},
methods: {
signIn: function() {
firebase.auth().signInWithEmailAndPassword(this.email, this.password).then(
(user) => {
this.$router.replace('/')
},
(err) => {
alert('Oops ' + err.message)
}
);
}
}
}
</script>
addPost.vue
<template>
<div id="add-blog">
<h2>Add a New Post</h2>
<form v-if="!submitted">
<label>Title:</label>
<input type="text" v-model="newPost.title" required />
<p>{{ getDate }}</p>
<label for="">Content:</label>
<textarea v-model.trim="newPost.content"></textarea>
<div id="checkboxes">
<p>Categories:</p>
<label>Vue.js</label>
<input type="checkbox" value="vue" v-model="newPost.categories" />
<label>CSS Magic</label>
<input type="checkbox" value="css" v-model="newPost.categories" />
</div>
<label>Author:</label>
<select v-model="newPost.author">
<option v-for="author in authors">{{ author }}</option>
</select>
<button #click.prevent="addPost">Add Post</button>
</form>
<div v-if="submitted">
<p>Congraturation!</p>
</div>
<div id="preview">
<h3>Preview Post</h3>
<h4>Title {{ newPost.title }}</h4>
<h4>Content </h4>
<p style="white-space: pre">{{ newPost.content }}</p>
<ul>
<li v-for="category in newPost.categories">{{ category }}</li>
</ul>
<p>{{ newPost.author }}</p>
</div>
</div>
</template>
<script>
import db from '../firebaseInit'
const postRef = db.ref('posts')
export default {
data() {
return {
newPost: {
date: '',
title: '',
author: '',
content: '',
categories: []
},
authors: ['Naeun', 'Raphael'],
submitted: false,
items: []
}
},
methods: {
addPost: function() {
postRef.push(this.newPost)
this.newPost.date = '',
this.newPost.title = '',
this.newPost.author = '',
this.newPost.content = '',
this.newPost.categories = ''
},
removePost: function() {
postRef.child(post['.key']).remove()
}
},
computed: {
getDate: function() {
const toTwoDigits = num => num < 10 ? '0' + num : num;
let today = new Date();
let year = today.getFullYear();
let month = toTwoDigits(today.getMonth() + 1);
let day = toTwoDigits(today.getDate());
return this.newPost.date = `${year}-${month}-${day}`;
}
}
}
</script>
Make sure that the next function is called exactly once in any given pass through the navigation guard. It can appear more than once, but only if the logical paths have no overlap, otherwise the hook will never be resolved or produce errors.
Your else if condition else if (!requiresAuth && currentUser) next('/') violates this rule.
Change your route guarding logic
// check if route requiresAuth
router.beforeEach((to, from, next) => {
if (to.matched.some(rec => rec.meta.requiresAuth)) {
const user = firebase.auth().currentUser;
// check auth state of user
user ? next() : next('/login') // user not signed in, route to login
} else {
next(); // route does not require auth
}
});
https://router.vuejs.org/guide/advanced/navigation-guards.html#global-before-guards
Hope this helps.