Handle limit data with 2 buttons (Next and previous button ) in vuejs - javascript

In my work, the concept look like have lot of data in array , but the first one i want to show only 4 object (id 1 to id 4) in array. So , next i press next button that i want to loop the next object in array (id 5 to id 8) , then i press the back button they return to previous object that i pass them . i don't know the logic for this.
<template>
<v-container fluid>
<v-row no-gutters>
<v-col cols="12" md="12" sm="12">
<v-card>
<v-row no-gutters>
<v-col cols="12" md="12" sm="12" class="">
<v-row no-gutters class="d-flex">
<v-col
cols="6"
md="6"
v-for="(item, index) in content"
:key="`item-${index}`"
>
<v-col cols="6">
<v-card-text>
<p>{{ item.id }}</p>
<p>my name is : {{ item.title }}</p>
</v-card-text></v-col
>
</v-col>
</v-row>
</v-col>
</v-row>
<v-col cols="12" md="12" sm="12" class="d-flex">
<v-col cols="6" md="6" sm="6">
<v-btn medium elevation="" color="primary" #click="backpage()"
>Back</v-btn
>
</v-col>
<v-col cols="6" md="6" sm="6">
<v-btn medium elevation="" color="primary" #click="nextPage()"
>Next</v-btn
>
</v-col>
</v-col>
</v-card>
</v-col>
</v-row>
</v-container>
</template>
<script>
export default {
data: () => ({
showCounted: 4,
content: '',
nextValue: Number,
user: [
{
id: 1,
title: 'Saysana',
},
{
id: 2,
title: 'Binh',
},
{
id: 3,
title: 'Say',
},
{
id: 4,
title: 'Q',
},
{
id: 5,
title: 'a',
},
{
id: 6,
title: 'b',
},
{
id: 7,
title: 'c',
},
{
id: 8,
title: 'e',
},
{
id: 9,
title: 'e',
},
{
id: 10,
title: 'e',
},
{
id: 11,
title: 'e',
},
{
id: 12,
title: 'e',
},
],
}),
computed: {
defaultPage() {
return (this.content = this.user.slice(0, this.showCounted))
},
},
methods: {
async nextPage() {
this.content = this.user.slice(this.showCounted, this.showCounted + 4)
console.log(this.content)
this.showCounted = this.showCounted + 4
},
defaultData() {
this.content = this.user.slice(0, this.showCounted)
},
async backpage() {
const previous = -this.showCounted - 4
let to = 4
this.content = this.user.slice(previous, to)
},
},
mounted() {
this.defaultData()
},
}
</script>

Try like following snippet:
Add data property currentPage: 1, then in next/back page increment/decrement current page and slice(this.showCounted * this.currentPage, this.showCounted * this.currentPage + this.showCounted) / slice(this.showCounted * this.currentPage - this.showCounted, this.showCounted * this.currentPage). Also you can disable/enable buttons if you are on last/first page. This way you can chose whatever number of items you want to show just set showCounted to desired value.
Vue.config.productionTip = false
Vue.config.devtools = false
new Vue({
el: '#demo',
vuetify: new Vuetify(),
data(){
return {
showCounted: 4,
currentPage: 1,
content: '',
nextValue: Number,
user: [
{id: 1, title: 'Saysana'}, {id: 2,title: 'Binh'}, {id: 3, title: 'Say'},
{id: 4, title: 'Q'}, {id: 5, title: 'a'}, {id: 6, title: 'b'},
{id: 7, title: 'c'}, {id: 8, title: 'e'}, {id: 9, title: 'e'},
{id: 10, title: 'e'}, {id: 11, title: 'e'},{id: 12, title: 'e'},
],
isDisabledN: false,
isDisabledB: false
}
},
methods: {
defaultData() {
this.content = this.user.slice(0, this.showCounted)
},
nextPage() {
this.content = this.user.slice(this.showCounted * this.currentPage, this.showCounted * this.currentPage + this.showCounted)
this.currentPage++
this.checkPage()
},
backpage() {
this.currentPage--
this.content = this.user.slice(this.showCounted * this.currentPage - this.showCounted, this.showCounted * this.currentPage)
this.checkPage()
},
checkPage() {
if (this.showCounted * this.currentPage >= this.user.length) {
this.isDisabledN = true
this.isDisabledB = false
} else if(this.currentPage <= 1) {
this.isDisabledN = false
this.isDisabledB = true
} else {
this.isDisabledN = false
this.isDisabledB = false
}
}
},
mounted() {
this.defaultData()
this.checkPage()
},
})
<link href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/#mdi/font#4.x/css/materialdesignicons.min.css" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/vuetify#2.x/dist/vuetify.min.css" rel="stylesheet">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no, minimal-ui">
<script src="https://cdn.jsdelivr.net/npm/vue#2.x/dist/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vuetify#2.x/dist/vuetify.js"></script>
<div id="demo">
<v-app>
<v-main>
<v-container fluid>
<v-row no-gutters>
<v-col cols="12" md="12" sm="12">
<v-card>
<v-row no-gutters>
<v-col cols="12" md="12" sm="12" class="">
<v-row no-gutters class="d-flex">
<v-col
cols="6"
md="6"
v-for="(item, index) in content"
:key="`item-${index}`"
>
<v-col cols="6">
<v-card-text>
<p>{{ item.id }}</p>
<p>my name is : {{ item.title }}</p>
</v-card-text></v-col
>
</v-col>
</v-row>
</v-col>
</v-row>
<v-col cols="12" md="12" sm="12" class="d-flex">
<v-col cols="6" md="6" sm="6">
<v-btn :disabled="isDisabledB" ref="back" medium elevation="" color="primary" #click="backpage()"
>Back</v-btn
>
</v-col>
<v-col cols="6" md="6" sm="6">
<v-btn :disabled="isDisabledN" ref="next" medium elevation="" color="secondary" #click="nextPage()"
>Next</v-btn
>
</v-col>
</v-col>
</v-card>
</v-col>
</v-row>
</v-container>
</v-main>
</v-app>
</div>

