Let's say I have component like this:
<template>
<div>
<div v-for="n in 2" :key="n">
<div v-for="i in 2" :key="i">
<input type="number" v-model="foo[n][i]">
</div>
</div>
<pre>{{ foo }} </pre>
</div>
</template>
This render 4 inputs. Now when I enter something into this inputs for example 1 to 4 to each I would like foo to become:
[
[1,2],
[3,4]
]
instead I have an error
TypeError: Cannot read property '1' of undefined
Your error is from the fact that the n and i indexes start at 1 in the v-for
Also for a more generic approach you could generate an array from the dimensions in your created lifecycle.
Vue.config.devtools = false;
Vue.config.productionTip = false;
var app = new Vue({
el: '#app',
data: {
x: 2,
y: 2,
array: []
},
created() {
for (let i = 0; i < this.y; i++) {
let row = [];
for (let j = 0; j < this.x; j++) {
row.push(0);
}
this.array.push(row);
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<div v-for="n in y" :key="n" style="display:flex">
<div v-for="i in x" :key="i">
<input type="number" v-model.number="array[n - 1][i - 1]">
</div>
</div>
{{array}}
</div>
you can try this solution: by adding a method to initial new object if it's undefined
<template>
<div>
<div v-for="n in 2" :key="n">
<div v-for="i in 2" :key="i">
<input type="number" v-model="getfoo(n)[i]">
</div>
</div>
<pre>{{ foo }} </pre>
</div>
</template>
methods: {
getfoo(n) {
if(!this.foo[n]) this.foo[n] = {}; // or = []
return this.foo[n];
},
}
Related
Here what happening is when i click on edittask at that time that task name should be set in the input value and its setting value as (object object) but i want to set the task name instead of it. can anyone help me with that.
this is what i am getting in console when i click on edit button and what its setting the value in input box
<script>
export default {
data(){
return{
newTaskTitle: "",
isEditing : false
}
},
props:{
Task:{
type:Array,
required: true
},
},
methods:{
removeTask: function(idx){
this.Index = idx;
this.$emit('remove',this.Index);
},
EditTaskI(tsk){
this.task = tsk;
console.log(this.task);
this.isEditing = this.isEditing == true ? false : true;
this.newTaskTitle = this.task;
},
TaskUpdated(){
this.isEditing = this.isEditing == true ? false : true;
}
}
}
</script>
<template>
<section v-if="Task.length > 0" class="taskMainSection">
<section v-for="(tasks,index) in Task" :key="index" class="sectionTask" >
<section class="TaskBox" v-if="!isEditing">
<div class="TaskTitleList" >
<div class="TaskSection">
<p class="listTask">{{ tasks.Task }}</p>
</div>
</div>
<div class="OptionSectionMain">
<div class="OptionSection">
<p class="removeTask fa fa-close" #click="removeTask(index)"></p>
<p class="editTask fa fa-edit" #click="EditTaskI(tasks)"></p>
</div>
</div>
</section>
<section class="TaskBoxEdit" v-else>
<div class="TaskTitleList" >
<div class="TaskSection">
<input type="text" class="form-control" :value="newTaskTitle">
</div>
</div>
<div class="OptionSectionMain">
<div class="OptionSection">
<p class="removeTask fa fa-check" #click="TaskUpdated"></p>
</div>
</div>
</section>
</section>
</section>
</template>
<script>
export default {
data(){
return{
newTaskTitle: "",
isEditing : false
}
},
props:{
Task:{
type:Array,
required: true
},
},
methods:{
removeTask: function(idx){
this.Index = idx;
this.$emit('remove',this.Index);
},
EditTaskI(tsk){
this.task = tsk;
console.log(this.task);
this.isEditing = this.isEditing == true ? false : true;
this.newTaskTitle = this.task;
},
TaskUpdated(){
this.isEditing = this.isEditing == true ? false : true;
}
}
}
</script>
<template>
<section v-if="Task.length > 0" class="taskMainSection">
<section v-for="(tasks,index) in Task" :key="index" class="sectionTask" >
<section class="TaskBox" v-if="!isEditing">
<div class="TaskTitleList" >
<div class="TaskSection">
<p class="listTask">{{ tasks.Task }}</p>
</div>
</div>
<div class="OptionSectionMain">
<div class="OptionSection">
<p class="removeTask fa fa-close" #click="removeTask(index)"></p>
<!--In below line just need to change "tasks.task" on the place of "tasks" -->
<p class="editTask fa fa-edit" #click="EditTaskI(tasks)"></p>
</div>
</div>
</section>
<section class="TaskBoxEdit" v-else>
<div class="TaskTitleList" >
<div class="TaskSection">
<input type="text" class="form-control" :value="newTaskTitle">
</div>
</div>
<div class="OptionSectionMain">
<div class="OptionSection">
<p class="removeTask fa fa-check" #click="TaskUpdated"></p>
</div>
</div>
</section>
</section>
</section>
</template>
I have been struggling trying to get Vue.js to dynamically create the form while updating the ids of the form elements. I am new to Vue.js.
I have tried to have each form as a vue component, but I can't seem to update the ids, or they all seem to have the same id when inspecting with vue dev tools.
I have also tried to nest vue instances, then I am able to get the id's to be correct but then the other functionality doesn't work.
html
<body class="bg-dark">
<!--style="background-color:Black;"-->
<div class="container-fluid p-4 bg-dark text-white" style="margin-top:74px">
<div class="row">
<div class="col-md-3"></div>
<div class="col-md-7">
<div class="row" id="app">
<!-- <member-form v-for="member in members" :key="member_form_component_id"></member-form>-->
</div>
<div class="row" id="add_button_div">
<button class="btn btn-primary m-2" type="button" id="id_btn_add_row"
v-on:click="add_member">Add Member</button>
</div>
</div>
<div class="col-md-2"></div>
</div>
</div>
</body>
<script type="text/x-template" id="member-form-template">
<div class="row" id="form-div-__prefix__">
<div class="row input-group input-group-md">
<div class="col border mx-auto">
<input type="text" name="member_set-__prefix__-first_name" placeholder="First Name" autocomplete="off"
class="form-control m-2" maxlength="100" id="id_member_set-__prefix__-first_name"
v-model="member_first_name">
</div>
<div class="col border mx-auto">
<input type="text" name="member_set-__prefix__-last_name" placeholder="Last Name" autocomplete="off"
class="form-control m-2" maxlength="100" id="id_member_set-__prefix__-last_name"
v-model="member_last_name">
</div>
<div class="col border mx-auto">
<input type="date" class="form-control m-2" name="member_set-__prefix__-dob" id="id_member_set-__prefix__-dob"
v-model="member_dob" #blur="joad_check">
</div>
<button class="btn btn-danger" id="id_member_button-__prefix__">Delete</button>
</div>
<div class="row input-group input-group-md border" id="form-div-__prefix__-joad" v-if="joad_seen">
<div class="col">To register for a JOAD session select start date:</div>
<div class="col">
<select name="member_set-__prefix__-joad" class="form-control m-2 costs" id="id_member_set-__prefix__-joad" v-model="member_joad">
<option value="" selected>None</option>
<option value="2020-05-24">2020-05-24</option>
</select>
</div>
</div>
</div>
</script>
js
var app1
var form_count = 0
var max_forms
var MemberForm
var MemberFormComponentId = 0
$(document).ready(function() {
app1 = new Vue({
el: '#app',
data: {
members: [],
},
methods: {
add_member: function() {
let v = new Vue({
el: "#form-div-" + form_count,
created: function() {
$("#app").html($("#app").html() + $("#member-form-template").html().replace(/__prefix__/g, form_count))
},
data: {
seen: false,
first_name: $("#id_member_set-" + form_count + "-first_name"),
last_name: $("#id_member_set-" + form_count + "-last_name"),
dob_id: $("#id_member_set-" + form_count + "-dob"),
joad: $("#id_member_set-" + form_count + "-joad"),
form_id: form_count,
},
methods: {
joad_check() {
if (this.member_dob === "") {
return false
}
var bd = new Date(this.member_dob);
var joad_date = new Date();
joad_date.setFullYear(joad_date.getFullYear() - 21);
this.joad_seen = (bd > joad_date && bd < new Date())
},
disable_delete () {
$("#id_member_button-"+ this.form_id).prop('disabled', true);
}
},
mounted: function () {
if(this.form_id == 0) {
$("#id_member_button-"+ this.form_id).prop('disabled', true);
}
}
})
this.members.push(v)
form_count++
MemberFormComponentId++
if( form_count > max_forms) {
$("#id_btn_add_row").prop('disabled', true);
} else {
$("#id_btn_add_row").prop('disabled', false);
}
}
}
})
app1.add_member()
})
I'm trying to create a modal form that will add a record. I am able to display the default values from data but as soon as I try to modify the field, I get the following error whenever I try to type changes to the input box.
*vue.min.js:6 TypeError: Cannot use 'in' operator to search for 'fullname' in undefined
at a.ke [as $set] (vue.min.js:6)
at input (eval at Ya (vue.min.js:1), <anonymous>:3:2182)
at He (vue.min.js:6)
at HTMLInputElement.n (vue.min.js:6)
at HTMLInputElement.Yr.o._wrapper (vue.min.js:6)*
In given Below I added the code of the component I'm trying to create:
Any help, please.
var bus = new Vue();
Vue.component('leagues_add', {
props: {
show: Boolean,
is_admin: Boolean,
},
data: function () {
return {
newLeague: {"fullname":"a", "notes":"b", "group_image_path": "c"} // remember to always enclose the fieldnames in doublequotes
}
},
methods: {
closeModal() {
this.show = false;
},
showModal() {
this.show = true;
},
addLeague() {
event.preventDefault();
var formData = this.toFormData(this.newLeague);
axios.get("http://"+ window.location.hostname +"/db/leagues/index.php?action=create").then(function(response){
if (response.data.error) {
app.errorMessage = response.data.message;
} else {
app.leagues = response.data.leagues;
}
});
},
toFormData(obj) {
var fd = new FormData();
for (var i in obj) {
fd.append(i, obj[i]);
}
return fd;
},
}
,
template:
`
<!-- Add new Leage -->
<div>
<div class="text-center pb-3" >
<button type="button" class="btn btn-outline-primary bg-success text-white" #click="showModal"><b class="" style="">Add League</b></button>
</div>
<transition name="modal">
<div id="overlay" v-show="this.show">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Add League</h5>
<button type="button" class="close" #click="closeModal"><span aria-hidden="true">×</span></button>
</div>
<div class="modal-body">
<form action="#" method="POST">
<div class="form-group">
<input type="text" v-model="this.newLeague.fullname" class="form-control form-control-md" placeholder="Name of League">
</div>
<div class="form-group">
<textarea v-model="this.newLeague.notes" rows="3" cols="100%" name="notes" class="form-control form-control-md" placeholder="Describe this league">
</textarea>
</div>
<div class="form-group form-inline ">
<div class="col-12 p-0">
<input type="url" v-model="this.newLeague.group_image_path" class="col-5 pull-left form-control form-control-md" placeholder="Image URL">
<button class="col-4 btn btn-primary btn-md">Image</button>
</div>
</div>
<div class="form-group">
<button class="btn btn-info btn-block btn-md" #click="closeModal();addLeague();">Add this league</button>
</div>
</form>
</div>
</div>
</div>
</div>
</transition>
<div>
`
});
<div class="row">
<div class="col-md-12">LEAGUES SECTION</div>
</div>
<div class="row mt-2">
<div class="col-md-12">
<leagues_add :show="true" />
</div>
</div>
The problem is here:
v-model="this.newLeague.fullname"
You cannot use this. with v-model. Instead it should be:
v-model="newLeague.fullname"
You should also remove all other references to this within your template. In many cases they are harmless but sometimes, such as with v-model, they will cause problems.
Complete examples below. Note how the first input does not function correctly when editing the text.
new Vue({
el: '#app1',
data () {
return { newLeague: { fullname: 'League 1' } }
}
})
new Vue({
el: '#app2',
data () {
return { newLeague: { fullname: 'League 1' } }
}
})
<script src="https://unpkg.com/vue#2.6.11/dist/vue.js"></script>
<div id="app1">
<input type="text" v-model="this.newLeague.fullname">
{{ newLeague.fullname }}
</div>
<div id="app2">
<input type="text" v-model="newLeague.fullname">
{{ newLeague.fullname }}
</div>
I followed the way #skirtle wrote the data section and it worked.
My original syntax was:
data: function () {
return {
newLeague: {"fullname":"a", "notes":"b", "group_image_path": "c"} }
}
to
data () {
return { newLeague: { fullname: 'League 1' } }
}
I've got input fields on one side of the page which is then displayed into boxes on the other side that actively shows what a user has inputted but in an organized way. I need to actively search the input fields for a specific string, in this case 'key' and then it would instantly change to a value stored in data. I've got a searchkeyword() function that should go through the array of objects where input fields are stored but haven't made it work just yet.
For example, if user types in 'key this is david' in input1 then it would change 'key' to it's stored value which is 'hello'. The value of key is also changing if a user clicks on other options. Really don't know where to go from here so any input helps :)
var app = new Vue({
el: '#app',
data: {
activeKeyword: 'HELLO',
inputs: [
{
input1: 'oranges',
input2: 'Online',
input3: 'Free'
}
]
},
methods: {
searchKeyword() {
for(var x = 0; x < this.ads.length; x++){
for(var input in this.inputs[x]){
if(this.ads[x] !== "boolean"){
this.ads[x][input] = String(this.inputs[x][input]).replace(/_keyword_/g, this.activeKeyword)
}
}
}
}
}
})
<link href="https://cdnjs.cloudflare.com/ajax/libs/bulma/0.7.2/css/bulma.min.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<div class="box" v-for="(key, index) in inputs">
<div>
<span class="headline-ok">{{key.input1}} | </span>
<span class="headline-ok">{{key.input2}} | </span>
<span class="headline-ok">{{key.input3}}</span>
<br>
</div>
</div>
<div class="box" v-for="(key, index) in inputs">
<div class="form-inputs">
<label class="label is-capitalized">Input 1</label>
<div class="field">
<div class="control is-expanded">
<input class="input" type="text" v-model="key.input1">
</div>
</div>
</div>
<div class="form-inputs">
<label class="label is-capitalized">Headline Two </label>
<div class="field">
<div class="control is-expanded">
<input type="text" v-model="key.input2" class="input">
</div>
</div>
</div>
<div class="form-inputs">
<label class="label is-capitalized">Headline Three </label>
<div class="field">
<div class="control is-expanded">
<input type="text" v-model="key.input3" class="input">
</div>
</div>
</div>
</div>
</div>
Use a filter method to search for the matching substring of each input:
new Vue({
el: '#app',
filters: {
keyword(value, key, replacer) {
return (value && value.includes(key)) ? value.replace(key, replacer) : value
}
},
data() {
return {
replacer: 'Hello',
inputs: [{
key: 'foo',
model: null
},
{
key: 'bar',
model: null
},
{
key: 'baz',
model: null
}
],
demo: '',
demoString: 'Watch as blah is replaced with Hello'
}
},
mounted () {
let index = 0
setInterval(() => {
this.demo += this.demoString.charAt(index)
if (this.demo === this.demoString) {
this.demo = ''
index = 0
} else {
index++
}
}, 250)
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<template v-for="(i, j) in inputs">
<label>Replace {{ i.key }} with {{ replacer }}</label>
<input v-model="i.model" :key="`input-${j}`">
<p :key="`p-${j}`">{{ i.model | keyword(i.key, replacer) }}</p>
</template>
<hr/>
<label>{{ this.demoString }}</label>
<input v-model="demo">
<p>{{ demo | keyword('blah', 'Hello') }}</p>
</div>
I have a problem on reactivating the button even if the conditional statement works.
it looked like the v-model wasn't communicating with the data but with a simple interpolation the value was updated.
I don't really know where I'm doing wrong on the code.
<template>
<div class="col-sm-6 col-md-4">
<div class="panel panel-success">
<div class="panel-heading">
<h3 class="panel-title">{{stock.name}}
<small>(Price: {{stock.price}})</small>
</h3>
</div>
<div class="panel-body">
<div class="pull-left">
<input v-model="quantity" type="number" class="form-control" placeholder="Quantity">
</div>
<div class="pull-right">
<button class="btn btn-success" #click="buyStock" :disabled="isDisabled">Buy</button>
</div>
<p>{{quantity}}</p>
</div>
</div>
</div>
</template>
<script>
export default {
props: [
"stock",
],
data() {
return {
quantity: 0,
}
},
methods: {
buyStock() {
const order = {
stockId: this.stock.id,
stockPrice: this.stock.price,
quantity: this.quantity
};
console.log(order);
this.$store.dispatch("buyStock", order);
this.quantity = 0;
}
},
computed: {
isDisabled() {
if (this.quantity <= 0 || !Number.isInteger(this.quantity)) {
return true;
} else {
return false;
}
}
}
}
</script>
By default, the v-model directive binds the value as a String. So both checks in your isDisabled computed will always fail.
If you want to bind quantity as a number, you can add the .number modifier like so:
<input v-model.number="quantity" type="number" ... >
Here's a working example:
new Vue({
el: '#app',
data() {
return { quantity: 0 }
},
computed: {
isDisabled() {
return (this.quantity <= 0 || !Number.isInteger(this.quantity))
}
}
})
<template>
<div class="col-sm-6 col-md-4">
<div class="panel panel-success">
<div class="panel-heading">
<h3 class="panel-title">{{stock.name}}
<small>(Price: {{stock.price}})</small>
</h3>
</div>
<div class="panel-body">
<div class="pull-left">
<input v-model="quantity" type="number" class="form-control" placeholder="Quantity">
</div>
<div class="pull-right">
<button class="btn btn-success" #click="buyStock" :disabled="isDisabled">Buy</button>
</div>
<p>{{quantity}}</p>
</div>
</div>
</div>
</template>
<script>
</script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.4.4/vue.min.js"></script>
<div id="app">
<input v-model.number="quantity" type="number">
<button :disabled="isDisabled">Foo</button>
</div>