passing data between components in vuejs - javascript

I've got my expense tracker app. I've got problem with adding Expense.
I've got two components responsible for this: addCategory.vue and selectCategory.vue.
This is my selectCategory.vue component:
<template>
<div>
<select class="custom-select" #selected="this.$emit('select-cat',category)">
<option v-for="category in categories">{{ category.title }}</option>
</select>
</div>
</template>
<script>
import axios from 'axios';
export default {
data() {
return {
categories: [],
errors: []
}
},
created() {
axios.get(`http://localhost:3000/categories`)
.then(response => {
this.categories = response.data;
console.log(response.data);
})
.catch(e => {
this.errors.push(e)
})
}
}
</script>
and this is my addExpense.vue component:
<template>
<div class="card">
<div class="card-header">
<h4>Dodaj nowy wydatek</h4>
</div>
<form v-on:submit.prevent="addExpense">
<div class="card-body">
<div class="form-group">
<label for="expense-name">Nazwa wydatku</label>
<input type="text" class="form-control" id="expense-name" v-model="Expense.title">
</div>
<div class="form-group">
<label for="expense-amount">Wartość</label>
<input type="number" class="form-control" id="expense-amount" v-model="Expense.amount">
</div>
<div class="form-group">
<label for="expense-date">Data wydatku</label>
<input type="date" class="form-control" id="expense-date" v-model="Expense.date">
</div>
<div class="form-group">
<label for="expense-category">Kategoria</label>
<select-category #select-cat="chooseCategory" v-model="Category.id"></select-category>
</div>
<br>
<div class="form-group">
<button class="btn btn-primary" #click="showAlert">Dodaj nowy wydatek</button>
</div>
</div>
</form>
</div>
</div>
</template>
<script>
import axios from 'axios';
import selectCategory from './selectCategory.vue';
export default {
components: {
'select-category': selectCategory
},
data(){
return {
Expense: {
title:'',
amount: '',
date:'',
categoryId:''
},
}
},
methods : {
chooseCategory(){
this.Expense.categoryId = this.Category.id
},
showAlert(){
this.$alert.success({
message: 'Wydatek dodany poprawnie'
})
},
addExpense(){
let newExpense = {
title : this.Expense.title,
amount : this.Expense.amount,
date : this.Expense.date,
categoryId: this.Expense.categoryId
}
console.log(newExpense);
axios.post(`http://localhost:3000/expenses`, newExpense)
.then((response) => {
console.log(response);
})
.catch((error) => {
console.log(error);
})
}
}
}
</script>
I need help because when I try to add the Expense, field with 'categoryId' remains empty.
I use Events to pass the name of categories but I dont know how to add category.id to Expense.