Related

How can I remove other values ​based on the value I selected?

When using Vuetify v-autocomplete component and using the prop multiple we can multi select values.
How can I remove other values ​​based on the value I selected?
For example:
When I select the main value, the others will be selected removed.Then, when I select the first value, the main value and other values ​​will be deselected. Then when I select the third and others (the third and below are in the same group) the selected main and first value will be selected removed.
<div id="app">
<v-app>
<v-main>
<v-container>
<template>
<v-card color="blue-grey darken-1" dark>
<v-form>
<v-container>
<v-row>
<v-col cols="5">
<v-autocomplete
v-model="parametres"
:items="people"
filled
chips
color="blue-grey lighten-2"
label="Liste"
item-text="name"
item-value="name"
multiple
>
<template v-slot:selection="data">
<v-chip
v-bind="data.attrs"
:input-value="data.selected"
close
#click="data.select"
#click:close="remove(data.item)"
>
{{ data.item.name }}
</v-chip>
</template>
<template v-slot:item="data">
<template v-if="typeof data.item !== 'object'">
<v-list-group>
<v-list-item-content
v-text="data.item"
></v-list-item-content>
</template>
<template v-else>
<v-list-item-content>
<v-list-item-title
v-html="data.item.name"
></v-list-item-title>
</v-list-item-content>
</v-list-group>
</template>
</template>
</v-autocomplete>
</v-col>
</v-row>
</v-container>
</v-form>
<v-divider></v-divider>
</v-card>
</template>
</v-container>
</v-main>
</v-app>
</div>
<script>
new Vue({
el: "#app",
vuetify: new Vuetify(),
data() {
return {
selected: ["name"],
autoUpdate: true,
parametres: ["Main Select"],
people: [
{ name: "Main Select"},
{ name: "First Select"},
{ name: "Second Select"},
{ name: "Third Select."},
{ name: "Fourth Select."},
{ name: "Fifth Select."},
],
model: 1,
};
},
methods: {
remove(item) {
const index = this.parametreler.indexOf(item.name);/*Chip Remove */
if (index >= 0) this.parametreler.splice(index, 1); /*Chip Remove */
},
},
});
</script>
Hi #sercan you can use the #change events and call method on this event and write your logic within that method.
<div id="app">
<v-app>
<v-main>
<v-container>
<template>
<v-card color="blue-grey darken-1" dark>
<v-form>
<v-container>
<v-row>
<v-col cols="5">
<v-autocomplete v-model="parametres" :items="people" filled chips color="blue-grey lighten-2" label="Liste" item-text="name" item-value="name" multiple :key="index">
<template v-slot:selection="data">
<v-chip v-bind="data.attrs" :input-value="data.selected" close #click="data.select" #click:close="remove(data.item)">
{{ data.item.name }}
</v-chip>
</template>
<template v-slot:item="data">
<template v-if="typeof data.item !== 'object'">
<v-list-group>
<v-list-item-content v-text="data.item">
<span>{{typeof data.item !== 'object'}}</span>
</v-list-item-content>
</template>
<template v-else>
<v-list-item-content #click="updateSelection(data.item.name)">
<v-list-item-title v-html="data.item.name"></v-list-item-title>
</v-list-item-content>
</v-list-group>
</template>
</template>
</v-autocomplete>
</v-col>
</v-row>
</v-container>
</v-form>
<v-divider></v-divider>
</v-card>
</template>
</v-container>
</v-main>
new Vue({
el: "#app",
vuetify: new Vuetify(),
data() {
return {
selected: ["name"],
autoUpdate: true,
parametres: ["Main Select"],
people: [
{ name: "Main Select" },
{ name: "First Select" },
{ name: "Second Select" },
{ name: "Third Select." },
{ name: "Fourth Select." },
{ name: "Fifth Select." }
],
model: 1,
index: 0
};
},
methods: {
updateSelection(name) {
let toRemove = ["Main Select","First Select"];
let temp = event;
console.log(name);
switch (name) {
case "Main Select":
this.parametres = ["Main Select"];
this.index++;
break;
case "First Select":
this.parametres = ["First Select"]
this.index++;
break;
default:
this.parametres = this.parametres.filter( ( el ) => !toRemove.includes( el ) );
}
},
remove(item) {
const index = this.parametreler.indexOf(item.name); /*Chip Remove */
if (index >= 0) this.parametreler.splice(index, 1); /*Chip Remove */
}
}
});

