Trying to dynamically show the input fields just for a specific iteration - currently it shows the input fields for all of them. I have also prepared this stackblitz fiddle
Currently when i click ”add user to Stockholm”, all inputs fields show and all input fields are populated when I type the name of the new user.
<div
v-for="(city, a) in cities"
v-bind:key="city.name"
style="margin-bottom: 30px"
>
{{ city.name }}
<div
v-for="(user, b) in city.users"
:class="[user.active ? 'green' : 'red']"
>
{{ user.name }} {{ b }}
<button v-on:click="deleteUser(a, b)">Delelete</button>
<button v-on:click="activateUser(a, b)">
{{ user.active ? 'Unactive' : 'Active' }}
</button>
</div>
<div v-if="addUser" style="margin-top: 10px">
<input type="text" v-model="newUserName" placeholder="name" />
<button v-on:click="addUserToArray(a)" class="green">Add</button><br />
</div>
<br />
<button v-on:click="addUser = !addUser">
{{ addUser ? 'Close' : `Add user to ${city.name}` }}
</button>
</div>
data() {
return {
cities: [
{
name: 'Stockholm',
users: [
{ name: 'Johan', active: true },
{ name: 'Adam', active: false },
{ name: 'Anders', active: false },
],
},
{
name: 'New York',
users: [
{ name: 'Andy', active: true },
{ name: 'Mitchell', active: false },
{ name: 'Steve', active: false },
],
},
{
name: 'San Jose',
users: [
{ name: 'Riana', active: true },
{ name: 'Rita', active: false },
{ name: 'Montgomery', active: false },
],
},
],
addUser: false,
newUserName: '',
};
},
Just replace boolean with index:
const app = Vue.createApp({
data(){
return {
cities: [{name: 'Stockholm', users:[{name: 'Johan', active: true}, {name: 'Adam', active: false}, {name: 'Anders', active: false}]}, {name: 'New York', users:[{name: 'Andy', active: true}, {name: 'Mitchell', active: false}, {name: 'Steve', active: false}]}, {name: 'San Jose', users:[{name: 'Riana', active: true}, {name: 'Rita', active: false}, {name: 'Montgomery', active: false}]}],
addUser: null,
newUserName: ''
}
},
computed: {
activeUser: function(){
return this.cities.map((city) => {
return city.users.filter(function(q){
return q.active
})
}).flat()
}
},
methods: {
// 👇 save user index
selectUser(idx) {
this.addUser === idx ? this.addUser = null : this.addUser = idx
},
activateUser(a,b){
this.cities[a].users[b].active = !this.cities[a].users[b].active
},
deleteUser(a,b){
this.cities[a].users.splice(b)
},
addUserToArray(a){
if(this.newUserName == ''){
return false
}
this.cities[a].users.push({name: this.newUserName, active:false})
this.newUserName = ''
this.addUser = false
},
deActiveAllUsers(){
this.cities.map((city) => {
return city.users.forEach(element => element.active = false)
})
},
},
})
app.mount('#demo')
<script src="https://unpkg.com/vue@3/dist/vue.global.prod.js"></script>
<div id="demo">
<h1>All users</h1>
<div v-for="(city, index) in cities" :key="index" style="margin-bottom: 30px">
{{city.name}}
<div v-for="(user, b) in city.users" :class="[user.active ? 'green' : 'red']">
{{user.name}} {{b}}
<button @click="deleteUser(index, b)">Delelete</button>
<button @click="activateUser(index, b)">{{user.active ? 'Unactive' : 'Active'}}</button>
</div>
<!-- 👇 compare user index -->
<div v-if="addUser === index" style="margin-top: 10px;">
<input type="text" v-model="newUserName" placeholder="name"/>
<button @click="addUserToArray(index)" class="green">Add</button><br>
{{newUserName}}
</div>
<br>
<!-- 👇 save user index -->
<button @click="selectUser(index)">{{addUser === index ? 'Close' : `Add user to ${city.name}`}}</button>
</div>
<button @click="deActiveAllUsers">Deactiev all users</button>
<h1>Active users</h1>
<div v-if="activeUser.length > 0">
<div v-for="user in activeUser">{{user.name}}</div>
</div>
<div v-else>
No active users
</div>
</div>
Observation/Suggestion :
As input fields will be dynamic based on the Add user to city button click. Hence, v-model should be unique so that when you trying to add the user in different city it will not retain the previously entered v-model value as all share the same name.
In template :
<input type="text" v-model="newUserName[a]" placeholder="name" />
In data object :
newUserName: []
You can simply achieve it by just assigning the index to addUser variable (addUser = a) on button click.
Working Demo :
new Vue({
el: '#app',
data: {
cities: [
{
name: 'Stockholm',
users: [
{ name: 'Johan', active: true },
{ name: 'Adam', active: false },
{ name: 'Anders', active: false },
],
},
{
name: 'New York',
users: [
{ name: 'Andy', active: true },
{ name: 'Mitchell', active: false },
{ name: 'Steve', active: false },
],
},
{
name: 'San Jose',
users: [
{ name: 'Riana', active: true },
{ name: 'Rita', active: false },
{ name: 'Montgomery', active: false },
],
},
],
addUser: null,
newUserName: []
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<div v-for="(city, a) in cities" v-bind:key="city.name" style="margin-bottom: 30px">
{{ city.name }}
<div v-for="(user, b) in city.users" :class="[user.active ? 'green' : 'red']">
{{ user.name }} {{ b }}
<button v-on:click="deleteUser(a, b)">Delelete</button>
<button v-on:click="activateUser(a, b)">
{{ user.active ? 'Unactive' : 'Active' }}
</button>
</div>
<div v-if="addUser === a" style="margin-top: 10px">
<input type="text" v-model="newUserName[a]" placeholder="name" />
<button v-on:click="addUserToArray(a)" class="green">Add</button><br />
</div>
<br/>
<button v-on:click="addUser = a">
{{ addUser === a ? 'Close' : `Add user to ${city.name}` }}
</button>
</div>
</div>