v-show within a v-for loop - show only clicked option - vue.js

I have a v-for loop set up there is a show button within the loop, when I click this, at the moment it opens all the hidden divs, I want it to open only the relevant one thats clicked. Maybe there is another way to do this or a better way I have been reading that v-show might net be suited to be used inside a loop.
<div id="app" class="container">
<form>
<div v-for="item in filterOptions" :class="{ activeclass: isActive }">
<dl>
<dt>
<!--- click to show --->
<button #click="toggle(item)" #click.self.prevent>{{item.name}}</button>
</dt>
<!--- show this --->
<dd v-show="isActive" :id="item.name">
<button v-for="option in item.values" v-on:click="option.selected = !option.selected" type="button" class="btn btn-primary" :class="{'active' : option.selected}">
{{option.name}}
</button>
</dd>
</dl>
</div>
</form>
<div v-for="item in products">
<div>
<h5>
{{item.name}}
</h5>
<h6>{{item.region}}</h6>
<a>View</a>
</div>
</div>
</div>
var main = new Vue({
el: '#app',
data: {
products: [
{
name: "davy",
region: "Highland",
category: "Single Cask"
},
{
name: "bill",
region: "Islay",
category: "New releases"
},
{
name: "shena",
region: "Highland",
category: "Remarkable Regional Malt"
}
],
filterOptions: [
{
name: 'Region',
isActive: false,
values: [
{
name: 'Highland',
selected : false
},
{
name: 'Speyside',
selected : false
},
{
name: 'Islay',
selected : false
}
]
},
{
name: 'Price',
isActive: false,
values: [
{
name: '£1-50',
selected : false
},
{
name: '£51-100',
selected : false
},
]
},
{
name: 'Category',
isActive: false,
values: [
{
name: 'Single Cask',
selected : false
},
{
name: 'Remarkable Regional Malt',
selected : false
},
{
name: 'New releases',
selected : false
}
]
}
],
isHidden: true,
isActive: false
},
methods: {
toggle: function () {
this.isActive = !this.isActive;
}
}
})