How can I pre-populate my input with data from API?

I have an edit form, and I'm trying to populate my input base on the response from API. My campaign data look like this.
{
"id": 219,
"name": "finishedddd-1642606412049"
}
Testing
You see that I can access campaign.name like this
<p>Campaign Name : {{ campaign.name }}</p>
Trying
I want to pre-populated my name input, so I did this
data() {
return {
campaign: {},
form: {
errors: {},
values: {
name: this.campaign.name,
...
Result
Somehow that kept getting me :
"TypeError: Cannot read properties of undefined (reading 'name')"
Code
<template>
<v-container fluid class="my-1">
<Navbar />
<Breadcrumbs />
<v-row>
<v-col cols="12">
<v-card elevation="2">
<PanelHeader type="create" icon="campaign" title="Campaigns" />
<v-stepper v-model="e1" justify="center" elevation="0">
<v-stepper-header>
<v-stepper-step :complete="e1 > 1" step="1"> Campaign </v-stepper-step>
<v-divider></v-divider>
<v-stepper-step :complete="e1 > 2" step="2"> Setup </v-stepper-step>
<v-divider></v-divider>
<v-stepper-step step="3"> Finish </v-stepper-step>
</v-stepper-header>
<v-stepper-items>
<v-stepper-content step="1">
<v-card class="mb-12" elevation="0">
<v-form ref="form" lazy-validation v-model="valid" id="form">
<v-row>
<v-card-text class="font-weight-bold">
Campaigns
<p>Campaign Name : {{ campaign.name }}</p>
<v-tooltip right>
<template v-slot:activator="{ on, attrs }">
<v-btn icon v-bind="attrs" v-on="on">
<v-icon color="grey lighten-1">info</v-icon>
</v-btn>
</template>
<span>Select marketing campaign type for Print Materials or Product Tags. Provide a name to identify the marketing campaign.</span>
</v-tooltip>
</v-card-text>
</v-row>
<v-row>
<v-col class="col-sm-2 col-lg-2 col-12">
<v-select disabled dense outlined :items="types" label="Type" v-model="form.values.type" :rules="form.rules.type"></v-select>
</v-col>
<v-col cols="12" sm="6" md="2">
<v-text-field dense outlined v-model="form.values.name" :rules="form.rules.name" label="Name" required></v-text-field>
</v-col>
</v-row>
<v-row>
<v-col cols="12" sm="6" md="4">
<v-textarea dense rows="1" outlined v-model="form.values.description" label="Description" required></v-textarea>
</v-col>
</v-row>
<v-row>
<v-card-text class="font-weight-bold"
>Schedule :
<v-tooltip right>
<template v-slot:activator="{ on, attrs }">
<v-btn icon v-bind="attrs" v-on="on">
<v-icon color="grey lighten-1">info</v-icon>
</v-btn>
</template>
<span>Set the time zone, start and end date for this campaign to be active.</span>
</v-tooltip>
</v-card-text>
<v-col cols="12" sm="6" md="4">
<v-select dense outlined :items="timezones" v-model="form.values.timezone" :rules="form.rules.timezone" label="Timezone" append-icon="lock_clock"></v-select>
</v-col>
</v-row>
<v-row>
<v-col cols="12" sm="6" md="2">
<v-menu v-model="form.values.startDateMenu" :close-on-content-click="false" :nudge-right="40" transition="scale-transition" offset-y min-width="auto">
<template v-slot:activator="{ on, attrs }">
<v-text-field dense outlined v-model="form.values.startDate" :rules="form.rules.startDate" label="Start Date" append-icon="mdi-calendar" readonly v-bind="attrs" v-on="on"></v-text-field>
</template>
<v-date-picker v-model="form.values.startDate"></v-date-picker>
</v-menu>
</v-col>
<v-col cols="12" sm="6" md="2">
<v-menu ref="menu" v-model="startTimeMenu" :close-on-content-click="false" :nudge-right="40" :return-value.sync="form.values.startTime" transition="scale-transition" offset-y max-width="290px" min-width="290px">
<template v-slot:activator="{ on, attrs }">
<v-text-field dense v-model="form.values.startTime" label="Start Time" append-icon="mdi-clock-time-four-outline" readonly v-bind="attrs" v-on="on" outlined></v-text-field>
</template>
<v-time-picker v-if="startTimeMenu" v-model="form.values.startTime" full-width #click:minute="$refs.menu.save(form.values.startTime)"></v-time-picker>
</v-menu>
</v-col>
</v-row>
<v-row>
<v-col cols="12" sm="6" md="2">
<v-menu v-model="endDateMenu" :close-on-content-click="false" :nudge-right="40" transition="scale-transition" offset-y min-width="auto">
<template v-slot:activator="{ on, attrs }">
<v-text-field dense outlined v-model="form.values.endDate" :rules="form.rules.endDate" :min="form.values.startDate" label="End Date" append-icon="mdi-calendar" readonly v-bind="attrs" v-on="on"></v-text-field>
</template>
<v-date-picker v-model="form.values.endDate"></v-date-picker>
</v-menu>
</v-col>
<v-col cols="12" sm="6" md="2">
<v-menu ref="menu" v-model="endTimeMenu" :close-on-content-click="false" :nudge-right="40" :return-value.sync="form.values.endTime" transition="scale-transition" offset-y max-width="290px" min-width="290px">
<template v-slot:activator="{ on, attrs }">
<v-text-field dense v-model="form.values.endTime" label="End Time" append-icon="mdi-clock-time-four-outline" readonly v-bind="attrs" v-on="on" outlined></v-text-field>
</template>
<v-time-picker v-if="endTimeMenu" v-model="form.values.endTime" :min="form.values.startTime" full-width #click:minute="$refs.menu.save(form.values.endTime)"></v-time-picker>
</v-menu>
</v-col>
</v-row>
</v-form>
</v-card>
<v-btn color="primary" #click="validate()" :disabled="!valid"> Continue </v-btn>
<router-link :to="`/${segment1}`">
<v-btn text> Cancel </v-btn>
</router-link>
</v-stepper-content>
<v-stepper-content step="2">
<v-card class="mb-12" elevation="0">
<v-form ref="form2" lazy-validation v-model="valid2" id="form2">
<v-row>
<v-card-text class="font-weight-bold">
Destination
<v-tooltip right>
<template v-slot:activator="{ on, attrs }">
<v-btn icon v-bind="attrs" v-on="on">
<v-icon color="grey lighten-1">info</v-icon>
</v-btn>
</template>
<span>The scan destination is the end point for a consumer experience. Can be single URL or use URL Groups. </span>
</v-tooltip>
</v-card-text>
</v-row>
<v-row>
<v-col class="col-sm-2 col-lg-2 col-12">
<v-select dense outlined :items="urlTypes" label="Single or Multi URL" v-model="form.values.urlType" :rules="form.rules.urlType"></v-select>
</v-col>
<v-col cols="12" sm="6" md="2">
<v-text-field dense outlined v-model="form.values.url" :rules="form.rules.url" label="URL" required></v-text-field>
</v-col>
</v-row>
<v-row>
<v-card-text class="font-weight-bold"
>Conditions :
<v-tooltip right>
<template v-slot:activator="{ on, attrs }">
<v-btn icon v-bind="attrs" v-on="on">
<v-icon color="grey lighten-1">info</v-icon>
</v-btn>
</template>
<span>Set the conditions for a campaign. If all conditions are true, this campaign will trigger for consumer experience.</span>
</v-tooltip>
</v-card-text>
<v-col cols="12" sm="6" md="4">
<v-select dense outlined :items="attributes" item-text="name" item-value="id" v-model="form.values.attribute" :rules="form.rules.attribute" label="Attribute"></v-select>
</v-col>
<v-col cols="12" sm="6" md="1">
<v-combobox v-model="operator" :items="operators" item-text="operator" item-value="id" label="Operator" outlined dense></v-combobox>
</v-col>
<v-col cols="12" sm="6" md="4">
<!-- <v-text-field dense outlined v-model="form.values.value" :rules="form.rules.value" label="Values" required></v-text-field> -->
<v-text-field v-model="value" :items="values" label="Value" multiple outlined dense></v-text-field>
</v-col>
</v-row>
</v-form>
</v-card>
<v-btn color="primary" #click="validate2()" :disabled="!valid2"> Update </v-btn>
<v-btn text #click="e1 = 1"> Back </v-btn>
</v-stepper-content>
<v-stepper-content step="3"> </v-stepper-content>
</v-stepper-items>
</v-stepper>
</v-card>
</v-col>
</v-row>
</v-container>
</template>
<script>
import Navbar from '../../../components/Navbar'
import Breadcrumbs from '../../../components/Breadcrumbs'
import PanelHeader from '../../../components/PanelHeader'
import axios from 'axios'
import moment from 'moment-timezone'
export default {
components: {
Navbar,
Breadcrumbs,
PanelHeader
},
beforeMount() {},
computed: {
segment1: function () {
const firstSegment = new URL(window.location.href).pathname.split('/')[1]
return `${firstSegment}`
},
timeZone: function () {
console.log('timeZone')
}
},
beforeMount() {},
mounted() {
this.getCampaign()
},
data() {
return {
campaign: {},
form: {
errors: {},
values: {
name: this.campaign.name,
type: 'Marketing',
description: null,
timezone: 'America/New_York',
startDate: new Date(Date.now() - new Date().getTimezoneOffset() * 60000).toISOString().substr(0, 10),
endDate: new Date(Date.now() - new Date().getTimezoneOffset() * 60000).toISOString().substr(0, 10),
startTime: moment().format('HH:mm'),
endTime: '24:00',
urlType: 'Single',
url: 'https://',
attribute: '',
operator: '',
value: ''
},
rules: {
type: [(v) => !!v || 'Type is required'],
name: [(v) => !!v || 'Name is required'],
startDate: [(v) => !!v || 'Start Date is required'],
endDate: [(v) => !!v || 'End Date is required'],
timezone: [(v) => !!v || 'Timezone is required'],
startTime: [(v) => !!v || 'Start Time is required'],
endTime: [(v) => !!v || 'End Time is required'],
urlType: [(v) => !!v || 'URL Type is required'],
url: [(v) => !!v || 'URL is required'],
attribute: [(v) => !!v || 'Attribute is required'],
operator: [(v) => !!v || 'Operator is required'],
value: [(v) => !!v || 'Value is required']
}
},
e1: 1,
valid: false,
valid2: false,
types: ['Product', 'Marketing'],
operator: [],
operators: ['=', '!=', 'in', 'not in'],
value: [],
values: ['Italy', 'Finland', 'Norway'],
timezones: moment.tz.names(),
startDateMenu: false,
endDateMenu: false,
startTimeMenu: false,
endTimeMenu: false,
urlTypes: ['Single', 'Multiple'],
attributes: []
}
},
watch: {
'form.values.attribute'() {
this.operator = null
axios.defaults.headers['Content-Type'] = 'application/json'
let data = {
$root: 'vc_operator',
op: 'read',
brand: 'COLM',
selection: {
filters: [`id:${this.form.values.attribute}`]
},
_SESSION: localStorage.getItem('session')
}
axios.post(`${process.env.VUE_APP_API_ENDPOINT_URL}`, data).then((response) => {
this.operators = response.data.operators
})
}
},
methods: {
getAllData(id) {
let myForm = document.getElementById(id)
console.log(
Array.from(myForm.elements).map((e) => {
return e.value
})
)
},
validate() {
this.$refs.form.validate()
if (this.$refs.form.validate()) {
let data = {
$root: 'vc_rule_attribute',
op: 'read',
brand: 'COLM',
_SESSION: localStorage.getItem('session')
}
axios.defaults.headers['Content-Type'] = 'applcation/json'
axios.post(`${process.env.VUE_APP_API_ENDPOINT_URL}`, data).then((response) => {
this.attributes = response.data.rule_attributes
})
this.e1 = 2
console.info(this.form.values)
} else {
console.info(this.form.values)
}
},
validate2() {
this.$refs.form2.validate()
if (this.$refs.form2.validate()) {
let data = {
id: this.form.values.id,
name: this.form.values.name,
description: this.form.values.description,
start_date: this.form.values.startDate,
end_date: this.form.values.endDate,
priority: '100',
status_id: 1,
type_id: 1
}
let body = {
$root: 'vc_campaign',
op: 'update',
brand: 'COLM',
campaigns: [data],
_SESSION: localStorage.getItem('session')
}
// this.$store
// .dispatch('editCampaign', body)
// .then(() => {
// this.$router.push({
// path: `/campaigns`
// })
// })
// .catch((err) => {
// console.log('Something went wrong: ', err)
// })
} else {
console.info(this.form.values)
}
},
displayTime(time) {
time = time.split(':')[0]
if (time > 12) {
return 'PM'
} else {
return 'AM'
}
},
getCampaign() {
let filters = 'id:' + this.$route.params.id
let body = {
$root: 'vc_campaign',
op: 'read',
brand: 'COLM',
selection: {
filters: [filters]
},
_SESSION: localStorage.getItem('session')
}
axios.defaults.headers['Content-Type'] = 'applcation/json'
axios
.post(`${process.env.VUE_APP_API_ENDPOINT_URL}`, body)
.then((response) => {
if (response.data.status == 0) {
this.campaign = response.data.campaigns[0]
} else {
alert(response.data.statustext)
reject(response.data.statustext)
}
})
.catch((err) => {
console.log('Something went wrong: ', err)
})
}
}
}
</script>
<style></style>
You are basically trying to access a variable which is not defined yet. data() is called only once when you initialize the component. This happens way before you get the response from api and assign a value to this.campaign.
That's why this line doesn't work:
name: this.campaign.name
What you can do instead is something like this:
data() {
return {
campaign: {},
form: {
errors: {},
values: {
name: "",
...
getCampaign() {
...
axios.post(`${process.env.VUE_APP_API_ENDPOINT_URL}`, body)
.then((response) => {
if (response.data.status == 0) {
this.campaign = response.data.campaigns[0]
this.form.values.name = this.campaign.name
...
Keeping your requirements in mind, you can use watch property to keep both the values in sync
Set the initial form.values.name to ''
data() {
return {
campaign: {},
form: {
errors: {},
values: {
name: '',
...
and to keep the values in sync, you can do something like this:
watch: {
campaign: {
handler(newVal) {
this.form.values.name = newVal.name
},
immediate: true
}
}
Try
<p>Campaign Name : {{ campaign?.name }}</p>

Vuetify: How do I prevent all expand-transition from happening at once?

I am creating a page that consists of multiple cards, each card has a v-expand-transition and I am creating all cards in a v-for loop. The problem is that if I click one v-expand-transition then all of them open. I understand that it is because all of them are connected to the same "show", but I am not sure how to split that up so each one has their own event. My code is here:
<template>
<v-container>
<h1 class="font-weight-medium pa-6">All Labs</h1>
<v-row dense>
<v-col v-for="card in cards" :key="card.title" :cols="card.flex">
<v-card class="mx-auto" max-width="350">
<v-img class="white--text align-end" height="200px" :src="card.src" gradient="to bottom, rgba(0,0,0,.1), rgba(0,0,0,.5)">
<v-card-title v-text="card.title"></v-card-title>
</v-img>
<v-card-subtitle class="pb=0" v-text="card.location"></v-card-subtitle>
<v-card-text class="text--primary">
<div v-text="card.hours"></div>
<div v-text="card.keycardInfo"></div>
</v-card-text>
<v-card-actions>
<v-btn color="blue" :href="card.directions" text target="_blank">Directions</v-btn>
<v-btn color="blue" :to="card.page" text>Learn More</v-btn>
<v-spacer></v-spacer>
<v-btn icon #click="show = !show">
<v-icon>{{ show ? 'mdi-chevron-up' : 'mdi-chevron-down'}}</v-icon>
</v-btn>
</v-card-actions>
<v-expand-transition>
<div v-show="show">
<v-divider></v-divider>
<v-card-text v-text="card.bottomInfo"></v-card-text>
</div>
</v-expand-transition>
</v-card>
</v-col>
</v-row>
</v-container>
</template>
and my script is here:
<script>
import App from '../App.vue'
export default {
components: { App },
name: 'Home',
data: () => ({
show: false,
cards: [],
}),
}
</script>
I emptied the cards to avoid cluttering the page.
What happens is that since you are using a single variable to control the state of your component, the action will end up reflecting for everyone. One alternative would be to add a "show" property inside each object in your list, making each object have its own state. Here is an example of how it would look:
new Vue({
el: '#app',
data() {
return {
cards: [{
title: "My title",
flex: 6,
src: '#',
location: '#',
hours: '13',
keycardInfo: 'Info',
directions: '#',
page: '#',
to: '#',
bottomInfo: 'Bottom info',
show: false,
},
{
title: "My title 2",
flex: 6,
src: '#',
location: '#',
hours: '13',
keycardInfo: 'Info 2',
directions: '#',
page: '#',
to: '#',
bottomInfo: 'Bottom info 2',
show: false,
}],
}
}
})
<link href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/#mdi/font#4.x/css/materialdesignicons.min.css" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/vuetify#2.x/dist/vuetify.min.css" rel="stylesheet">
<div id="app">
<v-container>
<h1 class="font-weight-medium pa-6">All Labs</h1>
<v-row dense>
<v-col v-for="card in cards" :key="card.title" :cols="card.flex">
<v-card class="mx-auto" max-width="350">
<v-img class="white--text align-end" height="200px" :src="card.src" gradient="to bottom, rgba(0,0,0,.1), rgba(0,0,0,.5)">
<v-card-title v-text="card.title"></v-card-title>
</v-img>
<v-card-subtitle class="pb=0" v-text="card.location"></v-card-subtitle>
<v-card-text class="text--primary">
<div v-text="card.hours"></div>
<div v-text="card.keycardInfo"></div>
</v-card-text>
<v-card-actions>
<v-btn color="blue" :href="card.directions" text target="_blank">Directions</v-btn>
<v-btn color="blue" :to="card.page" text>Learn More</v-btn>
<v-spacer></v-spacer>
<v-btn #click="card.show = !card.show">
{{ card.show ? 'Hide' : 'Show' }}
</v-btn>
</v-card-actions>
<v-expand-transition>
<div v-show="card.show">
<v-divider></v-divider>
<v-card-text v-text="card.bottomInfo"></v-card-text>
</div>
</v-expand-transition>
</v-card>
</v-col>
</v-row>
</v-container>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue#2.x/dist/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vuetify#2.x/dist/vuetify.js"></script>
As you are using Vuetify, another approach would be to use expansion panels, I'll leave the documentation link below for you to take a look.
https://vuetifyjs.com/en/components/expansion-panels/#usage

Vue-Router: page with params is blank after refreshing

On initial page, I get 2 ID variables: organizationId and brandId, that I want to save in my URL as params, while routing to Organization Overview Page.
export const router = new Router({
mode: 'history',
routes: [
...
{
path: 'organization/:organizationId/brand/:brandId/overview',
name: 'OrganizationOverviewPage',
component: OrganizationOverviewPage,
},
...
]
});
<router-link :to="{ name: 'OrganizationOverviewPage',
params: { organizationId: owner.id, brandId: brand.id }}">
BUTTON
</router-link>
It works properly, on the new page I can see both variables in URL and in Vue DevTools:
The problem is that when I refresh the page, I got a blank screen.
The interesting thing is that when I include only 'organizationId' variable in the router path, it works properly.
{
path: 'organization/:organizationId/overview',
name: 'OrganizationOverviewPage',
component: OrganizationOverviewPage,
},
This is also how it looked like earlier, but due to the needs of the second variable - brandId, I had to add it - now it does not work.
The URL seems to hit the route because only when I break it, I got 404. Could this be related to the generated component?
I have no idea how to find a solution, I did not even get any errors from the terminal or browser console.
Update - OrganizationOverviewPage:
<template>
<div>
<v-row no-gutters class="mb-3">
<v-col cols="12">
<v-card flat>
<v-card-title>
<h5 class="float-left m-0">
<router-link
:to="{ name: 'OrganizationOverviewPage'}"
class="main-options-router-link"
>
<span class="font-weight-bold text-dark">Overview</span>
</router-link>
<span class="px-1">|</span>
<router-link
class="main-options-router-link"
:to="{ name:'OrganizationArtists'}"
>Artists</router-link>
<span class="px-1">|</span>
<router-link class="main-options-router-link" :to="{ name: 'OrganizationBrands'}">Brands</router-link>
<span class="px-1">|</span>
<router-link class="main-options-router-link" :to="{ name: 'OrganizationTeam'}">Team</router-link>
<span class="px-1">|</span>
<router-link
class="main-options-router-link"
:to="{ name: 'OrganizationTemplates'}"
>Templates</router-link>
</h5>
</v-card-title>
</v-card>
</v-col>
</v-row>
<v-row>
<v-col md="6">
<OrganizationProfileWidget></OrganizationProfileWidget>
</v-col>
<v-col md="6">
<OrganizationMetricsWidget v-bind:organizationId="organizationId"></OrganizationMetricsWidget>
</v-col>
</v-row>
<v-row no-gutters class="mt-3">
<v-col cols="12">
<Widget
title="<h4><span class='font-weight-bold'>Active Campaigns</span></h4>"
customHeader
settings
:name="`Organization Overview`"
>
<div slot="body">
<v-row>
<v-col class="mt-4 mb-2">
<v-text-field
outlined
v-model="search"
dense
placeholder="Search user by Name"
hide-details
></v-text-field>
</v-col>
</v-row>
<v-data-table
:headers="headers"
:items="list"
item-key="id"
:search="search"
class="elevation-1 list-data-table"
:loading="inProgress"
hide-default-footer
:page.sync="page"
:items-per-page="itemsPerPage"
#page-count="pageCount = $event"
>
<template v-slot:item.artistName="{ item }">
<router-link
:to="{ name:'ArtistOpsPage',params: { organizationId: organizationId,artistId:item.artistId }}"
>{{item.artistName}}</router-link>
</template>
<template v-slot:item.title="{ item }">
<router-link
:to="{ name:'ArtistOpsPage',params: { organizationId: organizationId,artistId:item.artistId }}"
>{{item.title}}</router-link>
</template>
<template v-slot:item.startDt="{ item }">{{item.startDt | DayMonthYear}}</template>
<template v-slot:item.endDt="{ item }">{{item.endDt | DayMonthYear}}</template>
<template v-slot:footer>
<v-divider></v-divider>
<v-row align="center">
<v-col cols="2" class="px-4">
<v-select
:value="itemsPerPage"
:items="perPageRecordsOptions"
label="Items per page"
#change="itemsPerPage = parseInt($event, 10)"
></v-select>
</v-col>
<v-spacer></v-spacer>
<v-col cols="8" class="px-4">
<v-pagination v-model="page" :length="pageCount"></v-pagination>
</v-col>
<v-spacer></v-spacer>
</v-row>
</template>
</v-data-table>
</div>
</Widget>
</v-col>
</v-row>
</div>
</template>
<script>
import Widget from "#/components/venus/Widget";
import { createHelpers } from "vuex-map-fields";
import OrganizationProfileWidget from "#/components/venuspro/agent/suitcase/OrganizationProfileWidget";
import OrganizationMetricsWidget from "#/components/venuspro/agent/suitcase/OrganizationMetricsWidget";
const { mapFields } = createHelpers({
getterType: "organizationOne/getField",
mutationType: "organizationOne/updateField"
});
export default {
name: "OrganizationOverviewPage",
components: {
OrganizationProfileWidget,
OrganizationMetricsWidget,
Widget
},
computed: {
organizationId: function() {
return this.$route.params.organizationId;
},
brandId: function() {
return this.$route.params.brandId;
},
list: function() {
return this.$store.state.campaignList.allList;
},
organizationOne: function() {
return this.$store.state.organizationOne.one;
},
...mapFields(["one", "inProgress"])
},
data() {
return {
search:"",
page: 1,
itemsPerPage: 10,
perPageRecordsOptions: [5, 10, 50, 100, 500],
headers: [
{
text: "Artist",
align: "center",
sortable: false,
value: "artistName"
},
{
text: "Title",
align: "center",
sortable: true,
filterable: true,
value: "title"
},
{
text: "Start Date",
align: "center",
sortable: true,
filterable: true,
value: "startDt"
},
{
text: "End Date",
align: "center",
sortable: true,
filterable: true,
value: "endDt"
}
]
};
},
created() {
if (this.organizationOne.id !== this.organizationId)
this.$store.dispatch("organizationOne/fetchOne", this.organizationId);
this.$store.dispatch(
"campaignList/fetchAllCampaignList",
this.organizationId
);
},
methods: {}
};
</script>
Someone answer under this question with a hint, but then removed his answer. Thank You anyway, that was a solution!
In my vue.config.js, it was the configuration in my devServer object that I needed to fix.
module.exports = {
...
devServer: {
...
historyApiFallback: {
rewrites: [
...
// { from: /\/brand/, to: '/brand.html' },
]
}
},
}
I have commented out the line with brand rewriting, and now it's working!

Vue updates all button text after changing specific button

Hi everyone I'm playing around with Vue JS but for some how I cannot get what I expected. Below are my code.
Template
<div id="app">
<v-app id="inspire">
<div class="text-xs-center" v-for="x in count" :key="x">
<v-menu offset-y>
<v-btn
slot="activator"
color="primary"
dark
>
{{name}}
</v-btn>
<v-list>
<v-list-tile
v-for="(item, index) in items"
:key="index"
#click="test(item.title)"
>
<v-list-tile-title>{{ item.title }}</v-list-tile-title>
</v-list-tile>
</v-list>
</v-menu>
</div>
</v-app>
</div>
Vue
new Vue({
el: '#app',
data: () => ({
name: 'default',
items: [
{ title: 'Click Me 1' },
{ title: 'Click Me 2' },
{ title: 'Click Me 3' },
{ title: 'Click Me 2' }
],
count: 10
}),
methods: {
test(title) {
this.name = title
}
}
})
What I want is that when I change a specific button text the other buttons should not be affected. But it seems my code is doing the opposite. What am I missing here? Any help, explanation would be much appreciated. Thanks
new Vue({
el: '#app',
data: () => ({
name: 'default',
items: [
{ title: 'Click Me 1' },
{ title: 'Click Me 2' },
{ title: 'Click Me 3' },
{ title: 'Click Me 2' }
],
count: 10
}),
methods: {
test(title) {
this.name = title
}
}
})
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vuetify#1.5.3/dist/vuetify.min.js"></script>
<link href="https://cdn.jsdelivr.net/npm/vuetify#1.5.3/dist/vuetify.min.css">
<div id="app">
<v-app id="inspire">
<div class="text-xs-center" v-for="x in count" :key="x">
<v-menu offset-y>
<v-btn
slot="activator"
color="primary"
dark
>
{{name}}
</v-btn>
<v-list>
<v-list-tile
v-for="(item, index) in items"
:key="index"
#click="test(item.title)"
>
<v-list-tile-title>{{ item.title }}</v-list-tile-title>
</v-list-tile>
</v-list>
</v-menu>
</div>
</v-app>
</div>
You are iterating over a normal number, in your example 10, so you are just showing 10 times the same variable name.
If you now change that variable name to something, it will change in all the buttons accordingly.
You need some way to save the different names, e.g. an array of objects like your items with all the titles.
I took your code and changed it a bit. Instead of iterating over a fixed count, I created an array of names and iterate over that array. When you click one of the buttons and change the text, instead of just changing the universal name attribute - you change the name at the position in the array.
new Vue({
el: '#app',
data: () => ({
names: [
{name: 'default 1'}, {name: 'default 2'}, {name: 'default 3'}, {name: 'default 4'}],
items: [
{ title: 'Click Me 1' },
{ title: 'Click Me 2' },
{ title: 'Click Me 3' },
{ title: 'Click Me 4' }
],
}),
methods: {
test(title, index) {
this.names[index].name = title
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vuetify/1.5.3/vuetify.min.js"></script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/vuetify/1.5.3/vuetify.css.map">
<div id="app">
<v-app id="inspire">
<div class="text-xs-center" v-for="(x, index) in names" :key="'name' + index">
<v-menu offset-y>
<v-btn
slot="activator"
color="primary"
dark
>
{{x.name}}
</v-btn>
<v-list>
<v-list-tile
v-for="(item, i) in items"
:key="'item' + i"
#click="test(item.title, index)"
>
<v-list-tile-title>{{ item.title }}</v-list-tile-title>
</v-list-tile>
</v-list>
</v-menu>
</div>
</v-app>
</div>

Categories

Resources