Error Message:Error: Function DocumentReference.set() called with invalid data. Unsupported field value: undefined (found in field articleName)
I am trying to add the variables from my form to my Firebase Cloud Firestore database. I use vuex for global state management. When I pass the variables from my CreateSale component to sale->state they get defined. But when I call the action createSale when submitting the form, the state variables are undefined. Everything should be setup properly. I registered Vuex aswell as configured firebase.
sale.js (vuex module)
createSale(state, getters) {
console.log(state.articleName) //undefined
db.collection('clothes').add({
articleName: state.articleName,
description: state.description,
brand: state.brand,
price: state.price,
imagesRefs: getters.fileRefs
}).then(docRef => {
for (var i = 0; i < state.files.length; i++) {
}
this.$router.push('/')
}).catch(error => alert(error.message))
}
CreateSale.vue
template:
<div class="container" id="createSale">
<form #submit.prevent="createSale" class="col s12">
<div class="row">
<div class="input-field col s12">
<input type="text" v-model="articleName" v-on:change="setArticleName(articleName)" required>
<label>Article Name</label>
</div>
</div>
<div class="row">
<div class="input-field col s12">
<input type="text" v-model="description" v-on:change="setDescription(description)" required>
<label>Description</label>
</div>
</div>
<div class="row">
<div class="input-field col s12">
<input type="text" v-model="brand" v-on:change="setBrand(brand)" required>
<label>Brand</label>
</div>
</div>
<div class="row">
<div class="input-field col s12">
<input style="padding-top: 16px;" type="number" v-model="price" v-on:change="setPrice(price)" required>
<label>Price</label>
</div>
</div>
<UploadFile />
<button type="submit" class="btn btn-success">Submit</button>
<router-link to="/" class="btn grey">Cancel</router-link>
</form>
script:
export default {
name: 'createSale',
components: {
UploadFile
},
data() {
return {
articleName: '',
description: '',
brand: '',
price: ''
}
},
methods: {
...mapMutations('sale', [
'setArticleName',
'setDescription',
'setBrand',
'setPrice'
]),
...mapActions('sale', {
createSale: 'createSale',
console: 'console'
})
}
Your action need to be
createSale({state, getters}) {
console.log(state.articleName) //undefined
db.collection('clothes').add({
articleName: state.articleName,
description: state.description,
brand: state.brand,
price: state.price,
imagesRefs: getters.fileRefs
}).then(docRef => {
for (var i = 0; i < state.files.length; i++) {
}
this.$router.push('/')
}).catch(error => alert(error.message))
}
The first parameter which is passed to action is context object, you need to get state and getters from this context object.
Example from vuex website:
Related
I'm trying to get the sign in part working on my webapp but it's not working properly.
Whenever I press the login button the page either refreshes and the url gets updated with the credentials and stays at the same page OR the router gets pushed and goes to the 'homepage' without logging the user in.
I also followed this guide for reference: https://blog.logrocket.com/vue-firebase-authentication/
What's weird is that the sign up part is working just fine.
SignIn.vue
<div class="card-body">
<form>
<!-- email -->
<div class="input-group form-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fas fa-user"></i></span>
</div>
<input id="email" type="email" class="form-control" name="email" placeholder="e-mail" value required autofocus v-model="form.email" />
</div>
<!-- password -->
<div class="input-group form-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fas fa-key"></i></span>
</div>
<input id="password" type="password" class="form-control" name="password" placeholder="password" required v-model="form.password" />
</div>
<!-- error -->
<div v-if="error" class="alert alert-danger animated shake">{{ error }}</div>
<br />
<!-- login -->
<div class="form-group d-flex justify-content-between">
<div class="row align-items-center remember"><input type="checkbox" v-model="form.rememberMe" />Remember Me</div>
<input type="submit" #click="submit" value="Login" class="btn float-right login_btn" />
</div>
</form>
</div>
Script in SignIn.vue
<script>
import firebase from 'firebase';
export default {
data() {
return {
form: {
email: '',
password: '',
rememberMe: false
},
error: null
};
},
methods: {
submit() {
firebase
.auth()
.signInWithEmailAndPassword(this.form.email, this.form.password)
.catch(err => {
this.error = err.message;
})
.then(data => {
this.$router.push({ name: 'home' });
});
}
}
};
</script>
Store.js
import Vue from 'vue';
import Vuex from 'vuex';
import profile from './modules/profile';
import authenticate from './modules/authenticate';
Vue.use(Vuex);
export default new Vuex.Store({
modules: {
profile,
authenticate
}
});
Authenticate.js in store
const state = {
user: {
loggedIn: false,
data: null
}
};
const getters = {
user(state) {
return state.user;
}
};
const mutations = {
SET_LOGGED_IN(state, value) {
state.user.loggedIn = value;
},
SET_USER(state, data) {
state.user.data = data;
}
};
const actions = {
fetchUser({ commit }, user) {
commit('SET_LOGGED_IN', user !== null);
if (user) {
commit('SET_USER', {
displayName: user.displayName,
email: user.email
});
} else {
commit('SET_USER', null);
}
}
};
export default {
state,
mutations,
actions,
getters
};
It is probably because you assign the submit type to your button, your form is submitted before the Firebase method is triggered.
You should change the button code from
<input type="submit" #click="submit" value="Login" class="btn float-right login_btn" />
to
<input type="button" #click="submit" value="Login" class="btn float-right login_btn" />
See the W3 specification for more detail on button types.
I'm coding a Employee Database in Vuejs using Firebase as my backend. On my new employee page I keep getting a 3 errors when submitting the form to add a new employee.
"Property or method "saveEmployee" is not defined on the instance but referenced during render. Make sure that this property is reactive, either in the data option, or for class-based components, by initializing the property."
"[Vue warn]: Error in v-on handler: "TypeError: _vm.saveEmployee is not a function"
"TypeError: "_vm.saveEmployee is not a function"
This is how my newemployee.vue file looks like
<template>
<div id="new-employee">
<h3>New Employee</h3>
<div class="row">
<form #submit.prevent="saveEmployee" class="col s12">
<div class="row">
<div class="input-field col s12">
<input type="text" v-model="employee_id" required />
<label>Employee ID#</label>
</div>
</div>
<div class="row">
<div class="input-field col s12">
<input type="text" v-model="name" required />
<label>Name</label>
</div>
</div>
<div class="row">
<div class="input-field col s12">
<input type="text" v-model="dept" required />
<label>Department</label>
</div>
</div>
<div class="row">
<div class="input-field col s12">
<input type="text" v-model="position" required />
<label>Name</label>
</div>
</div>
<button type="submit" class="btn">Submit</button>
<router-link to="/" class="btn grey">Cancel</router-link>
</form>
</div>
</div>
</template>
<script>
import db from "./firebaseInit";
export default {
name: "new-employee",
data() {
return {
employee_id: null,
name: null,
dept: null,
position: null
};
},
methods: {
saveEmployee() {
db.collection("employees")
.add({
employee_id: this.employee_id,
name: this.name,
dept: this.dept,
position: this.position
})
.then(docRef => {
console.log("Client added: ", docRef.id);
this.$router.push("/");
})
.catch(error => {
console.error("Error adding employee: ", error);
});
}
}
};
</script>
The whole problem is on the saveEmployee function and I can not find a fix.
I am using the trumbowyg editor control in my vuejs spa. From the documentation I know that I can use the following code to set the contents of the editor.
$('#editor').trumbowyg('html','<p>Your content here</p>');
$('#editor').trigger('tbwchange');
However, it is not working for me in my VueJs App. I have an object that has a description key defined. I can console.log the description , but when I try to assign it to the editor control as mentioned above, it fails . I can see no error in the console but the text just won't show up in the editor.
Here is what I am going at the moment.
<template>
<div class="modal fade" data-backdrop="static" data-keyboard="false">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title">
<span v-if="anouncement">Edit Anouncement</span>
<span v-else>New Anouncement</span>
</h4>
</div>
<div class="modal-body">
<div class="form-group">
<input type="text" placeholder="enter anouncement summary here" class="form-control" v-model="anouncementObj.summary">
</div>
<div class="form-group">
<input type="text" placeholder="enter location here" class="form-control" v-model="anouncementObj.location">
</div>
<textarea class="note-view__body" id="anonDescription" v-model="description" placeholder="enter event description"></textarea>
</div>
<div class="modal-footer">
<button type="button" v-on:click="clear()" class="btn btn-link" data-dismiss="modal">Close</button>
<button type="button" v-on:click="performSave()" class="btn btn-link">Save</button>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
props : {
anouncement : Object
},
data() {
return {
anouncementObj :{}
}
},
mounted () {
this.makeTextBoxReady();
this.anouncementObj = Object.assign({},this.anouncementObj, this.anouncement || {});
$('#anonDescription').trumbowyg('html',this.anouncement.description);
$('#anonDescription').trigger('tbwchange');
console.log(this.anouncement.description);
},
methods : {
makeTextBoxReady: function() {
$(document).ready(function() {
if (!$('html').is('.ie9')) {
if ($('.note-view__body')[0]) {
$('.note-view__body').trumbowyg({
autogrow: true,
btns: [
'btnGrp-semantic', ['formatting'],
'btnGrp-justify',
'btnGrp-lists', ['removeformat']
]
});
}
}
});
},
performSave : function() {
let description = $('#anonDescription').trumbowyg('html');
let formData = new FormData();
for (name in this.anouncementObj) {
formData.append(name, this.anouncementObj[name]);
}
if( !this.anouncementObj.id) {
this.anouncementObj.id = 0;
}
formData.append('description',description);
this.$http.post('/admin/anouncement/createOrUpdate', formData).then(response => {
// console.log(response);
if(response.data.status==200) {
alert(response.data.message);
this.$emit('getAnouncements');
}
})
},
clear: function() {
this.anouncementObj= {};
}
}
}
</script>
Can you please let me know what I am doing wrong here? I have also tried the nexttick approach but even that is not working.
I got it working. I was not using the correct bootstrap modal id. Please see this related question for more information.
This is the correct code.
if(this.anouncementObj && this.anouncementObj.description && this.anouncementObj.id) {
$('#'+this.anouncementObj.id+' #anonDescription').trumbowyg('html',this.anouncementObj.description);
$('#'+this.anouncementObj.id+' #anonDescription').trigger('tbwchange');
}
I want to create a custom field validation for forms using Meteor. I don't want a packaged solution.
Here is my code so far.
JavaScript:
var errorState = new ReactiveVar;
Template.lottoForm.created = function() {
errorState.set('errors', {});
};
Template.lottoForm.events({
'blur input': function(e, t) {
e.preventDefault();
validate($(this));
}
});
Template.lottoForm.helpers({
errorClass: function (key) {
return errorState.get('errors')[key] ? 'has-error' : '';
}
});
Template.errorMsg.helpers({
errorMessages: function(key) {
return errorState.get('errors');
}
});
function throwError(message) {
errorState.set('errors', message)
return errorState.get('errors');
}
function validate(formField) {
$("input").each(function(index, element){
var fieldValue = $(element).val();
if(fieldValue){
if(isNaN(fieldValue) == true) {
throwError('Not a valid Number');
}
}
});
}
HTML:
<template name="lottoForm">
<form class="playslip form-inline" role="form">
<fieldset>
<div class="form-group {{errorClass 'ball0'}}">
<input type="text" class="input-block form-control" id="ball0" maxlength="2" name="ball0">
</div>
<div class="form-group {{errorClass 'ball1'}}">
<input type="text" class="input-block form-control" id="ball1" maxlength="2" name="ball1">
</div>
<div class="form-group {{errorClass 'ball2'}}">
<input type="text" class="input-block form-control" id="ball2" maxlength="2" name="ball2">
</div>
<div class="form-group {{errorClass 'ball3'}}">
<input type="text" class="input-block form-control" id="ball3" maxlength="2" name="ball3">
</div>
<div class="form-group {{errorClass 'ball4'}}">
<input type="text" class="input-block form-control" id="ball4" maxlength="2" name="ball4">
</div>
<div class="form-group {{errorClass 'ball5'}}">
<input type="text" class="input-block form-control" id="ball5" maxlength="2" name="ball5">
</div>
{{> errorMsg}}
<div class="play-btn">
<button type="submit" value="submit" class="btn btn-primary">Play Syndicate</button>
</div>
</fieldset>
</form>
</template
<template name="errorMsg">
<div class="errorMsg">
{{#if errorMessages}}
<div class="list-errors">
{{#each errorMessages}}
<div class="list-item">{{> fieldError}}</div>
{{/each}}
</div>
{{/if}}
</div>
</template>
<template name="fieldError">
<div class="alert alert-danger" role="alert">
{{errors}}
</div>
</template>
I'm also getting this error message in the console - "Uncaught Error: {{#each}} currently only accepts arrays, cursors or falsey values."
Why do I get this error and how would I implement the custom validation?
Just as the error says, #each template functions must be passed the proper type. Are you sure your errorMessages helper is returning an array? It looks like you are initializing it as a hash.
VueJS, with Vuex and Firebase allow me to to register a user in my app easily but can I, AT THE SAME TIME, create one or more database refs for that specific user?
I can set and get the user.email from the users list, but I'm stuck when it comes to create more fields in the actual database.
Can you help me out?
This is my sign-up component code:
<template>
<section class="section">
<h1>Sign-up</h1>
<h2>Create an Account</h2>
</div>
<div class="card-content">
<form v-on:submit.prevent>
<div class="field">
<label class="label">Name</label>
<div class="control">
<input class="input" type="text" placeholder="Your Display Name" v-model="name">
</div>
</div>
<div class="field">
<label class="label">Email</label>
<div class="control">
<input class="input" type="email" placeholder="joe#bloggs.com" v-model="email">
</div>
</div>
<div class="field">
<label class="label">Password</label>
<div class="control">
<input class="input" type="password" v-model="password">
</div>
</div>
<button type="submit" class="button is-primary" v-on:click="signUp">Sign-up</button>
</form>
<p>Already registered? <router-link to="/Sign-In">Log in</router-link></p>
</section>
</template>
<script>
import Firebase from "firebase";
export default {
data: function() {
return {
name: "",
email: "",
password: ""
};
},
methods: {
signUp: function() {
Firebase.auth()
.createUserWithEmailAndPassword(this.email, this.password)
.then(
user => {
this.$router.replace('dashboard');
},
error => {
alert(error.message);
}
);
}
}
};
</script>
I am assuming I could create another method but I really want to keep it as simple as possible.
You need to make an additional API call to set the displayName on the user:
firebase.auth()
.createUserWithEmailAndPassword(this.email, this.password)
.then(
user => {
return user.updateProfile({displayName: this.displayName});
},
error => {
alert(error.message);
}
);