There is a component in which two select fields (country, city) are created on click.The fields are dependent, when the country is selected, the values in the second select (city) are changed.The problem is that when we change one select (country) all other select-s (cities) change.
<template>
<b-container class="bv-example-row">
<b-row v-for="(station, counter) in stations" v-bind:key="counter">
<b-col cols="6">
<label>Country</label>
<select class='form-control' name="country_dest_id" v-model='station.country' #change='getStates($event)'>
<option value='0' >Select Country</option>
<option v-for='data in countries' :value='data.id'>{{ data.name }}</option>
</select>
</b-col>
<b-col cols="6">
<label >City</label>
<select class='form-control' v-model='station.state'>
<option value='0' >Select State</option>
<option v-for='data in states' :value='data.id'>{{ data.name }}</option>
</select>
</b-col>
<b-col cols="1">
<button class="btn btn-danger remove" #click="deleteStation(counter)"><i class="fa fa-times" aria-hidden="true"></i> Remove</button>
</b-col>
</b-row>
<b-row class="justify-content-md-center">
<b-col cols="3" md="3">
<button class="btn btn-success" type="button" #click="addStation">Add</button>
</b-col>
</b-row>
</b-container>
</template>
<script>
export default {
mounted() {
console.log('Component mounted.')
},
data(){
return {
stations:[
{
country: '',
state:'',
}
],
countries: [],
states:[]
}
},
methods:{
addStation(){
this.stations.push({
country:'',
state: ''
})
},
deleteStation(counter){
this.stations.splice(counter,1);
},
getCountries: function(){
axios.get('/getCountries')
.then(function (response) {
this.countries = response.data;
}.bind(this));
},
getStates: function(event) {
console.log(event.target.value);
axios.get('/getStates',{
params: {
country_id: event.target.value
}
}).then(function(response){
this.states = response.data;
}.bind(this));
}
},
created: function(){
this.getCountries()
}
}
</script>
How to make field groups unique values?
The problem is that the getStates function works always on the same array in data(). #change overwrites that value & every select that is bound to it receives the new set of values.
You'd be better off if you built up an object of states, something like (the sample probably is not working, just giving an idea):
data() {
return {
states: {
countryId1: ["cid1state1", "cid1state2"],
countryId2: ["cid2state1", "cid2state2"],
},
}
},
methods: {
getStates: function(event) {
if (!(event.target.value in this.states)) {
axios.get('/getStates',{
params: {
country_id: event.target.value
}
}).then(function(response){
this.states[event.target.value] = response.data;
}.bind(this));
}
}
},
Related
I have pretty simple table of users here with only 4 cols, and i want to show a button for each user depending on his status 'isActive'. If user is active i want to show button with text 'disable' and vice versa. I am little bit stuck with this because i dont have an idea how can i show these buttons, because i am using vuexy template for this project(admin panel). Is there a way to do this with JSX?
Please take a look at code, i am getting data from mysql with nodejs. Ask me if you need more info. Thanks.
<template>
<div>
<div class="container">
<b-card-text class="mb-2">
<div
v-if="showLoginError"
class="text-center bg-danger colors-container rounded text-white width-360 height-50 d-flex align-items-center justify-content-center mr-1 ml-50 my-1 shadow"
>
<span>{{ loginError }}</span>
</div>
</b-card-text>
<b-card-text class="mb-2">
<div
v-if="showSuccessMessage"
class="text-center bg-success colors-container rounded text-white width-360 height-50 d-flex align-items-center justify-content-center mr-1 ml-50 my-1 shadow"
>
<span>{{ successMessage }}</span>
</div>
</b-card-text>
<section id="card-actions" class="input-section">
<b-row>
<b-col cols="8">
<b-card-actions ref="cardAction">
<validation-observer ref="simpleRules">
<b-form>
<b-row>
<b-col md="6">
<b-form-group>
<validation-provider
#default="{ errors }"
name="First Name"
rules="required"
>
<b-form-input
v-model="name"
:state="errors.length > 0 ? false:null"
placeholder="Twitter username"
/>
</validation-provider>
</b-form-group>
</b-col>
<b-col cols="12">
<b-button
variant="primary"
type="submit"
#click.prevent="validationForm"
>
Submit
</b-button>
</b-col>
</b-row>
</b-form>
</validation-observer>
</b-card-actions>
</b-col>
</b-row>
</section>
// This is table
<b-table responsive="sm" :items="items"/>
</div>
</div>
</template>
<script>
import { ValidationProvider, ValidationObserver } from 'vee-validate'
import {
BFormInput, BFormGroup, BForm, BRow, BCol, BButton, BTable,
} from 'bootstrap-vue'
import { required } from '#validations'
import axios from 'axios'
import { getUserToken } from '#/auth/auth'
export default {
components: {
ValidationProvider,
ValidationObserver,
BFormInput,
BFormGroup,
BForm,
BRow,
BCol,
BButton,
BTable,
},
data() {
return {
name: '',
successMessage: '',
showSuccessMessage: false,
loginError: '',
showLoginError: false,
required,
items: [],
}
},
beforeMount() {
this.getAllUsers()
},
methods: {
getAllUsers() {
const API_URL = `${this.$server}/api/twitter/allusers`
const params = {
token: getUserToken(),
}
axios.post(API_URL, null, { params }).then(res => {
if (res.data) {
res.data.forEach(element => {
let isActive = 'active'
if (element.isActive === 0) {
isActive = 'disabled'
}
const arr = {
twitter_name: element.twitter_name,
twitter_username: element.twitter_username,
twitter_id: element.twitter_id,
userActive: isActive,
}
this.items.push(arr)
})
}
})
},
validationForm() {
const API_URL = `${this.$server}/api/twitter/adduser`
const params = {
twitter_username: this.name,
token: getUserToken(),
}
axios.post(API_URL, null, { params }).then(res => {
if (res.data.success) {
this.successMessage = res.data.message
this.showSuccessMessage = true
// Hide message after 5sec
setTimeout(() => {
this.successMessage = ''
this.showSuccessMessage = false
}, 5000)
} else {
this.loginError = res.data.message
this.showLoginError = true
// Hide message after 5sec
setTimeout(() => {
this.loginError = ''
this.showLoginError = false
}, 5000)
}
})
},
},
}
</script>
I'm a little bit confused, where do you want to show your button ?
If it's in the table, you can use the custom templation of Bootstrap-Vue, you'll find the doc here with an example : https://bootstrap-vue.org/docs/components/table#custom-data-rendering
EDIT: here an example for your case
<b-table responsive="sm" :items="items">
<template #cell(userActive)="data">
<b-button v-if="data.userActive">Disabled</b-button>
<b-button v-else>Enabled</b-button>
</template>
</b-table>
I need your help solving a problem ...
I'm new to vue and I can't use modal bootstrap-vue.
I need to load a form with the data into my "MODAL" so that I can change it, but I can't load this data by the user ID inside the modal form.
<PageTitle icon="fa fa-cogs" main="Administração" sub="Feriados" />
<b-table hover striped :items="feriados" :fields="fields" small class="mt-0 mb-0">
<template slot="actions" slot-scope="data">
<b-button variant="warning" #click="loadFeriado(data.item)" class="mr-2" size="sm">
<i class="fa fa-pencil"></i>
</b-button>
<b-button variant="info" #click="viewFeriado" class="mr-2" size="sm">
<i class="fa fa-pencil"> Editar</i>
</b-button>
</template>
</b-table>
<div>
<b-modal ref="showFeriado"
title="Visualizar Feriado"
size="xl"
#shown="loadFeriado"
#hidden="reset"
#ok="save"
ok-variant="primary"
cancel-title="Cancelar">
<div>
<input id="feriado-id" type="hidden" v-model="feriado.id"/>
<b-row class="mt-0 mb-0">
<b-col md="6" sm="12">
<b-form-group
label="Data do Feriado:"
label-for="feriado-data"
label-size="sm"
class="mt-0 mb-0">
<v-date-picker v-model="feriado.data" />
</b-form-group>
</b-col>
<b-col md="6" sm="12">
<b-form-group label="Nome:" label-for="feriado-descricao" label-size="sm">
<b-form-input id="feriado-descricao" type="text"
size="sm"
v-model="feriado.descricao" required
:readonly="mode === 'remove'"
placeholder="Informe o nome do Feriado..." />
</b-form-group>
</b-col>
</b-row>
</div>
</b-modal>
</div>
</div>
</template>
<script>
import PageTitle from '../template/PageTitle'
import axios from 'axios'
import { baseApiUrl, showError, userKey } from '#/global'
import moment from 'moment'
export default {
name: 'FeriadoAdmin',
components: {
PageTitle,
moment,
},
data: function() {
return {
mode: 'save',
feriado: {},
feriados: [],
fields: [
{ key: 'id', label: 'Número', sortable: true },
{
key: 'descricao',
label: 'Descrição',
sortable: true
},
{
key: 'data',
label: 'Data',
sortable: true,
formatter: (value, key, item) => {
return moment.utc(value).format('DD/MM/YYYY')
}
},
{
key: 'actions',
label: 'Status'
}
]
}
},
methods: {
viewFeriado () {
this.$refs.showFeriado.show()
},
loadFeriados() {
axios.get(`${baseApiUrl}/feriados`)
.then((list) => {
this.feriados = list.data
})
},
loadFeriado(feriado) {
this.feriado = {
...feriado,
data: moment.utc(feriado.data).toDate()
}
//console.log(feriado)
}
},
mounted() {
this.loadFeriados()
}
}
</script>
<style>
</style>
This console error occurs when attempting to load data into the modal form:
Why my b-table displays all columns of my table even I only selected some?
Here is my server-side code that calls the selected columns. Also, it worked if I use bootstrap only not bootstrap-vue.
router.get('/users', function(req, res) {
/* Get the person who has the latest date */
let getUser = "SELECT DISTINCT(MEMB.MEMB_N), MAX(PrintDate) AS PrintDate, MEMB.* \
FROM MEMB LEFT JOIN VD_Print ON MEMB.MEMB_N = VD_Print.MEMB_N GROUP BY MEMB.LAST_M \
ORDER BY PrintDate DESC LIMIT 100;"
myDB.query(getUser, function(err, rows) {
if (err) {
console.log(err);
} else {
console.log(rows);
res.send(rows);
}
});
});
And this one is on my client-side which is vuejs
<template>
<section>
<div class="sidebar"></div>
<div>
<b-form-input class="searchBar" placeholder="Search Here"></b-form-input>
</div>
<div>
<b-table class="table" striped hover :items="results"></b-table>
</div>
<b-button class="printBtn">PRINT</b-button>
</section>
</template>
<script>
import axios from "axios";
export default {
data() {
return {
results: [],
};
},
mounted() {
this.getUsers();
},
methods: {
getUsers: function() {
axios
.get("http://localhost:9000/api/users/")
.then(response => (this.results = response.data))
.catch(error => alert(error));
}
}
};
</script>
My JSON looks like this:
You need to define the your column name in field definition of b-table. If you multiple filed in response but you want to display some fields.
Please below code and working demo.
CODE SNIPPET
export default {
data() {
return {
selectAll: false,
records: [],
perPage: 10,
currentPage: 1,
pageOptions: [5, 10, 15],
column: [{
key: "name",
sortable: true,
label: "Log File Name"
}, {
key: "lastModified",
sortable: true,
label: "Last Modified Date",
class: "text-right options-column"
}]
};
}
}
<template>
<div>
<div v-if="!hasRecords" style="text-align: center"><br><br>LOADING DATA...</div>
<div style="padding: 15px;" v-if="hasRecords">
<b-table :items="records" :fields="column" striped hover :current-page="currentPage" :per-page="perPage">
</b-table>
<b-row>
<b-col md="6" class="my-1">
<b-pagination :total-rows="totalRows" :per-page="perPage" v-model="currentPage" class="my-0" />
</b-col>
<b-col md="6" class="my-1">
<b-form-group horizontal label="Per page" class="mb-0">
<b-form-select :options="pageOptions" v-model="perPage" />
</b-form-group>
</b-col>
</b-row>
</div>
</div>
</template>
I’m using Vue to create a Flickr app and want to add a search bar so users can search for photos containing tags with their search term.
What i’ve done so far produces some results, but I noticed the photos don’t always include my search term as tags, for example if I search 'cats' the returned items might have the tags 'cat' but not 'cats' and sometimes it doesn't include tags that are even slightly similar.
There's no console errors, so i'm not sure where to find the error.
<template>
<b-container>
<b-row>
<b-col md="12">
<b-input-group size="lg" prepend="Search" class="flickr-search">
<b-form-input v-model="search"></b-form-input>
</b-input-group>
</b-col>
</b-row>
<b-row>
<b-card-group columns>
<b-col v-for="photo in Photos" class="item" md="12">
<b-card :title="photo.title"
:img-src="photo.media.m"
img-alt="Image"
img-top
img-fluid
tag="article"
style="max-width: 20rem;"
class="mb-2">
<span class="item-date">31 May 2017</span>
<hr/>
<p>By <a :href="'https://www.flickr.com/photos/' + photo.author_id" :title="formatAuthor(photo.author)" target="_blank">{{ formatAuthor(photo.author) }}</a></p>
<ul class="tags">
<li v-for="tag in splitTags(photo.tags)" class="item-tag">
<a :href="'https://www.flickr.com/photos/tags/' + tag" target="_blank" class="item-taglink">{{ tag }}</a>
</li>
</ul>
</b-card>
</b-col>
</b-card-group>
</b-row>
</b-container>
</template>
<script>
import jsonp from "jsonp";
export default {
name: 'PhotoFeed',
data: function () {
return {
Photos: [],
apiURL: "https://api.flickr.com/services/feeds/photos_public.gne?format=json",
search: ''
}
},
mounted(){
this.getFlickrFeed();
},
methods: {
getFlickrFeed(){
let jsonp = require('jsonp');
let self = this;
jsonp(this.apiURL, {name: 'jsonFlickrFeed'}, (err, data) => {
if (err) {
console.log(err.message);
}
else {
self.Photos = data.items;
}
})
},
formatAuthor(authorString){
if (authorString) return authorString.split("\"")[1];
return "Author";
},
splitTags(tagsString) {
if (tagsString) return tagsString.split(" ");
}
},
watch: {
search(newVal, oldVal) {
let self = this;
let apiURL = "https://api.flickr.com/services/feeds/photos_public.gne?tags=" + self.search + "&format=json";
let jsonp = require('jsonp');
jsonp(apiURL, {name: 'jsonFlickrFeed'}, (err, data) => {
if (err) {
console.log(err.message);
}
else {
self.Photos = data.items;
}
})
}
}
}
</script>
An error won't be thrown if the flicker API doesn't return any results.
In the UI you can let you users know they can provide a comma separated list of keywords, example cats, cat
You can also check no see if there are any results and display a message if none were found here is just one simple example implementation:
Relevant HTML
<b-card-group columns v-if="Photos.length">
<b-col v-for="photo in Photos" class="item" md="12">
<b-card :title="photo.title"
:img-src="photo.media.m"
img-alt="Image"
img-top
img-fluid
tag="article"
style="max-width: 20rem;"
class="mb-2">
<span class="item-date">31 May 2017</span>
<hr/>
<p>By <a :href="'https://www.flickr.com/photos/' + photo.author_id" :title="formatAuthor(photo.author)" target="_blank">{{ formatAuthor(photo.author) }}</a></p>
<ul class="tags">
<li v-for="tag in splitTags(photo.tags)" class="item-tag">
<a :href="'https://www.flickr.com/photos/tags/' + tag" target="_blank" class="item-taglink">{{ tag }}</a>
</li>
</ul>
</b-card>
</b-col>
</b-card-group>
<b-col md="12" v-else-if="!Photos.length && errorMessage">
<p>{{errorMessage}}</p>
</b-col>
Relevant JS
data: function () {
return {
Photos: [],
apiURL: "https://api.flickr.com/services/feeds/photos_public.gne?format=json",
search: '',
errorMessage: null
}
}
In both getFlickerFeed & watch: search:
if (err) {
self.errorMessage = err.message;
}
else {
self.Photos = data.items;
if (self.Photos.length) {
self.errorMessage = null;
} else {
self.errorMessage = 'No results found for: ' + self.search
}
}
I asked question on adding/removing row in how to use "v-for" for adding or removing a row with multiple components
However, I got a bug: when I adding a row, the items in the first row filled to second row and when I changed the second row, the 1st row is also overwritten as the same as 2nd row.
i must did sth really wrong.
in .js
var data1={selected: null, items: ["A1","B1"]};
Vue.component('comp1',{
template: ` <select v-model="selected">
<option disabled value="">Please select</option>
<option v-for="item in items" :value="item">{{item}}
</option>
</select>`,
data:function(){
return data1
}
});
var data2={selected: null, items: ["A2","B2"]};
Vue.component('comp2',{
template: ` <select v-model="selected">
<option disabled value="">Please select</option>
<option v-for="item in items" :value="item">{{item}}
</option>
</select>`,
data:function(){
return data2
}
});
new Vue({
el: '#app',
data: {
rows: []
},
computed:{
newId(){
return this.rows.length == 0 ? 1 : Math.max(...this.rows.map(r => r.id)) + 1
}
},
methods: {
addRow: function() {
this.rows.push({id: this.newId });
},
removeRow: function(row) {
this.rows.splice(this.rows.indexOf(row), 1)
}
},
});
in .html
<div id="app">
<div v-for="row in rows">
<comp1></comp1>
<comp2></comp2>
<button #click="removeRow(row)">Remove Row</button>
</div>
<button #click="addRow">Add Row</button>
</div>
You need to add the key.
<div v-for="row in rows" :key="row.id">
And you shouldn't share the data between the components, so move your data into the components.
data: function() {
return {
selected: null,
items: ["A1", "B1"]
}
}
Here is a working version.
Vue.component('comp1', {
template: ` <select v-model="selected">
<option disabled value="">Please select</option>
<option v-for="item in items" :value="item">{{item}}
</option>
</select>`,
data: function() {
return {
selected: null,
items: ["A1", "B1"]
}
}
});
Vue.component('comp2', {
template: ` <select v-model="selected">
<option disabled value="">Please select</option>
<option v-for="item in items" :value="item">{{item}}
</option>
</select>`,
data: function() {
return {
selected: null,
items: ["A2", "B2"]
}
}
});
new Vue({
el: '#app',
data: {
rows: []
},
computed: {
newId() {
return this.rows.length == 0 ? 1 : Math.max(...this.rows.map(r => r.id)) + 1
}
},
methods: {
addRow: function() {
this.rows.push({
id: this.newId
});
},
removeRow: function(row) {
this.rows.splice(this.rows.indexOf(row), 1)
}
},
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.4.2/vue.js"></script>
<div id="app">
<div v-for="row in rows" :key="row.id">
<comp1></comp1>
<comp2></comp2>
<button #click="removeRow(row)">Remove Row</button>
</div>
<button #click="addRow">Add Row</button>
</div>
Specifically, the way you are sharing data is because you are defining the data like this:
var data1={selected: null, items: ["A1","B1"]};
And returning that object from your data function in the component:
data:function(){
return data1
}
This means that every instance of that component is sharing the same data. That's not what you want with components. Each component should have it's own copy of the data. In this case, there is no need whatsoever to define the data object returned from the data function outside the component.