The issues in your codes:
you need to add one data property to save which option the user selected, so add one data property=selectedCategory
you didn't bind the value for the options of the select, and you didn't bind the value of the select, so add v-model="selectedCategory" for <select> and add :value="category" for <option>
It seems you bind wrong event (event=selected more likely is the event name you customized) for <select>, change to #change="selectChange(selectedCategory)"
Finally, in addExpense.vue, listen event=select-cat.
Like below demo:
Vue.config.productionTip = false
Vue.component('select-category', {
template: `<div>
<select class="custom-select" v-model="selectedCategory" #change="selectChange(selectedCategory)">
<option v-for="category in categories" :value="category">{{ category.title }}</option>
</select>
</div>`,
data() {
return {
categories: [],
errors: [],
selectedCategory: null
}
},
mounted() {
this.categories = [
{'id':1, 'title': 'abc'},
{'id':2, 'title': 'xyz'}
]
},
methods: {
selectChange: function(newCatetory) {
this.$emit('select-cat',newCatetory)
}
}
})
new Vue({
el: '#app',
data() {
return {
categorySelected: null
}
},
watch: {
categorySelected: function (newVal, oldVal) {
console.log('changed to ' + newVal.id + ' from ' + (oldVal ? oldVal.id : oldVal))
}
},
methods:{
chooseCategory: function(data) {
this.categorySelected = data
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.js"></script>
<div id="app">
<div class="form-group">
<label for="expense-category">Kategoria</label>
<select-category #select-cat="chooseCategory($event)"></select-category>
</div>
</div>

Related

Vue.js add filename dynamically in a v-for loop

I have a component that dynamically adds inputs to my application. One input is a text input and another is a file input. In the case of text input everything works, I take the value via v-model. In the case of file input it is not possible to use v-model, instead v-on: change is used. How do I add the filename to the lists object dynamically?
Template
<template lang="html">
<div v-for="list in lists">
<input type="text" v-model="list.text" >
<input type="file" #change="upload">
</div>
<button #click="addItem">Add Item</button>
</template>
Script
<script>
export default {
data() {
return {
lists:[{text: '', filename: '',}]
}
},
methods: {
addItem() {
this.lists.push({ text: '', filename: '' })
},
upload(event) {
this.lists.filename = event.target.files[0].name
},
}
}
</script>
I'm using Vue 3.
Thanks in advance.
You can iterate with the index as well, then pass the index to the upload function, like this:
<script>
export default {
data() {
return {
lists:[{text: '', filename: '',}]
}
},
methods: {
addItem() {
this.lists.push({ text: '', filename: '' })
},
upload(event, index) {
this.lists[index].filename = event.target.files[0].name
},
}
}
</script>
<template lang="html">
<div v-for="(list, index) in lists">
<input type="text" v-model="list.text" >
<input type="file" #change="upload($event, index)">
</div>
<button #click="addItem">Add Item</button>
</template>
You can bind with array index, do not need upload method
new Vue({
el: '#app',
data() {
return {
title: "Vue app",
lists:[{text: '', filename: '',}]
};
},
methods:{
addItem() {
this.lists.push({ text: '', filename: '' })
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app"><h2>{{title}}</h2>
<div v-for="(list,i) in lists">
<input type="text" v-model="lists[i].text" >
<input type="file" v-model="lists[i].filename">
</div>
<button #click="addItem">Add Item</button>
<pre>{{lists}}</pre>
</div>
You can do it like this:
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<input id='inputfile' type='file' name='inputfile' onChange='getoutput()'>
new Vue({
el: '#app',
data() {
return {
title: "Vue app",
lists:[{text: '', filename: '',}]
};
},
methods:{
addItem() {
this.lists.push({ text: '', filename: '' })
},
getFile(filePath) {
return filePath.substr(filePath.lastIndexOf('\\') + 1).split('.')[0];
},
getoutput(e, index) {
let fileName = this.getFile(e.target.value);
let fileExtention = e.target.value.split('.')[1];
this.lists[index].filename = `${fileName}.${fileExtention}`;
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app"><h2>{{title}}</h2>
<div v-for="(list,i) in lists">
<input type="text" v-model="lists[i].text" >
<input type="file" #change="getoutput($event, i)">
</div>
<button #click="addItem">Add Item</button>
<pre>{{lists}}</pre>
</div>

nuxtjs with simple-vue-validator as plugin

I am new to Vue and Nuxt and I am building simple forms with simple-vue-validator for validating the form before I submit it to the back-end. I did follow the Nuxtjs plugin's documentation but I am getting some errors 😞
The error I am getting is TypeError: Cannot read property 'Validator' of undefined.
I can't find any $SimpleValidator on this. Not sure if I did something wrong or forgot something, but I keep reading the documentation and this is all I understand. Thanks in advance.
./plugins/simple-validator.js
import Vue from 'vue'
import SimpleValidator from 'simple-vue-validator'
Vue.use(SimpleValidator)
./nuxt.config.js
export default {
...
plugins: [
{ src: '#/plugins/simple-validator.js', ssr: true }
],
...
}
./pages/index.vue*
<template>
<form>
<div class="field-holder">
first name *
<input type="text" v-model="formValues.firstName" :value="formValues.firstName" />
<span v-if="validation.hasError('firstName')">
{{ validation.firstError('firstName') }}
</span>
</div>
<div class="field-holder">
middle name
<input type="text" v-model="formValues.middleName" :value="formValues.middleName" />
</div>
<div class="field-holder">
last name *
<input type="text" v-model="formValues.lastName" :value="formValues.lastName" />
<span v-if="validation.hasError('lastName')">
{{ validation.firstError('lastName') }}
</span>
</div>
<button #click="submit">Submit</button>
</form>
</template>
<script>
export default {
data () {
formValues: {
firstName: '',
middleName: '',
lastName: ''
}
},
validators: {
'formValues.firstName' (value) {
return this.$SimpleValidator.Validator.value(value).required()
},
'formValues.lastName' (value) {
return this.$SimpleValidator.Validator.value(value).required()
}
},
methods: {
submit (event) {
event.preventDefault()
this.$validate()
.then(function (success) {
if (success) {
console.log('Validation succeeded!')
}
})
}
}
}
</script>
I fixed my issue! 🎊
We just need to add import { Validator } from 'simple-vue-validator' to the component that requires it. So the updated component will look like this:
./pages/index.vue
<template>
<form>
<div class="field-holder">
first name *
<input type="text" v-model="formValues.firstName" :value="formValues.firstName" />
<span v-if="validation.hasError('firstName')">
{{ validation.firstError('firstName') }}
</span>
</div>
<div class="field-holder">
middle name
<input type="text" v-model="formValues.middleName" :value="formValues.middleName" />
</div>
<div class="field-holder">
last name *
<input type="text" v-model="formValues.lastName" :value="formValues.lastName" />
<span v-if="validation.hasError('lastName')">
{{ validation.firstError('lastName') }}
</span>
</div>
<button #click="submit">Submit</button>
</form>
</template>
<script>
import { Validator } from 'simple-vue-validator'
export default {
data () {
formValues: {
firstName: '',
middleName: '',
lastName: ''
}
},
validators: {
'formValues.firstName' (value) {
return Validator.value(value).required()
},
'formValues.lastName' (value) {
return Validator.value(value).required()
}
},
methods: {
submit (event) {
event.preventDefault()
this.$validate()
.then(function (success) {
if (success) {
console.log('Validation succeeded!')
}
})
}
}
}
</script>

Need the value (subtraction of value) to effect the value of another input

I'm trying to the value of money_due and have the value for deposit subtracted from it if the deposit input is holding a value. So far, it is adding it on just fine, but it is not removing it when I empty the value.
<div class="col-sm-12 col-md-6 col-lg-4">
<div class="form-group">
<label>Total Sum Due <span class="note">2</span></label>
<input type="text" class="form-control" v-model="document.money_due" placeholder="">
</div>
</div>
<div class="col-sm-12 col-md-6 col-lg-4">
<div class="form-group">
<label>Deposit <span class="note">2</span></label>
<input type="text" class="form-control" #change="hasDeposit" v-model="document.deposit" placeholder="">
</div>
</div>
data() {
return {
now: new Date().toISOString(),
document: {
deposit: '',
money_due: '',
}
}
},
this.document.deposit = this.listing.price;
this.document.money_due = this.document.last_month + this.document.security_deposit,
methods: {
hasDeposit() {
if(this.document.deposit == '') {
return this.document.money_due = this.document.money_due + this.document.deposit;
} else {
return this.document.money_due = this.document.money_due;
}
},
mounted() {
this.hasDeposit();
},
Sorry, but it's a bit unclear what you want to achieve with your code.
I created a snippet where I tried to model the deposit and the money_due relations.
new Vue({
el: "#app",
data: {
listing: {
price: 10
},
document: {
last_month: -20,
security_deposit: 10,
deposit: 0,
money_due: 0
},
},
computed: {
hasDeposit() {
return Number(this.document.money_due) + Number(this.document.deposit)
}
},
mounted() {
this.document.deposit = this.listing.price;
this.document.money_due = this.document.last_month + this.document.security_deposit
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<label>Total Sum Due <span class="note">2</span>
<input type="text" class="form-control" v-model="hasDeposit" placeholder=""></label><br />
<label>Deposit <span class="note">2</span>
<input type="text" class="form-control" v-model="document.deposit" placeholder=""></label>
</div>
I mocked the data that's referenced in your code
Moved hasDeposit() from being a method to computed (it only returns a calculated value)
Changed what's in mounted
I hope this is what you were after.

Why i can't filter local json file in this case (vuejs2)

Before i set vue cli-3 in my project, the filter could work. After I set it up, I cannot reach any result. What am I doing wrong? There isn't any error in the console. I'll share the codes below.
Thanks in advance.
<template>
<div id="preview" class="nested">
<div class="card-body" v-for="item in items" :key="item.id">
<h5 class="card-title">{{item.companyName}}</h5>
<p class="card-text">{{item.positionName}}</p>
<p class="card-text">{{item.cityName}}</p>
<p class="card-text">{{item.townName}}</p>
<p class="card-text">{{item.distance}}</p>
Go!
</div>
</div>
</template>
<script>
export default {
data() {
return {
filteredFounded: [],
founded: [],
items: [],
search: "",
show: false,
city: ""
};
},
created() {
this.$http
.get("http://www.json-generator.com/api/json/get/cguWKZoQMO?indent=2")
.then(res => {
return res.json();
})
.then(res => {
this.founded = res.items;
});
},
methods: {
setFounded() {
this.filteredFounded = this.founded.filter(items => {
return (
items.cityName.toLowerCase() === this.city.toLowerCase() &&
items.positionName.toLowerCase().match(this.search.toLowerCase())
);
});
}
}
};
</script>
i forgot add this code. sorry. everythings look fine but. Cannot read property 'filter' of undefined error fetch now.
<template>
<div id="app" class="nested">
<div class="card w-50">
<div class="search">
<input type="text" class="job" v-model="search" placeholder="Job...">
<select name="" class="city" id="">
<option value="Seçiniz">Seçiniz</option>
<option value="İstanbul">İstanbul</option>
<option value="Ankara">Ankara</option>
<option value="İzmir">İzmir</option>
<option value="Çanakkale">Çanakkale</option>
</select>
</div>
<div class="find">
<button v:on-click="setFounded">Find!</button>
</div>
</div>
</div>
Your problem is not related to Vue Cli 3.x, you had done some mistakes in the promise scope such as return res.json() and this.founded = res.items; but the res object have the following keys :config data headers request status and statusText.
In this situation we need only the data property which is an array of objects (items), so change return res.json() to return res.data and this.founded = res.items; to this.founded = res.data;
add v-model="city" to the select as follow <select name="" class="city" id="" v-model="city"> and you had written v:on-click incorrectly, change it to #click
Finally change return ( items.cityName.toLowerCase() === this.city.toLowerCase() && items.positionName.toLowerCase().match(this.search.toLowerCase()) ); to return ( items.cityName === this.city && items.positionName===this.search );

Filter with search and select inputs

I'm not getting any errors but I think I'm just missing something that I cannot see.
My API's are all working. I just wanted to be able to have a search bar to search the names and then a <select> element for searching through departments. Pretty sure my problem is something to do with the filteredList().
Employees.vue
<script>
<template>
<div class="container">
<div class="form-row align-items-center justify-content-center">
<div class="col-md-6">
<label>Name</label>
<input v-model="search" id="inputKeyword" name="inputKeyword" type="text" class="form-control mb-2 form-control-lg">
</div>
<div class="col-md-4">
<label>Department</label>
<select v-model="selectedDepartment" id="inputfilter" name="inputfilter" class="form-control mb-2 form-control-lg select-department">
<option v-for="department in departments" v-bind:key="department.name">
{{ department.name }}
</option>
</select>
</div>
</div>
<div class="row justify-content-center">
<div class="col-md-10">
<ul class="list-unstyled">
<employee v-for="employee in filteredList" :employee="employee" :key="employee.id"></employee>
</ul>
</div>
</div>
</div>
</template>
<script>
export default {
data () {
return {
search: '',
selectedDepartment: '',
employees: [],
meta: null,
departments: [],
}
},
computed: {
filteredList() {
if(this.selectedDepartment == this.employees.department){
return this.employees == this.employees.filter(employees => {
return employees.department.toLowerCase().includes(this.selectedDepartment.toLowerCase())
})
}else{
return this.employees.filter(employees => {
return employees.name.toLowerCase().includes(this.search.toLowerCase())
})
}
}
},
methods: {
getEmployees(page){
axios('/employees').then((response) => {
this.employees = response.data.data
this.meta = response.data.meta
})
},
getDepartments(){
axios('/departments').then((response) => {
// console.log(response.data)
this.departments = response.data
})
},
},
mounted() {
this.getEmployees()
this.getDepartments()
}
}
</script>

Categories

Resources