I hope this will help you
window.onload = function() {
new Vue({
el: '#app',
data: {
products: [
{
name: "davy",
region: "Highland",
category: "Single Cask"
},
{
name: "bill",
region: "Islay",
category: "New releases"
},
{
name: "shena",
region: "Highland",
category: "Remarkable Regional Malt"
}
],
filterOptions: [
{
name: 'Region',
isActive: false,
values: [
{
name: 'Highland',
selected : false
},
{
name: 'Speyside',
selected : false
},
{
name: 'Islay',
selected : false
}
]
},
{
name: 'Price',
isActive: false,
values: [
{
name: '£1-50',
selected : false
},
{
name: '£51-100',
selected : false
},
]
},
{
name: 'Category',
isActive: false,
values: [
{
name: 'Single Cask',
selected : false
},
{
name: 'Remarkable Regional Malt',
selected : false
},
{
name: 'New releases',
selected : false
}
]
}
],
isHidden: true,
isActive: false,
selectedName: ''
},
methods: {
toggle: function (item) {
this.selectedName = item.name
}
}
})
}
.success {
display: block !important;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id='app'>
<div id="app" class="container">
<form>
<div v-for="item in filterOptions" :class="{ activeclass: isActive }">
<dl>
<dt>
<!--- click to show --->
<button v-on:click.prevent="toggle(item)">{{item.name}}</button>
</dt>
<!--- show this --->
<dd :id="item.name" v-if="item.name == selectedName" v-bind:class="{'success': (item.name == selectedName)}">
<button v-for="option in item.values" v-on:click="option.selected = !option.selected" type="button" class="btn btn-primary" :class="{'active' : option.selected}">
{{option.name}}
</button>
</dd>
</dl>
</div>
</form>
<div v-for="item in products">
<div>
<h5>
{{item.name}}
</h5>
<h6>{{item.region}}</h6>
<a>View</a>
</div>
</div>
</div>
</div>

Define activeitem in data and set this.activeitem = item.namein toggle method.
toggle: function (item) {
this.isActive = !this.isActive;
this.activeitem = item.name
}
in HTML add v-show="item.name == activeitem && isActive"
<dd v-show="item.name == activeitem && isActive" :id="item.name">
<button v-for="option in item.values" v-on:click="option.selected = !option.selected" type="button" class="btn btn-primary" :class="{'active' : option.selected}">
{{option.name}}
</button>
</dd>
var main = new Vue({
el: '#app',
data: {
activeitem:null,
products: [
{
name: "davy",
region: "Highland",
category: "Single Cask"
},
{
name: "bill",
region: "Islay",
category: "New releases"
},
{
name: "shena",
region: "Highland",
category: "Remarkable Regional Malt"
}
],
filterOptions: [
{
name: 'Region',
isActive: false,
values: [
{
name: 'Highland',
selected : false
},
{
name: 'Speyside',
selected : false
},
{
name: 'Islay',
selected : false
}
]
},
{
name: 'Price',
isActive: false,
values: [
{
name: '£1-50',
selected : false
},
{
name: '£51-100',
selected : false
},
]
},
{
name: 'Category',
isActive: false,
values: [
{
name: 'Single Cask',
selected : false
},
{
name: 'Remarkable Regional Malt',
selected : false
},
{
name: 'New releases',
selected : false
}
]
}
],
isHidden: true,
isActive: false
},
methods: {
toggle: function (item) {
this.isActive = !this.isActive;
this.activeitem = item.name
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app" class="container">
<form>
<div v-for="item in filterOptions" :class="{ activeclass: isActive }">
<dl>
<dt>
<!--- click to show --->
<button #click="toggle(item)" #click.self.prevent>{{item.name}}</button>
</dt>
<!--- show this --->
<dd v-show="item.name == activeitem && isActive" :id="item.name">
<button v-for="option in item.values" v-on:click="option.selected = !option.selected" type="button" class="btn btn-primary" :class="{'active' : option.selected}">
{{option.name}}
</button>
</dd>
</dl>
</div>
</form>
<div v-for="item in products">
<div>
<h5>
{{item.name}}
</h5>
<h6>{{item.region}}</h6>
<a>View</a>
</div>
</div>
</div>

Related

Deleting row list in Vue.js 3

I am trying to remove item from my list , i am using js delete operator but it can't , here is the code
HTML :
<ol v-if="status">
<li v-for="(item, key) in items" :key="item.title">
{{ item.title }} - Type - {{ item.type }}
<input v-model="item.type" type="text" value="{{ item.type }}" style="margin-left: 2%;" />
<button #click="delete items[key]">Remove Items</button>
</li>
</ol>
JS :
const sample = {
data() {
return {
newItem: [
{title: "", type: ""}
],
subject: 'Vue.js',
items: [
{ title: 'Javascript', type: 'Native'},
{ title: 'Vue', type: 'Framework'},
{ title: 'React', type: 'Framework'},
{ title: 'Angular', type: 'Framework'},
],
item: 'Javascript',
status: 1
}
},
}
Vue.createApp(sample).mount('#app')
you need to declare a proper method for your #click event:
<button #click="deleteItem(key)">Remove Items</button>
then, in your vue, methods section, declare this function:
methods:{
deleteItem(key){
this.items.splice(key, 1)
},
},
your complete vue code will be this :
const sample = {
data() {
return {
newItem: [
{title: "", type: ""}
],
subject: 'Vue.js',
items: [
{ title: 'Javascript', type: 'Native'},
{ title: 'Vue', type: 'Framework'},
{ title: 'React', type: 'Framework'},
{ title: 'Angular', type: 'Framework'},
],
item: 'Javascript',
status: 1
}
},
methods:{
deleteItem(key){
this.items.splice(key, 1)
},
},
}
Vue.createApp(sample).mount('#app')
Hi In your #onclick u dont use correctly ur function u most to do something like this :
<ol v-if="status">
<li v-for="(item, key) in items" :key="item.title">
{{ item.title }} - Type - {{ item.type }}
<input v-model="item.type" type="text" value="{{ item.type }}" style="margin-left: 2%;" />
<button #click="delete(item)">Remove Items</button>
</li>
</ol>
And here, delete is a function for delete ur item in items like this function
delete(item){
this.items.splice(item.title, 1)
},

How to sort a list by item valie in VueJs?

Is it possible in VueJs to display my blocks sorted by "block.position" value, and update all block position when I click on a "UP" or "DOWN" button?
I want to have my blocks in correct order in my "blocks" array, reflect the index position on the "block.position" value, and display all my blocks in a correct order.
<template>
<div>
<div class="mb-5" v-for="(block, index) in blocks" :key="block.id">
<div class="btn btn-primary" #click="move(index,index-1)" v-if="index">
<span class="fa fa-arrow-up"></span>
</div>
<div class="btn btn-primary"
#click="move(index,index+1)" v-if="index !== (blocks.length - 1)">
<span class="fa fa-arrow-down"></span>
</div>
<h3 class="text-uppercase text-info"><u>{{ block.name }}</u> - {{ block.position }}</h3>
<div v-for="field in block.fields" :key="field.id">
<p>{{ field.name }}</p>
<div class="mb-3">
<div v-if="field.type === 'textarea'">
<textarea
:required="field.required"
:placeholder="field.attr ? field.attr.placeholder : null"
:value="field.data"
/>
</div>
<div v-else>
<input
:type="field.type"
:required="field.required"
:placeholder="field.attr ? field.attr.placeholder : null"
:value="field.data"
/>
</div>
<div class="alert alert-danger" v-for="error in field.errors">
{{ error }}
</div>
</div>
</div>
<hr class="border-light">
</div>
<ul class="list-unstyled d-block">
<li class="d-inline-block">
<button class="btn btn-light" v-on:click="addParagraph">
Add Paragraph
</button>
</li>
<li class="d-inline-block ml-3">
<button class="btn btn-light" v-on:click="addImage">
Add Image
</button>
</li>
</ul>
</div>
</template>
<script>
// Going to use this neat array function
Array.prototype.move = function (from, to) {
this.splice(to, 0, this.splice(from, 1)[0]);
return this;
};
export default {
name: 'BlockForm',
created() {
// Tri dans le tableau les blocks par "block.position".
this.updateBlockPosition();
},
data() {
return {
blocks: [
{
id: 0,
type: 'paragraph',
name: 'Paragraphe',
position: '1',
fields: [
{
id: 0,
type: 'textarea',
name: 'Saisissez votre texte ci-dessous',
required: true,
data: null,
errors: [
'Lorem ipsum sir dolor emet',
],
},
],
},
{
id: 1,
type: 'image',
name: 'Image',
position: '2',
fields: [
{
id: 0,
type: 'file',
name: 'Sélectionner une image',
required: false,
placeholder: null,
data: null,
errors: [],
},
{
id: 1,
type: 'text',
name: 'Description de l\'image',
required: false,
placeholder: null,
data: null,
errors: [],
},
],
},
{
id: 2,
type: 'image',
name: 'Image',
position: '3',
fields: [
{
id: 0,
type: 'file',
name: 'Sélectionner une image',
required: false,
placeholder: null,
data: null,
errors: [],
},
{
id: 1,
type: 'text',
name: 'Description de l\'image',
required: false,
placeholder: null,
data: null,
errors: [],
},
],
},
],
};
},
methods: {
addParagraph() {
this.blocks.push({ ...this.blocks[0] });
this.updateBlockPosition();
},
addImage() {
this.blocks.push(this.blocks[1]);
this.updateBlockPosition();
},
move(from, to) {
this.blocks.move(from, to);
this.updateBlockPosition();
},
updateBlockPosition() {
this.blocks.forEach((block, index) => {
block.id = index + 1;
block.position = index + 1;
});
},
},
};
</script>

how to add edit button on vue-good-table in vue

I m new to Vue and stuck in a situation and don't know how to do that if anybody suggests me how I can do this let me show my code first
<div class="table-responsive-sm">
<vue-good-table
title="Shop List Table"
:columns="columns"
:rows="rows"
:paginate="true"
:lineNumbers="true"
:globalSearch="true" >
<template slot="table-row" slot-scope="props" ><a class="btn btn-sm primary" #on-row-click="onRowClick">save</a></template>
</vue-good-table>
and in script
data(){
return{
columns: [
{
label: 'Brand Name',
field: 'brand_name',
},
{
label: 'Brand Desc',
field: 'brand_desc',
},
{
label: 'Action',
field: 'before',
},
],
rows:[],
}
}
getTotals(){
var self = this;
var new1=[];
this.$http.get('/api/brands')
.then(function (response) {
self.rows=response.data
})
},
now my problem is that if I use
<span v-if="props.column.field == 'before'">
before
</span>
as suggested in this https://jsfiddle.net/aks9800/hsf0sqf8/ it throws an error like field not defined I just want to add an extra action button for edit this is vue-good table one more thing none of the action as suggested in this link for eg:- #on-row-click="onRowClick" not working
Try this
<div class="table-responsive-sm">
<vue-good-table
title="Shop List Table"
:columns="columns"
:rows="rows"
:paginate="true"
:lineNumbers="true"
:globalSearch="true" >
<template slot="table-row" slot-scope="props" >
<a class="btn btn-sm primary" #on-row-click="onRowClick">save</a>
</template>
<template slot="table-row" slot-scope="props">
<span v-if="props.column.field == 'actions'">
<a class="btn btn-sm primary" #on-row-click="onRowClick">save</a>
</span>
<span v-else>
{{props.formattedRow[props.column.field]}}
</span>
</template>
</vue-good-table>
</div>
data(){
return{
columns: [
{
label: 'Brand Name',
field: 'brand_name',
},
{
label: 'Brand Desc',
field: 'brand_desc',
},
{
label: 'Actions',
field: 'actions',
sortable: false,
}
],
rows:[],
}
}
getTotals(){
var self = this;
var new1=[];
this.$http.get('/api/brands')
.then(function (response) {
self.rows=response.data
})
},
Here is very good example how to add "edit" button in a row by marekfilip
https://jsfiddle.net/marekfilip/jm4ywzor/
html:
<div id="app">
<vue-good-table
:columns="columns"
:rows="rows" >
<template slot="table-row" slot-scope="props">
<span v-if="props.column.field == 'before'">
<button #click="editRow(props.row.id)">Edit</button>
<button #click="deleteRow(props.row.id)">Delete</button>
</span>
<span v-else>
{{props.formattedRow[props.column.field]}}
</span>
</template>
</vue-good-table>
<span>{{ text }}</span>
</div>
javascript
new Vue({
el: '#app',
data() {
return {
columns: [
{
label: 'Before',
field: 'before'
},
{
label: 'ID',
field: 'id',
sortable: true,
},
{
label: 'Text',
field: 'text',
type: 'number',
sortable: true,
},
],
rows: [
{ text: 'A', id: 1 },
{ text: 'B', id: 2 },
{ text: 'C', id: 3 },
{ text: 'D', id: 5 },
],
text: ''
};
},
methods: {
editRow(id) {
this.showAlert(id, 'EDIT')
},
deleteRow(id) {
this.showAlert(id, 'DELETE')
},
showAlert(id, type) {
this.text = `You clicked ${type} on row ID ${id}`
}
}
});

VueJS Custom dropdown buttons not working independently of each other

So I have a button dropdown which is working as expected but I have a bug where I can't reuse the same component as they both dont work independently of each other but instead when one is clicked the other changes too.
Please find a JSFiddle below and my code.
Thanks
<div id="app">
<div v-on:click="menuVisible = !menuVisible" class="dropdownBtn">
<div v-bind:class="{ active : menuVisible }"class="dropdownBtn__title">
<span v-if="!btnTitle">exploring</span>
<span v-else>{{ btnTitle }}</span>
</div>
<div v-show="menuVisible" class="dropdownBtn__content">
<ul v-for="reason in reasons">
<li v-on:click="updateTitle(reason)">{{ reason.title }}</li>
</ul>
</div>
</div>
<div v-on:click="menuVisible = !menuVisible" class="dropdownBtn">
<div v-bind:class="{ active : menuVisible }"class="dropdownBtn__title">
<span v-if="!btnTitle">exploring</span>
<span v-else>{{ btnTitle }}</span>
</div>
<div v-show="menuVisible" class="dropdownBtn__content">
<ul v-for="reason in reasons">
<li v-on:click="updateTitle(reason)">{{ reason.title }}</li>
</ul>
</div>
</div>
</div>
new Vue({
el: '#app',
data() {
return {
menuVisible: false,
btnTitle: false,
reasons: [{
title: 'family fun',
value: 1
},
{
title: 'relaxing',
value: 2
},
{
title: 'dining',
value: 3
},
{
title: 'meetings & events',
value: 4
},
{
title: 'what\'s on',
value: 5
},
{
title: 'gold',
value: 6
}]
}
},
methods: {
updateTitle($event) {
this.btnTitle = $event.title
}
}
})
So, for one, you haven't actually made a reusable component for the dropdown. You need to define that using Vue.component. Then you can use the tag for the custom component in the root template.
Secondly, if you are binding to the same data, then that is what will be reflected in both templates. You'll still need to pass separate data to the two separate dropdown components.
Vue.component('dropdown', {
template: `
<div v-on:click="menuVisible = !menuVisible" class="dropdownBtn">
<div v-bind:class="{ active : menuVisible }"class="dropdownBtn__title">
<span v-if="!btnTitle">exploring</span>
<span v-else>{{ btnTitle }}</span>
</div>
<div v-show="menuVisible" class="dropdownBtn__content">
<ul v-for="reason in reasons">
<li v-on:click="updateTitle(reason)">{{ reason.title }}</li>
</ul>
</div>
</div>
`,
props: ['menuVisible', 'btnTitle', 'reasons'],
methods: {
updateTitle($event) {
this.btnTitle = $event.title
}
}
})
new Vue({
el: '#app',
data() {
return {
fooReasons: [
{ title: 'family fun', value: 1 },
{ title: 'relaxing', value: 2 },
{ title: 'dining', value: 3 },
{ title: 'meetings & events', value: 4 },
{ title: 'what\'s on', value: 5 },
{ title: 'gold', value: 6 }
],
barReasons: [
{ title: 'family bar', value: 1 },
{ title: 'bar relaxing', value: 2 },
{ title: 'bar dining', value: 3 },
{ title: 'meetings & bars', value: 4 },
{ title: 'bar\'s on', value: 5 },
{ title: 'gold bar', value: 6 }
]
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.4.4/vue.min.js"></script>
<div id="app">
<dropdown :menu-visible="false" :btn-title="false" :reasons="fooReasons"></dropdown>
<dropdown :menu-visible="false" :btn-title="false" :reasons="barReasons"></dropdown>
</div>

Vue.js nested for loop input field model binding

In this example I am allowing the user to create their own typed list of sections. Each type has it's own form fields. The form fields render properly however, if I enter data into one of the fields after I've created two duplicate sections, both inputs are updated with the typed in text. This is not the intended result.
Instead each section should update its form field data content individually and it should reflect back to the value stored within the data.section related to it.
What am I missing?
Laravel View
{{Form::open(['route' => 'api.post.store', 'class' => 'form-horizontal'])}}
<fieldset>
<div id="legend">
<legend class="">Register</legend>
</div>
<div :key="section.id" v-for="(index,section) in sections" class="control-group form-group-lg">
<div class="form-header">
<h3>#{{ section.label }}</h3>
</div>
<pre>#{{ section | json }}</pre>
<div v-for="field in section.fields" :key="field.id">
<div class="text-field" v-show="field.inputType == 'text'">
<label class="control-label" :for="section.name">#{{ field.label }}</label>
<div class="controls">
<input v-model="field.data.content" class="input-xlarge form-control">
<p class="help-block">#{{ field.helpText }}</p>
</div>
</div>
<div class="text-area-field" v-show="field.inputType == 'text-area'">
<label class="control-label" :for="section.name">#{{ field.label }}</label>
<div class="controls">
<textarea :v-bind="field.data.content" class="input xlarge form-control" :placeholder="field.placeholder">
#{{ field.data.content }}
</textarea>
</div>
</div>
<div class="text-area-field" v-show="field.inputType == 'data-map'">
<label class="control-label" :for="section.name">#{{ field.label }}</label>
<div class="controls">
<textarea :v-bind="field.data.content" class="input xlarge form-control" :placeholder="field.placeholder">
#{{ field.data.content }}
</textarea>
</div>
</div>
</div>
</div>
<div class="control-group">
<div class="controls">
<div class="dropdown">
<a data-target="#" href="page.html" data-toggle="dropdown" class="dropdown-toggle">Dropdown <b class="caret"></b></a>
<ul class="dropdown-menu">
<li v-for="sectionType in sectionTypes">
<a #click="setSectionCreateType(sectionType)" href="#">#{{ sectionType.label }}</a>
</li>
</ul>
</div>
</div>
<div class="controls">
<div #click="addSection()" class="btn bdn-success" class="btn btn-success">Add Section</div>
<div #click="savePost()" class="btn bdn-success" class="btn btn-success">Save</div>
</div>
</div>
</fieldset>
{{Form::close()}}
Vuefile
<script type="text/javascript">
import Vue from 'vue';
import FormField from './create/FormField.vue';
export default {
components: {
FormField,
},
ready: function () {
},
filters: {},
data(){
return {
messages: [],
sections: [],
saveSections: [],
sectionCreateType: false,
sectionTypes: [
{
label: 'Company',
map: 'company',
fields: [
{
label: 'name',
name: 'name',
inputType: 'text',
placeholder: 'Company Name',
data: {
content: '',
},
},
{
label: 'symbol',
name: 'symbol',
inputType: 'text',
placeholder: 'stock symbol',
data: {
content: '',
},
}
]
},
{
label: 'Link',
map: 'link',
inputType: 'text',
data: {},
fields: [
{
label: 'url',
name: 'url',
inputType: 'text',
placeholder: 'Url',
data: {
content: '',
},
},
]
},
{
label: 'Paragraph',
map: 'paragraph',
data: {},
fields: [
{
label: 'content',
name: 'content',
inputType: 'text-area',
placeholder: 'Content',
data: {
content: '',
},
},
]
},
{
label: 'Person',
map: 'person',
data: {},
inputType: 'data-map',
'fields': [
{
label: 'first_name',
name: 'name',
placeholder: 'Person Name',
data: {
content: '',
},
},
{
label: 'last_name',
name: 'name',
placeholder: 'Person Name',
data: {
content: '',
},
}
]
},
],
}
},
directives: {},
events: {},
methods: {
setSectionCreateType(type)
{
console.log('setting sectionCreateType: ' + type.label)
this.sectionCreateType = type;
},
addSection()
{
if (!this.sectionCreateType) {
this.sectionCreateType = this.sectionTypes[0];
}
this.createSection(this.sectionCreateType);
},
createSection(type)
{
this.sections.push(Vue.util.extend({}, type))
},
previewPost(){
},
savePost: function(){
var view = this;
var saveObject = [];
var sectionObject = [];
this.sections.forEach(function (section) {
if(!sectionObject[section.type.map])
{
sectionObject[section.type.map] = [];
}
for (var key in section.type.fields) {
var field = section.type.fields[key];
var saveKey = [];
saveKey[field.name] = field.data.content;
}
sectionObject[section.type.map].push(saveKey);
});
saveObject.push(sectionObject);
console.log(saveObject);
},
}
}
</script>
You are using the same v-model so VueJS does what it should do.
You have to create e.g. list of models and somehow handle index (e.g. take it from v-for for every section/subsection and use v-model='list[index].field