How to dynamically create Input Fields in VueJS - javascript

I'm trying to dynamically add or remove input fields on the go.
I got a simple solution from here https://smarttutorials.net/dynamically-add-or-remove-input-textbox-using-vuejs/, which works. However saving input values in the database stops it's functionality.
Component Code:
<div class="form-group" v-for="(input,k) in inputs" :key="k">
<input type="text" class="form-control" v-model="input.name" />
<input type="text" class="form-control" v-model="input.party" />
<span>
<i
class="fas fa-minus-circle"
#click="remove(k)"
v-show="k || ( !k && inputs.length > 1)"
></i>
<i
class="fas fa-plus-circle"
#click="add(k)"
v-show="k == inputs.length-1"
></i>
</span>
</div>
<button #click="addCandidate">
Submit
</button>
<script>
export default {
data() {
return {
inputs: [
{
name: "",
party: ""
}
]
};
},
methods: {
add(index) {
this.inputs.push({ name: "", party: "" });
},
remove(index) {
this.inputs.splice(index, 1);
},
addCandidate() {
axios
.post("/candidates", this.inputs)
.then(response => {})
.catch(error => {});
}
}
};
</script>
I always get a 422 error, saying the input field is empty.

This is not a Vue problem. Before you send an array you'll need to call JSON.stringify() on it, then decode it on the server with Laravel. Example:
foreach(json_decode($request -> input('my_prop_name ')) as $my_object_in_array)
{
print_r($my_object_in_array); // this is your object name/party
}
Vue code working like magic. :)
new Vue({
el: '#app',
data () {
return {
inputs: [{
name: '',
party: ''
}]
}
},
methods: {
add () {
this.inputs.push({
name: '',
party: ''
})
console.log(this.inputs)
},
remove (index) {
this.inputs.splice(index, 1)
},
addCandidate () {
axios
.post('/candidates', {
my_prop_name: JSON.stringify(this.inputs)
})
.then(response => {})
.catch(error => {})
}
}
})
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.14.0/css/all.min.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id=app>
<div class="form-group" v-for="(input,k) in inputs" :key="k">
<input type="text" class="form-control" v-model="input.name">
<input type="text" class="form-control" v-model="input.party">
<span>
<i class="fas fa-minus-circle" #click="remove(k)" v-show="k || ( !k && inputs.length > 1)">Remove</i>
<i class="fas fa-plus-circle" #click="add(k)" v-show="k == inputs.length-1">Add fields</i>
</span>
</div>
<button #click="addCandidate">
Submit
</button>
</div>

Related

Vue.js What is the correct way to display the selected item from the dropdown?

I am trying to display the selected value in a pill badge that can be removed.
I tried using v-for but I keep breaking the form or it display the selected like this:
here is my code:
data()
{
sport: {
name: '',
sportSize: null,
externalId: '',
teamId: null,
},
teamTransformer: teamTransformer,
comboConfig: {
itemPerPage:30
}
additionalSearchField: null,
},
computed:
{
...mapGetters({
getTeamById: "getTeamById"
})
methodds:{
...mapActions({
fetchTeamsByName: "fetchTeamsByName",
fetchTeamtDetails: "fetchTeamDetails",
fetchOfferByName: "fetchOfferByName",
fetchTeamInfo: "fetchTeamInfo"
}),
async onTeamComboSelect({value})
{
this.teamId = value;
this.form.team = value;
this.form.teamId = value;
this.additionalSearchField = {teamId: this.teamId};
await this.fetchTeamInfo({id: this.teamId});
},
}
<div class="creation-form-field creation-form-field--with-error-wrapper">
<label
for="team"
class="inline-3-columns--camp-wizard"
>
<span class="title__field">Client*</span>
<combo-select
id="teams"
v-model="sport.teamId"
api-location="fetchTeamsByName"
api-details-location="fetchTeamDetails"
search-parameter="teamName"
:additional-search-fields="additionalSearchField"
:transformer="teamTransformer"
:config="{
...comboConfig,
searchLabel: 'Search teams',
showDefaultLabelOnSelect: true
}"
class="input input__typeahead"
#on-select-item="onTeamComboSelect"
/>
<input
v-model="sport.teamId"
type="hidden"
name="teamId"
class="form-control"
:data-vv-scope="sections[0].name"
value="team"
>
</label>
<div class="sport-results-decoration">
<div class="results">
<span
v-for="(teamName, key) in sport.teamId"
class="badge badge-pill badge-success"
>
<span>{{ teamName }}</span>
<font-awesome-icon
icon="times"
class="close"
#click="removeTeam()"
/>
</span>
</div>
</div>
</div>
I also tried {{sport.team}} and {{sport.teamId}} but I get
this
this is how my selected item shows up in inspector:
I tried using label but it shows up empty. Please help, how can I solve this problem?
Many thanks for any help :)

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>

