Undefined ReferenceError in Vuex - javascript

Am working on an SPA using Vuejs and Vuex. I'm setting some data in the store and showing it in a child component. In the child component, there are radio buttons, which when clicked, I call a function called getCalculations where I log the vehicle object but I keep getting undefined error. The child component is further embedded in parent component.
Vuex Store
const getDefaultState = () => {
return {
//Vehicle Data
vehicleData: {
reg_no: "KAS 234R",
chasis_number: "BGSHS-IUISUS",
engine_number: "MNVSS-8787SNS"
}
}
}
const state = getDefaultState()
//getters
const getters = {
vehicle: (state) => state.vehicleData
}
//actions
const actions = {
//......
}
//mutations
const mutations = {
// .....
}
export default {
state,
getters,
actions,
mutations
}
Parent Component
<template>
<div>
<vehicleFeature/>
</div>
</template>
<script>
import { mapGetters } from "vuex";
import vehicleFeature from "./partials/vehicleFeature";
export default {
name: "StepFour",
data() {
return {
//.....
};
},
computed: mapGetters(["vehicle"]),
components:{
vehicleFeature
}
</script>
Child Component
<template>
<div>
<form class="ipf_form">
<div class="inputGroup">
<input id="radio4" name="radio" #change="getcalculations" type="radio" value="4">
<label for="radio4">1 INSTALMENTS</label>
</div>
<div class="inputGroup">
<input id="radio5" name="radio" #change="getcalculations" type="radio" value="5" >
<label for="radio5">2 INSTALMENTS</label>
</div>
</form>
</div>
</template>
<script>
import { mapGetters } from "vuex";
export default {
name: "vehicleFeature",
data() {
return {
//.....
};
},
computed: {
...mapGetters(["vehicle"]),
principalamount(){
//.....
}
},
methods: {
getcalculations() {
console.log(this.vehicle.reg_no);
}
}
</script>
<style scoped>
</style>

simply change your code as:
const state = {
//Vehicle Data
vehicleData: {
reg_no: "KAS 234R",
chasis_number: "BGSHS-IUISUS",
engine_number: "MNVSS-8787SNS"
}
}
const getters = {
vehicle: (state) => state.vehicleData
}
and get vehicle state as:
...mapGetters(['vehicle'])
this.vehicle.reg_no

Related

how to call a function from another component in vue3?

i have tried to using $on method and this.$root.$refs.compname_component = this; but got some errors,Please refer below my codes
formComponent.vue
<template>
<div v-if="showForm">
create Form
</div>
</template>
<script>
export default {
props:[],
setup(props) {
return props;
},
data() {
return {
formData:{},
showForm:false
}
},
created() {
// this.$root.$refs.tableCommon = this;
// this.$root.$refs.compname_component = this;
},
mounted() {
console.log('form mounted');
// this.$root.$on("displayForm", () => {
// this.displayForm();
// });
},
methods: {
displayForm:function(){
this.showForm = true;
}
},
}
</script>
commonComponent.vue
<template>
<div class="col-10 text-end custom-inline-spacing mb-3">
<button type="button" class="btn btn-outline-secondary" #click="showCreateForm">Create</button>
</div>
</template>
<script>
export default {
props: [],
setup() {
return {}
},
data() {
return {
}
},
mounted() {
console.log('mounted common')
},
methods: {
showCreateForm : function(){
// this.$refs.form.displayForm();
// this.$root.$refs.compname_component.displayForm();
// this.$root.$emit("displayForm");
this.createForm.displayForm();
}
}
}
</script>
app.js
require('./bootstrap')
import { createApp } from 'vue'
import tableCommon from './components/CommonComponent'
import createForm from './components/formComponent';
const app = createApp({})
app.component('vue-common', tableCommon);
app.component('create-form', createForm);
app.mount('#app')
actualy what i want means call formComponent.displayForm() from CommonComponent.
If you want to conditionally display child component, you can move logic out of that component into the parent component.
Parent.vue
<template>
<FormComponent v-if="showComponent" />
</template>
Alternatively, you can pass a prop into FormComponent if you need to conditionally display only a part of that component.
Parent.vue
<template>
<FormComponent v-bind:show-component="showComponent" />
</template>
FormComponent.vue
<template>
<div v-if="showComponent">
...
</div>
<div>
...
</div>
</template>

todo list how to properly add delete function in Vue3?

I have created a Vue3 to-do list project with VueCLI(VueX) for practice. I can add items to the array of objects and display them from objects.
Now, I want to implement a delete function that when I click the delete button beside the item, it deletes the element and also removes the object from array.
Here is my code:
NoteInput.vue
<template>
<div>
<input
type="text"
v-model="inputValue"
#keyup.enter="addItem"
/>
</div>
</template>
<script>
import { ref } from 'vue'
import { useStore } from 'vuex'
export default {
setup() {
const inputValue = ref()
const store = useStore()
const addItem = () => {
if (inputValue.value !== '') {
store.commit('addItem', inputValue.value)
}
inputValue.value = ''
}
return {
inputValue,
addItem
}
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped lang="scss">
</style>
NoteItem.vue
<template>
<div>
<div
v-for="(item, index) in list"
:key="index"
>
<span>{{item.title}}</span>
<span>
<button #click="deleteItem">Delete</button>
</span>
</div>
</div>
</template>
<script>
import { useStore } from 'vuex'
export default {
setup() {
const store = useStore()
const list = store.state.list
const deleteItem = () => {
// store.commit('deleteItem', this.item.title)
console.log()
}
return {
list,
deleteItem
}
}
}
</script>
store/index.js
import { createStore } from 'vuex'
export default createStore({
state: {
list: []
},
getters: {
},
mutations: {
addItem(state, item) {
state.list.push({
title: item,
status: 'normal'
})
},
deleteItem(state, item) {
}
},
actions: {
},
modules: {
}
})
Please modify your NoteItem.vue and store/index.js files as below.
Working codesandbox link https://codesandbox.io/s/vue-3-vuex-4-vue-router-forked-ei4x1r
NoteItem.vue
<template>
<div>
<div
v-for="(item, index) in list"
:key="index"
>
<span>{{item.title}}</span>
<span>
<button #click="deleteItem(index)">Delete</button>
</span>
</div>
</div>
</template>
<script>
import { useStore } from 'vuex'
export default {
setup() {
const store = useStore()
const list = store.state.list
const deleteItem = () => {
store.commit('deleteItem', index)
}
return {
list,
deleteItem
}
}
}
</script>
store/index.js
import { createStore } from 'vuex'
export default createStore({
state: {
list: []
},
getters: {
},
mutations: {
addItem(state, item) {
state.list.push({
title: item,
status: 'normal'
})
},
deleteItem(state, index) {
state = state.list.splice(index, 1);
}
},
actions: {
},
modules: {
}
})

how to add Firebase to a Vuex todo list app?

I am building a simple Vue.js todo list app using Vue.js, Vuex, and Firebase. The Vuex store dispatches, commits, and returns the inputted data just as it should, but I want to be able to connect the app to a Firestore database. So far, I have managed to set up the app so that data is pushed into the collection, but I also want the database to return a snapshot of the firestore data to the DOM, as well as to enable deleting of data from database. I have experience with these Firestore methods in simple non Vuex-projects, but am not sure how to synthesize Firestore methods with a Vuex store. How can I do this? Here is what I have so far. Thanks so much!
<!--GetTodo.vue-->
<template>
<div id="get-todo" class="container">
<input class="form-control" :value="newTodo" #change="getTodo" placeholder="I need to...">
<button class="btn btn-primary" #click="addTodo">Add New Post</button>
<ul class="list-group">
<li class="list-group-item" v-for="todo in todos">
{{todo.body}}
<div class="btn-group">
<button type="button" #click="remove(todo)" class="btn btn-default btn-sm">
<span class="glyphicon glyphicon-remove-circle"></span> Remove
</button>
</div>
</li>
</ul>
</div>
</template>
<script>
export default {
methods: {
getTodo(e) {
this.$store.dispatch('getTodo', e.target.value)
},
addTodo() {
this.$store.dispatch('addTodo')
this.$store.dispatch('clearTodo')
},
remove(todo){
this.$store.dispatch('removeTodo', todo)
}
},
computed: {
todos(){
return this.$store.getters.todos
},
newTodo() {
return this.$store.getters.newTodo
}
}
}
</script>
<style>
</style>
//store.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex);
import db from '../firebase';
export default new Vuex.Store({
state: {
todos: [],
newTodo: ''
},
mutations: { //syncronous, committed
GET_TODO(state, todo){
state.newTodo = todo
},
ADD_TODO(state){
state.todos.push({
body: state.newTodo,
completed: false
})
db.collection('messages').add({
content: state.newTodo
})
},
REMOVE_TODO(state, todo){
var todos = state.todos
todos.splice(todos.indexOf(todo), 1)
},
CLEAR_TODO(state){
state.newTodo = ''
}
},
actions: { //asyncronous, dispatched
getTodo({commit}, todo){
commit('GET_TODO', todo)
},
addTodo({commit}){
commit('ADD_TODO')
},
removeTodo({commit}, todo){
commit('REMOVE_TODO', todo)
},
clearTodo({commit}){
commit('CLEAR_TODO')
}
},
getters: {
newTodo: state => state.newTodo,
todos: state => state.todos.filter((todo) => {
return !todo.completed
})
}
})
<!--App.vue-->
<template>
<div id="app" class="container">
<GetTodo></GetTodo>
</div>
</template>
<script>
import GetTodo from './components/GetTodo.vue'
export default {
components: {
GetTodo
}
}
</script>
<style>
body {
font-family: Helvetica, sans-serif;
}
li {
margin: 10px;
}
</style>
You can make sync in your mutation, see the example below:
source: https://www.codewall.co.uk/how-to-create-a-real-time-to-do-list-app-with-vue-vuex-firebase-tutorial/
import Vue from 'vue'
import Vuex from 'vuex'
import { db } from '#/main'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
items: null
},
getters: {
getItems: state => {
return state.items
}
},
mutations: {
setItems: state => {
let items = []
db.collection('items').orderBy('created_at').onSnapshot((snapshot) => {
items = []
snapshot.forEach((doc) => {
items.push({ id: doc.id, title: doc.data().title })
})
state.items = items
})
}
},
actions: {
setItems: context => {
context.commit('setItems')
}
}
})
import { db } from '#/main'
export default {
name: 'home',
beforeCreate: function () {
this.$store.dispatch('setItems')
},
data: function () {
return {
myTodo: '',
errors: ''
}
},

Why is my component not rerendering after form submission and setting state with Firebase?

I am building a component that is supposed to rerender after a form submission, and it sends the database perfectly fine, but I want the component to rerender after it submits and not having to refresh the page.
I have heard React rerenders after state is changed, and I have tried to set the state of the form inputs on submit.
import React, { Component } from 'react';
import { withFirebase } from '../Firebase';
import { FirebaseContext } from '../Firebase';
import { Link } from 'react-router-dom';
import AddNew from '../AddNew';
import { compose } from 'recompose';
import Firebase from '../Firebase';
import * as ROUTES from '../../constants/routes';
import { throwStatement, thisExpression, tsExpressionWithTypeArguments } from '#babel/types';
class Home extends Component {
constructor(props) {
super(props)
this.state = {
loading: false,
isHidden: false,
name: '',
image: '',
data: []
}
this.baseState = this.state
this.toggleAddNew = this.toggleAddNew.bind(this);
}
getPosts() {
this.props.firebase.getClients().then(snapshot => {
this.setState({
data: snapshot.docs
})
});
}
// Component lifecycle methods
componentWillMount() {
this.getPosts()
}
componentDidUpdate(){
console.log('updated')
}
toggleAddNew() {
this.setState({
isHidden: !this.state.isHidden
})
}
updateInput = e => {
this.setState({
[e.target.name]: e.target.value
});
}
resetForm = () => {
this.setState(this.baseState)
}
deletePost = (id) => {
this.props.firebase.deleteClient(id);
}
addClient = e => {
e.preventDefault();
this.props.firebase.addClient().add({
name: this.state.name,
image: this.state.image
})
this.setState({
name: '',
image: ''
});
this.resetForm();
};
render() {
const renderPosts = this.state.data.map((item) => (
<li data-id={item.id} className="client-wrapper col-sm-4">
<button onClick={() => this.deletePost(item.id)}>X</button>
<Link to={`/dates/${item.id}`}>
<h2>{item.data().name}</h2>
</Link>
<Link to={`/dates/${item.id}`}>
<img src={item.data().image} />
</Link>
</li>
));
return (
<div>
<ul id="client-list" className="row">{renderPosts}</ul>
<button onClick={this.toggleAddNew.bind(this)}>Add New</button>
{this.state.isHidden ?
<div id="add-new-form-wrapper">
<button onClick={this.toggleAddNew.bind(this)} id="x-add-new">X</button>
<form onSubmit={this.addClient.bind(this)} id="add-new-form">
<input type="text" name="name" placeholder="Name" onChange={this.updateInput} value={this.state.name} />
<input type="text" name="image" placeholder="Image" onChange={this.updateInput} value={this.state.image} />
<button type="submit">Submit</button>
</form>
</div> :
''}
</div>
)
}
}
export default compose(
withFirebase,
)(Home);
This
this.baseState = this.state
only makes a copy of object reference, not a copy of state object (with property values).
When we have a reference copy
resetForm = () => {
this.setState(this.baseState)
}
can work like state = state, does nothing.
The copy of object (with current property values) can be done (f.e.) this way:
this.baseState = {...this.state}
With this small fix it should work ...
... if not, try
resetForm = () => {
this.setState({...this.baseState})
}
You can also update some state field with current time to force rerender or simply call this.forceUpdate() (see docs).
BTW - resetForm shouldn't overwrite data. Luckily we have a copy of data object reference in baseState ;)

Vue binding parent and child components

How to binding parent's model to child in Vue.js?
These codes below is works fine. if i fill the input manually, then child's model return it's value to the parent's model.
But the issue is, if the data set from AJAX request in a parent, the input doesn't automatically filled.
Can anyone help me on this?
Form.vue
<template>
<form-input v-model="o.name" :fieldModel="o.name" #listenChanges="o.name = $event"/>
<form-input v-model="o.address" :fieldModel="o.address" #listenChanges="o.address = $event"/>
</template>
<script>
import FormInput from '../share/FormInput.vue'
export default {
data () {
return {
o: {
name: '',
address: ''
}
}
},
components: { 'form-input': FormInput },
created: function() {
axios.get('http://api.example.com')
.then(response => {
this.o.name = response.data.name
this.o.address = response.data.address
})
.catch(e => { console.log(e) })
}
}
</script>
FormInput.vue
<template>
<input type="text" v-model='fieldModelValue' #input="forceUpper($event, fieldModel)">
</template>
<script>
export default {
props: ['fieldModel'],
data() {
return {
fieldModelValue: ''
}
},
mounted: function() {
this.fieldModelValue = this.fieldModel;
},
methods: {
forceUpper(e, m) {
const start = e.target.selectionStart;
e.target.value = e.target.value.toUpperCase();
this.fieldModelValue = e.target.value.toUpperCase();
this.$emit('listenChanges', this.fieldModelValue)
}
}
}
</script>
Things are more straightforward if you take advantage of v-model in components.
If you put v-model on a component, the component should take a prop named value, and should emit input events to trigger it to update.
I like to make a computed to hide the event emitting, and allow me to just v-model the computed inside my component.
new Vue({
el: '#app',
data: {
o: {
name: '',
address: ''
}
},
components: {
'form-input': {
template: '#form-input',
props: ['value'],
computed: {
fieldModelValue: {
get() {
return this.value;
},
set(newValue) {
this.$emit('input', newValue.toUpperCase());
}
}
}
}
},
// Simulate axios call
created: function() {
setTimeout(() => {
this.o.name = 'the name';
this.o.address = 'and address';
}, 500);
}
});
<script src="//unpkg.com/vue#latest/dist/vue.js"></script>
<div id="app">
Name ({{o.name}})
<form-input v-model="o.name"></form-input>
Address ({{o.address}})
<form-input v-model="o.address"></form-input>
</div>
<template id="form-input">
<input type="text" v-model='fieldModelValue'>
</template>
The mounted() hook is blocking subsequent updates from the parent.
Remove mounted and change v-model to 'fieldModel'
<template>
<input type="text" :value='fieldModel' #input="forceUpper($event, fieldModel)">
</template>
<script>
export default {
props: ['fieldModel'],
data() {
return {
fieldModelValue: ''
}
},
// mounted: function() {
// this.fieldModelValue = this.fieldModel;
// },
methods: {
forceUpper(e, m) {
const start = e.target.selectionStart;
e.target.value = e.target.value.toUpperCase();
this.fieldModelValue = e.target.value.toUpperCase();
this.$emit('listenChanges', this.fieldModelValue)
}
}
}
</script>
Demo CodeSandbox

Categories

Resources