I am very new to VueJS. I am trying to populate a table. One of the columns is a button on clicking which a modal opens. There can be 3 different types of modals. I tried solving the problem like this :
I have linked my jsfiddle after this reference code.
HTML
<div id="app">
<!-- Main table element -->
<b-table striped hover :items="items" :fields="fields" >
<template slot="name" slot-scope="item">
{{item.value.first}} {{item.value.last}}
</template>
<template slot="isActive" slot-scope="item">
{{item.value?'Yes :)':'No :('}}
</template>
<template slot="actions" slot-scope="item">
<div v-if="item.item.age < 15">
<b-btn v-b-modal.modal1 size="sm" #click="details(item.item)">Details</b-btn>
</div>
<div v-else-if="item.item.age > 26">
<b-btn v-b-modal.modal2 size="sm" #click="details(item.item)">Details</b-btn>
</div>
<div v-else>
<b-btn v-b-modal.modal3 size="sm" #click="details(item.item)">Details</b-btn>
</div>
</template>
</b-table>
<b-modal id="modal1">
modal 1
</b-modal>
<b-modal id="modal2">
modal 2
</b-modal>
<b-modal id="modal3">
modal3
</b-modal>
</div>
VueJS
new Vue({
el: '#app',
data: {
items: [{
isActive: true,
age: 40,
name: {
first: 'Dickerson',
last: 'Macdonald'
}
}, {
isActive: false,
age: 21,
name: {
first: 'Larsen',
last: 'Shaw'
}
}, {
isActive: false,
age: 9,
state: 'success',
name: {
first: 'Mitzi',
last: 'Navarro'
}
}, {
isActive: false,
age: 89,
name: {
first: 'Geneva',
last: 'Wilson'
}
}, {
isActive: true,
age: 38,
name: {
first: 'Jami',
last: 'Carney'
}
}, {
isActive: false,
age: 27,
name: {
first: 'Essie',
last: 'Dunlap'
}
}, {
isActive: true,
age: 40,
name: {
first: 'Dickerson',
last: 'Macdonald'
}
}, {
isActive: false,
age: 21,
name: {
first: 'Larsen',
last: 'Shaw'
}
}, {
isActive: false,
age: 26,
name: {
first: 'Mitzi',
last: 'Navarro'
}
}, {
isActive: false,
age: 22,
name: {
first: 'Geneva',
last: 'Wilson'
}
}, {
isActive: true,
age: 38,
name: {
first: 'Jami',
last: 'Carney'
}
}, {
isActive: false,
age: 27,
name: {
first: 'Essie',
last: 'Dunlap'
}
}],
fields: {
name: {
label: 'Person Full name',
sortable: true
},
age: {
label: 'Person age',
sortable: true
},
isActive: {
label: 'is Active'
},
actions: {
label: 'Actions'
}
}
},
methods: {
details(item) {
//gets some data. Write 3 different functions to fetch data accordingly
}
}
})
Style:
#app {
padding: 20px;
height: 500px;
}
Update:
I was able to replicate the bug described below here after I added pagination.
my fiddle: fiddle
To replicate, go to a different page and try opening all of the modals. Some will not open.
Observation:
I see that I am able to open only one modal per page after I change to a different page from the initial page.
Is there any better way of doing this? Or am I missing something trivial?
Bug descriptoin:
I am running into a strange behavior in my code where most of the rows(seems random to me) don't open a pop-up when the button is clicked.This happens even for the same modal type
Eg : Suppose P,Q entries of the column are of type modal1. Clicking on P opens a modal and clicking on Q does not.
I know this bug seems vague but I will be very glad if some one could point me in the right direction. Thanks.
Can't explain why this happens (maybe is a bug of bootstrap-vue). Anyway, if you replace:
<b-btn v-b-modal.modal1 size="sm" #click="details(item.item)">Details</b-btn>
<b-btn v-b-modal.modal2 size="sm" #click="details(item.item)">Details</b-btn>
<b-btn v-b-modal.modal3 size="sm" #click="details(item.item)">Details</b-btn>
with:
<b-btn v-b-modal="'modal1'" size="sm" #click="details(item.item)">Details</b-btn>
<b-btn v-b-modal="'modal2'" size="sm" #click="details(item.item)">Details</b-btn>
<b-btn v-b-modal="'modal3'" size="sm" #click="details(item.item)">Details</b-btn>
it seems to work fine.
Hope this helps you.
Related
I use bootstrapvue compoentp and i want to change hover color tored.
I use my stytle ,it doesn't work.
how ca i do ?
my code:
<div>
<b-table class="table-sm table-hover" fixed bordered striped ></b-table>
</div>
<style>
.table-hover tbody tr:hover td,
.table-hover tbody tr:hover th {
background-color: red !important;
</style>
}
You can check the example of Using variants for table cells on official doc
<template>
<div>
<b-table hover :items="items"></b-table>
</div>
</template>
<script>
export default {
data() {
return {
items: [
{ age: 40, first_name: 'Dickerson', last_name: 'Macdonald' },
{ age: 21, first_name: 'Larsen', last_name: 'Shaw' },
{
age: 89,
first_name: 'Geneva',
last_name: 'Wilson',
_rowVariant: 'danger'
},
{
age: 40,
first_name: 'Thor',
last_name: 'MacDonald',
_cellVariants: { age: 'info', first_name: 'warning' }
},
{ age: 29, first_name: 'Dick', last_name: 'Dunlap' }
]
}
}
}
</script>
I'm new to vue, I still don't understand everything, tell me. I have buttons that I display through v-for, I need to get the active class of only one button when pressed, all the others need to be turned off, tell me, preferably visually, how can I do it better?
I am using the method activeBtn, but this doesn't turn off the active class from the previous buttons
activeBtn(event, index) {
this.buttons[index].isActive = !this.buttons[index].isActive;
<script>
data() {
return {
buttons: [
{
label: "A",
isActive: false,
type: "border-left",
name: "BorderLeftComonent",
},
{
label: "A",
isActive: false,
type: "text-balloon",
name: "TextBalloonComponent"
},
{
label: "A",
isActive: false,
type: "dashed",
name: "DashedComponent"
},
],
};
},
methods: {
activeBtn(event, index) {
this.buttons[index].isActive = !this.buttons[index].isActive;
}
</script>
<template>
<div id="btn-box">
<button
v-for="(button, index) in buttons"
:key="index"
:class="button.isActive ? 'on' : 'off'"
#click="component = button.name, activeBtn($event, index)">
<div :class="`btn btn-${button.type}`">{{ button.label }}</div>
</button>
</div>
</template>
Since you only want to get one active button at any one point, it doesn't make sense to manage the active state inside each button.
Instead, you should manage it at group level by storing the currently selected button's id. A button would then be active when its id matches the currently selected id.
Here's an example:
new Vue({
el: '#app',
data: () => ({
buttons: [
{
id: "button-1",
label: "A",
type: "border-left",
name: "BorderLeftComonent"
},
{
id: "button-2",
label: "A",
type: "text-balloon",
name: "TextBalloonComponent"
},
{
id: "button-3",
label: "A",
type: "dashed",
name: "DashedComponent"
}
],
activeButtonId: "button-1"
}),
methods: {
activate(id) {
this.activeButtonId = id;
}
}
})
.on {
background-color: red
}
.off {
background-color: blue
}
.on, .off {
color: white
}
<script src="https://unpkg.com/vue#2/dist/vue.min.js"></script>
<div id="app">
<div>
<button
v-for="{id, type, label} in buttons"
:key="id"
:class="activeButtonId === id ? 'on' : 'off'"
#click="activate(id)"
>
<div :class="`btn btn-${type}`" v-text="label" />
</button>
</div>
</div>
Following Bootstrap Vue documentation, I've following code:
<template>
<div>
<b-table :items="items" :fields="fields" striped responsive="sm">
<template #cell(show_details)="row">
<b-button size="sm" #click="row.toggleDetails" class="mr-2">
{{ row.detailsShowing ? 'Hide' : 'Show'}} Details
</b-button>
<!-- As `row.showDetails` is one-way, we call the toggleDetails function on #change -->
<b-form-checkbox v-model="row.detailsShowing" #change="row.toggleDetails">
Details via check
</b-form-checkbox>
</template>
<template #row-details="row">
<b-card>
<b-row class="mb-2">
<b-col sm="3" class="text-sm-right"><b>Age:</b></b-col>
<b-col>{{ row.item.age }}</b-col>
</b-row>
<b-row class="mb-2">
<b-col sm="3" class="text-sm-right"><b>Is Active:</b></b-col>
<b-col>{{ row.item.isActive }}</b-col>
</b-row>
<b-button size="sm" #click="row.toggleDetails">Hide Details</b-button>
</b-card>
</template>
</b-table>
</div>
</template>
<script>
export default {
data() {
return {
fields: ['first_name', 'last_name', 'show_details'],
items: [
{ isActive: true, age: 40, first_name: 'Dickerson', last_name: 'Macdonald' },
{ isActive: false, age: 21, first_name: 'Larsen', last_name: 'Shaw' },
{
isActive: false,
age: 89,
first_name: 'Geneva',
last_name: 'Wilson',
_showDetails: true
},
{ isActive: true, age: 38, first_name: 'Jami', last_name: 'Carney' }
]
}
}
}
</script>
Full example could be found here.
According to this example, if I click Show Details button on each of the row it will show its respective row detail, but I want it to close all previous rows upon clicking it and only open details of the currently clicked row.
I know that looping through rows and setting detailsShowing to false can solve it like below:
this.rownames.forEach(item => {
this.$set(item, 'detailsShowing', false)
})
but I don't know how to do it. Thank You.
I feel the accepted answer is a bit over engineered, so I want to suggest what I think is a simpler solution.
This works by saving the "currently" open row in a variable, and setting _showDetails on it to false if another row is opened. This way we avoid having to do any sort of loops.
new Vue({
el: "#app",
data() {
return {
detailsRow: null,
items: [
{ age: 40, first_name: "Dickerson", last_name: "Macdonald" },
{ age: 21, first_name: "Larsen", last_name: "Shaw" },
{ age: 89, first_name: "Geneva", last_name: "Wilson" },
{ age: 38, first_name: "Jami", last_name: "Carney" }
]
};
},
methods: {
onRowClicked(item) {
const { detailsRow } = this
if (detailsRow && detailsRow !== item) {
detailsRow._showDetails = false;
}
this.$set(item, "_showDetails", !item._showDetails);
this.detailsRow = item;
}
}
});
<link type="text/css" rel="stylesheet" href="https://unpkg.com/bootstrap/dist/css/bootstrap.min.css" />
<link type="text/css" rel="stylesheet" href="https://unpkg.com/bootstrap-vue#2.21.2/dist/bootstrap-vue.min.css" />
<script src="https://unpkg.com/vue#2.6.2/dist/vue.min.js"></script>
<script src="https://unpkg.com/bootstrap-vue#2.21.2/dist/bootstrap-vue.min.js"></script>
<div id="app">
<b-table :items="items" #row-clicked="onRowClicked">
<template #row-details="{ item }">
<pre>{{ item }}</pre>
</template>
</b-table>
</div>
Add a new data property called visibleRow and set it to null.
Then create a computed getter that returns your items but has the _showDetails property set. The value should be false by default unless visibleRow matches the item:
computed: {
tableItems () {
return this.items.map((item) => {
return Object.assign({}, item, {
_showDetails: this.visibleRow && this.visibleRow.first_name === item.first_name && this.visibleRow.last_name === item.last_name
})
})
}
}
I've taken some liberties in determining what makes your rows unique but you're free to use whatever you want in the above comparison
Create a new method and modify your row click function to set the visibleRow on toggle:
methods: {
setVisibleRow (row) {
this.$set(this, 'visibleRow', row)
}
}
Update your click method:
#click="setVisileRow(row.item._showDetails ? null : row.item)"
Finally, update your items binding on b-table:
<b-table :items="tableItems"
I have a dataset that looks like this:
[
{id: 1, name: 'Foo', is_primary: false},
{id: 2, name: 'Bar', is_primary: true},
{id: 3, name: 'Baz', is_primary: false},
]
Only one of the entries is allowed to have is_primary = true. I'm displaying these items in a list, and I'm trying to display a radio button for each that the user can select to indicate that this is the primary one.
<tr v-for="item in items">
<td><input name="primary" type="radio" v-model="item.is_primary"></td>
</tr>
However, I don't think I'm understanding how this is supposed to work, because it's not working for me. Is this possible or am I supposed to handle this situation another way?
A set of radio inputs should v-model the same variable: a scalar that takes on the value associated with the selected radio.
To translate that back and forth into your item list, you can use a settable computed.
new Vue({
el: '#app',
data: {
items: [{
id: 1,
name: 'Foo',
is_primary: false
},
{
id: 2,
name: 'Bar',
is_primary: true
},
{
id: 3,
name: 'Baz',
is_primary: false
},
]
},
computed: {
primaryItem: {
get() {
return this.items.find((i) => i.is_primary);
},
set(pi) {
this.items.forEach((i) => i.is_primary = i === pi);
}
}
}
});
<script src="https://unpkg.com/vue#latest/dist/vue.js"></script>
<div id="app">
<div v-for="item in items">
<input name="primary" type="radio" :value="item" v-model="primaryItem">
</div>
<pre>{{JSON.stringify(items, null, 2)}}</pre>
</div>
Suppose I want to display a List of Questions. For each question, there is a list of answers, none of which are right or wrong. For each question, the user can choose an answer. I'm wondering how to create two-way binding on the selected answer.
The Vue:
new Vue(
{
el: "#app",
data:
{
questions: [{}]
}
}
Example Question Model:
{
id: 1,
name: "Which color is your favorite?",
selectedAnswerId: null,
selectedAnswerName: null,
answers:
[
{id: 1, name: red, photoUrl: ".../red", selected: false},
{id: 2, name: green, photoUrl: ".../green", selected: false},
{id: 3, name: blue, photoUrl: ".../blue", selected: false},
]
}
Components:
var myAnswer =
{
props: ["id", "name", "url", "selected"],
template:
`
<div class="answer" v-bind:class="{selected: selected}">
<img class="answer-photo" v-bind:src="url">
<div class="answer-name">{{name}}</div>
</div>
`
};
Vue.component("my-question",
{
props: ["id", "name", "answers"],
components:
{
"my-answer": myAnswer
},
template:
`
<div class ="question">
<div class="question-name">{{name}}</div>
<div class="question-answers">
<my-answer v-for="answer in answers" v-bind:id="answer.id" v-bind:name="answer.name" v-bind:url="answer.photoUrl" v-bind:selected="answer.selected"></my-answer>
</div>
</div>
`
});
When the user selects an answer to a question by clicking on the div, I want the Question model's selectedAnswerId/selectedAnswerName along with the answers selected property to be set accordingly. Therefore, what do I need to add to my components in order to accomplish this two-way binding? I believe it requires input elements and v-model, but I couldn't quite figure it out. Also, I am only one day into Vue.js and have no experience with related frameworks. So if I am doing anything blatantly wrong or against best practice, that would be good to know as well. Thanks in advance!
The answer will handle a click event and emit a (custom) selected-answer event. The question will have its own data item to store the selected answer ID; the answer component's selected prop will be based on that. The question will handle the selected-answer event by setting its selectedId.
var myAnswer = {
props: ["id", "name", "url", "selected"],
template: `
<div class="answer" v-bind:class="{selected: selected}"
#click="setSelection()"
>
<img class="answer-photo" :src="url">
<div class="answer-name">{{name}}</div>
</div>
`,
methods: {
setSelection() {
this.$emit('selected-answer', this.id);
}
}
};
Vue.component("my-question", {
props: ["id", "name", "answers"],
data() {
return {
selectedId: null
};
},
components: {
"my-answer": myAnswer
},
template: `
<div class ="question">
<div class="question-name">{{name}}</div>
<div class="question-answers">
<my-answer v-for="answer in answers"
:id="answer.id" :name="answer.name" :url="answer.photoUrl"
:selected="answer.id === selectedId" #selected-answer="selectAnswer"></my-answer>
</div>
</div>
`,
methods: {
selectAnswer(answerId) {
this.selectedId = answerId;
}
}
});
new Vue({
el: '#app',
data: {
questions: [{
id: 1,
name: "Which color is your favorite?",
answers: [{
id: 1,
name: 'red',
photoUrl: ".../red"
},
{
id: 2,
name: 'green',
photoUrl: ".../green"
},
{
id: 3,
name: 'blue',
photoUrl: ".../blue"
},
]
}]
}
});
.answer {
cursor: pointer;
}
.selected {
background-color: #f0f0f0;
}
<script src="//cdnjs.cloudflare.com/ajax/libs/vue/2.2.6/vue.min.js"></script>
<div id="app">
<my-question v-for="q in questions" :name="q.name" :answers="q.answers"></my-question>
</div>