passing data between components in vuejs

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>

Posting Object to Api using Vue.js and Axios

I'm trying to post a new object to my api array using axios and Vue.js. I'm trying to add the functionality to add a new object and display it on the timeline. I can see that when I post a new title I get a console.log of the object but it is not added to the correct array from the api, there is no new id associated with the new object.
Index.html
<body>
<div id="app">
<template>
<form #submit.prevent>
<input type="text" v-model="postBody"/>
<button type="button" class="btn btn-primary pull-right" data-toggle="modal" #click="postPost()">Post Title</button>
</form>
<ul v-if="errors && errors.length">
<li v-for="error of errors">
{{error.message}}
</li>
</ul>
</template>
<br>
<!-- <p>{{ status }}</p> -->
<template v-for="(results, index) in result">
<div class="card" style="width: 20rem; display:inline-block;">
<div class="card-block">
<p>{{ results.id }}</p>
<p>{{ results.title }}</p>
<!-- <button type="button" class="btn btn-danger pull-right" data-toggle="modal" v-on:submit.prevent="deleteData(index)" #click="deleteData(results, index) in result">Delete</button> -->
<button type="button" class="btn btn-danger pull-right" data-toggle="modal" #click="deleteData(results, index)">Delete</button>
<h1> {{ results.comments}} </h1>
</div>
</div>
</template>
</div>
</body>
Main.js
var vm = new Vue ({
el: '#app',
data: {
result: '',
title: '',
data: '',
postBody: '',
User: { title: '' },
errors: []
},
created: function(){
this.getResult();
},
methods: {
getResult: function() {
// this.results = 'Loading...';
axios.get('https://my-json-server.typicode.com/shaneogrady/json/db')
.then(response => {
// console.log(response.data);
this.result = response.data.posts;
console.log(this.result);
});
},
deleteData: function(result, id) {
axios.delete('https://my-json-server.typicode.com/shaneogrady/json/posts/' + result.id)
.then(response => {
this.result.splice(id, 1)
console.log(this.result);
});
},
postPost() {
axios.post('https://my-json-server.typicode.com/shaneogrady/json/posts/', {
// id: 4,
// title: 'Shane',
body: this.postBody
})
.then(response => { console.log(response.data); this.result.push(response.data) })
.catch(e => {
this.errors.push(e)
})
}
}
});
Try adding #submit.prevent on the form element
<div id="app">
<form #submit.prevent>
<h4> Add Title</h4>
<div class="form-group">
<label class="pull-left"> Title </label>
<input type="text" class="form-control" placeholder="Title " v-model="User.title">
</div>
<button type="submit" class="btn btn-large btn-block btn-primary" #click="postPost">Submit</button>
</form>
...
if u need to get results after creation of new object u just need to call ur getResult function inside postPost function
like this :
postPost() {
axios.post('https://my-json-server.typicode.com/shaneogrady/json/posts/', {
// id: 4,
// title: 'Shane',
body: this.postBody
})
.then(response => {
this.getResult();
console.log(response.data);
})
.catch(e => {
this.errors.push(e)
})

Vue.js Todo task with CSS animation and transition effect

How do you add a visual indicator such as a spinner and a tick when you save a new task in the example below.
Basically when I click the save button the following should happen:
Save button should become invisible
Cancel icon should become invisible, a spinner should appear instead to indicate something is happening
A tick icon should briefly appear if successfully saved and then disappear before displaying the delete icon.
The v-show that i'm using doesn't seem to work when I try to replicate a sleep effect to display the spinner when the updateTask() method is fired?
<div class="container" id="el">
<div class="row">
<div class="col-md-10"><h1>Tasks</h1></div>
<div class="col-md-2">
<button id="" class="btn btn-primary pull-right" type="button" v-on:click="createTask()">
Add New
</button>
</div>
</div>
<div class="row" >
<div class="col-sm-10 col-md-8">
<table class="table table-striped">
<tr class="row" v-for="task in tasks">
<td class="col-sm-5">
<div class="form-group">
<label class="sr-only" for="name">
Name</label>
<input v-if="editKey == task.id" name="name" type="text" class="form-control" v-model="task.name">
<span v-else v-on:click="editTask(task)">{{ task.name }}</span>
</div>
</td>
<td class="col-sm-5">
<div class="form-group">
<label class="sr-only" for="date">
Date</label>
<input v-if="editKey == task.id" name="date" type="text" class="form-control date-picker" v-pikaday="task.date">
<span v-else v-on:click="editTask(task)">{{ task.date }}</span>
</div>
</td>
<td class="col-sm-2">
<ul class="list-inline">
<li v-if="editKey == task.id" >
<button class="btn btn-success btn-sm" type="button" v-on:click="updateTask(task)" v-show="!loading">
Save
</button>
</li>
<li v-if="editKey == task.id ">
<span v-show="!loading"><i class="fa fa-times text-danger" v-on:click="cancelEdit(task)" title="Cancel"></i></span>
<span v-show="loading"> <i class="fa fa-spinner"></i></span>
</li>
<li v-if="editKey !== task.id">
<i class="fa fa-trash-o text-muted" v-on:click="removeTask(task)" title="Delete"></i>
</li>
<li v-if="editKey !== task.id && task.id == -1">
<i class="fa fa-exclamation-triangle text-warning" title="Unsaved"></i>
</li>
</ul>
</td>
</tr>
</table>
</div>
<pre>{{$data | json }}</pre>
</div>
</div>
<script>
Vue.directive('pikaday', {
twoWay: true,
bind: function () {
var self = this
$(this.el)
.pikaday({
format: 'D MMM YYYY',
defaultDate: moment().toDate()
})
.on('change', function () {
self.set(this.value)
})
},
update: function (value) {
$(this.el).val(value).trigger('change')
},
unbind: function () {
$(this.el).off().pikaday('destroy')
}
})
var vm = new Vue({
el: '#el',
data: {
editKey: '',
loading: false,
beforeEditCache: {
id: '',
name: '',
date: ''
},
editedTask: null,
tasks: [
{id: 1, name: 'Task A', date: '25 Dec 2015'},
{id: 2, name: 'Task B', date: '26 Dec 2015'}
]
},
methods: {
createTask: function() {
// if previously we were editing a task, lets cancel the edit
if (this.editedTask){
this.cancelEdit();
}
// add new task with id -1 to indicate it hasn't been persisted
this.tasks.push({
id: -1,
name: '',
date: ''
});
// set edit key
this.editKey = -1;
},
storeTask: function(task) {
// check if mandatory field completed
if (!task.name || !task.date) {
return;
}
// persist the task by generating valid id
task.id = Math.floor((Math.random() * 100) + 1);
},
editTask: function(task) {
// if we were previously editing a task and clicked on another to edit without saving, let cancel the edit
if (this.editedTask){
this.cancelEdit();
}
this.setBeforeEditCache(task);
this.editedTask = task;
this.editKey = task.id;
},
updateTask: function (task) {
// if its a new task
if (task.id == -1){
this.storeTask(task);
}
// otherwise we are editing an existing task
else {
if (!this.editedTask.name || !this.editedTask.date) {
return;
}
this.loading = true;
this.sleep(3000);
this.editedTask = null;
this.editKey = '';
this.loading = false;
}
},
cancelEdit: function (task = null) {
if (task && task.id == -1) {
this.removeTask(task);
}
else {
this.editedTask.name = this.beforeEditCache.name;
this.editedTask.date = this.beforeEditCache.date;
this.editedTask = null;
this.editKey = '';
}
},
removeTask: function(task) {
this.tasks.$remove(task);
},
setBeforeEditCache: function(task) {
this.beforeEditCache.id = task.id;
this.beforeEditCache.name = task.name;
this.beforeEditCache.date = task.date;
},
sleep: function(milliseconds) {
var start = new Date().getTime();
for (var i = 0; i < 1e7; i++) {
if ((new Date().getTime() - start) > milliseconds){
break;
}
}
}
}
})
</script>
This is the fiddle https://jsfiddle.net/ozzii/6oe2k3py/
* UPDATE *
So I've managed to update this - see the new fiddle, to provide the functionality required. But it's a very messy solution - code is all over the place. Anyone know of a better/cleaner way to refactor and achieve the same functionality and perhaps provide a fade in/out affect to the tick when you save the element?
Here is the part about the sleep effect. You can use setTimeout and use the bind function to make sure the this context inside it is the Vue component.
this.loading = true;
setTimeout((function(){
this.editedTask = null;
this.editKey = '';
this.loading = false;
}).bind(this), 3000);

Categories

Resources