how to fetch object with object - javascript

I am trying to fetch NAME from the list in the datatable. So,far below is the code that I have written.
HTML template -
<v-data-table :headers="headers" :items="topics" :footer-props="{'items-per-page-options': [5, 10, 20, 40]}" :items-per-page="10" item-key="srno">
<template v-slot:item.skillName="{ item }">
<span>{{ item.skillName || '-' }}</span>
</template>
</v-data-table>
export default {
data () {
return {
skills: [
{
name: ''
}
],
topics: [],
headers: [
{ text: 'Sr No', value: 'srno', align: 'center', sortable: true },
{ text: 'Skills', value: 'skills', align: 'center' },
],
skillName: [],
}
},
created: async function () {
await this.getAllList()
},
methods: {
getAllList: async function () {
try {
this.isLoading = true
let r = []
let res = await http.get(`${CONSTANTS.API_URL}/api/getall-themes`, {
params: {
page: this.page - 1,
size: this.pageSize
// search: this.search
}
})
this.topics = res.data.rows.map((item, index) => {
item.srno = this.pageSize * (this.page - 1) + index + 1
item.skillName = item.Skills ? item.Skills.name : '-'
return item
})
this.totalPages = res.data.totalPages
} catch (e) {
const errorMessage = (e && e.response && e.response.data.message) || e.message
this.errMsg(errorMessage)
} finally {
this.isLoading = false
}
},
ERROR - skillName is going as undefined.
Excpeted Output - Writing,Singing should go in skillName

Your don't declare a skillnName column in your header, only skills:
Try:
export default {
data () {
return {
...
headers: [
{ text: 'Sr No', value: 'srno', align: 'center', sortable: true },
{ text: 'Skills',
value: 'skills', // <---- probably should be skillName
align: 'center' },
],
}
},
or
<v-data-table :headers="headers" :items="topics" :footer-props="{'items-per-page-options': [5, 10, 20, 40]}" :items-per-page="10" item-key="srno">
<template v-slot:item.skills="{ item }"> <!-- <-------- slot name should match header.value -->
<span>{{ item.skillName || '-' }}</span>
</template>
</v-data-table>
Also, according to your data, you want to use an array here, so you need to create something printable from it. Try something like {{ item.Skills.map(s => s.name).join(',') || '-' }} in the template.
If you don't want complex expressions in the template, you have other options:
create a method on the component, so it would be {{ formatSkills(item) }}
if the items are instances of classes, creates a method on the class, so you can do: {{ item.getSkillsString() }}
after loading your data, create a property on each item with the joined value, something like:
async fetchData(){
const items = await loadItems()
items.forEach(item => item.skillsCsv = item.Skills.map(s => s.name).join(',')
}
The latter is usually the preferred option in Vue, it allows you to work without the slot, just set the new property in the header declaration:
{ text: 'Skills', value: 'skillsCsv', align: 'center' },
Note however that it is not reactive, if you add skills to an item, you have to rebuild the `skillsCsvĀ“ prop manually (or you handle it through a setter).
Of course you can always use Skills directly in the template:
<template v-slot:item.Skills="{ item }">
<span v-for="skill in item.Skills" :key="skill.id">
{{ skill.name}}
</span>
</template>

Related

vue-autosuggest output object-object on twice click

by the way this question very similar on Vue algolia autosuggest on select showing [Object object]
but i still did not solve here is my debug or code
<b-form-group
label="Name Agen"
label-for="vi-agen-name"
>
<span v-text="form.agen.name_agen" />
<vue-autosuggest
id="vi-agen-name"
v-model="form.agen.name_agen"
:suggestions="[{ data: form.agen.agens }]"
:limit="7"
:input-props="{
id: 'autosuggest__input',
class: 'form-control',
placeholder: 'Name Agen',
}"
#selected="onSelectedFrom"
#input="searchForm($event, 'agen/', 'agen', 'name_agen')"
>
<template slot-scope="{suggestion}">
<span class="my-suggestion-item">{{ suggestion.item.name_agen }}</span>
</template>
</vue-autosuggest>
</b-form-group>
my problem:
i have on typing yogi on form input
select item that show on suggesstion : [ { data : [ { name_agen : 'Yogi' .....
<span v-text="form.agen.name_agen" /> // output : Yogi
form input // output : yogi
but when i tried to type again on form input yogiabc
thats not show any suggestion so i remove by backspace so the input now is yogi
then i tried select again
the unexpected on twice select :
<span v-text="form.agen.name_agen" /> // output : Yogi // why this result is string
form input // output : object-object // but on form input is an object-object ?
function code:
async onSelectedFrom(option) {
const model = this.form.currentModel
const fieldSuggest = this.form.currentFieldSuggest
const currentLoadData = this.form[`${model}`][`${model}s`]
const currentField = this.form[`${model}`][`${fieldSuggest}`]
this.form[`${model}`] = {
isNew: false,
[`${model}s`]: currentLoadData,
...option.item,
}
console.log('selected', this.form[`${model}`], 'Name Agen:', currentField)
},
searchForm(keyword, uri, model, currentSuggest) {
this.form.currentModel = model
this.form.currentFieldSuggest = currentSuggest
if (keyword) {
clearTimeout(this.timeoutDebounce)
this.timeoutDebounce = setTimeout(() => {
useJwt.http.get(`${uri}`, { params: { keyword, single_search: true } })
.then(response => {
// test debug
if (response.data.total_items === 0) {
// no data
this.form[`${model}`].isNew = true
this.form[`${model}`].user.full_name = null
this.form.isAgenAndTrans = false
this.form[`${model}`][`${model}s`] = []
console.log('no data show', this.form[`${model}`])
} else {
// this.form[`${model}`].isNew = false
this.form.isAgenAndTrans = false
this.form[`${model}`][`${model}s`] = response.data[`${model}s`]
}
}).catch(e => {
this.form[`${model}`].isNew = true
this.form[`${model}`].user.full_name = null
this.form.isAgenAndTrans = false
this.form[`${model}`][`${model}s`] = []
})
}, 300)
}
},
then these the data
data() {
return {
payload: [],
form: {
currentModel: '',
currentFieldSuggest: '',
isAgenAndTrans: false,
agen: {
isNew: true,
id: 0,
name_agen: '',
dm_personal_id: 0,
user: {
full_name: '',
},
agens: [],
},
}
}
}

bootstrap-vue b-table: keep expanded rows expanded on table reload

the expand/collapse part of this works just fine.
Right now I am using javascript startInterval() to reload the table every 2 seconds. Eventually this will be moving to web sockets.
In general, as part of the table load/reload, the system checks to see if it should display the icon " ^ " or " v " in the details column by checking row.detailsShowing, this works fine.
getChevron(row, index) {
if (row.detailsShowing == true) {
return "chevronDown";
}
return "chevronUp";
}
When the user selects the " ^ " icon in the relationship column, #click=row.toggleDetails gets called to expand the row and then the function v-on:click="toggleRow(row)" is called to keep track of which row the user selected. This uses a server side system generated guid to track.
Within 2 seconds the table will reload and the row collapses. On load/reload, in the first column it loads, relationship, I call a function checkChild(row), to check the row guid against my locally stored array, to determine if this is a row that should be expanded on load.
<template #cell(relationship)="row"> {{checkChild(row)}} <\template>
if the row guid matches one in the array I try setting
checkChild(row){
var idx = this.showRows.indexOf( row.item.id);
if(idx > -1){
row.item.detailsShowing = true;
row.rowSelected = true;
row.detailsShowing == true
row._showDetails = true;
}
}
and I am able to see that i have found match, but none of those variables set to true keeps the expanded row open, the row always collapses on reload
anyone have any ideas as to how i can make the row(s) stay open on table reload?
The issue with your code is because of a Vue 2 caveat. Adding properties to objects after they've been added to data will not be reactive. To get around this you have to utilize Vue.set.
You can read more about that here.
However, calling a function like you are doing in the template seems like bad practice.
You should instead do it after fetching your data, or use something like a computed property to do your mapping.
Here's two simplified examples.
Mapping after API call
{
data() {
return {
items: [],
showRows: []
}
},
methods: {
async fetchData() {
const { data } = await axios.get('https://example.api')
foreach(item of data) {
const isRowExpanded = this.showRows.includes(item.id);
item._showDetails = isRowExpanded;
}
this.items = data;
}
}
}
Using a computed
{
computed: {
// Use `computedItems` in `<b-table :items="computedItems">`
computedItems() {
const { items, showRows } = this;
return items.map(item => ({
...item,
_showDetails: .showRows.includes(item.id)
}))
}
},
data() {
return {
items: [],
showRows: []
}
},
methods: {
async fetchData() {
const { data } = await axios.get('https://example.api')
this.items = data;
}
}
}
For a more complete example, check the snippet below.
const {
name,
datatype,
image
} = faker;
const getUser = () => ({
uuid: datatype.uuid(),
personal_info: {
first_name: name.firstName(),
last_name: name.lastName(),
gender: name.gender(),
age: Math.ceil(Math.random() * 75) + 15
},
avatar: image.avatar()
});
const users = new Array(10).fill().map(getUser);
new Vue({
el: "#app",
computed: {
computed_users() {
const {
expanded_rows,
users
} = this;
return users.map((user) => ({
...user,
_showDetails: expanded_rows[user.uuid]
}));
},
total_rows() {
const {
computed_users
} = this;
return computed_users.length;
}
},
created() {
this.users = users;
setInterval(() => {
users.push(getUser());
this.users = [...users];
}, 5000);
},
data() {
return {
per_page: 5,
current_page: 1,
users: [],
fields: [{
key: "avatar",
class: "text-center"
},
{
key: "name",
thClass: "text-center"
},
{
key: "personal_info.gender",
label: "Gender",
thClass: "text-center"
},
{
key: "personal_info.age",
label: "Age",
class: "text-center"
}
],
expanded_rows: {}
};
},
methods: {
onRowClicked(item) {
const {
expanded_rows
} = this;
const {
uuid
} = item;
this.$set(expanded_rows, uuid, !expanded_rows[uuid]);
}
}
});
<link href="https://unpkg.com/bootstrap#4.5.3/dist/css/bootstrap.min.css" rel="stylesheet" />
<link href="https://unpkg.com/bootstrap-vue#2.21.2/dist/bootstrap-vue.css" rel="stylesheet" />
<script src="https://unpkg.com/vue#2.6.12/dist/vue.min.js"></script>
<script src="https://unpkg.com/bootstrap-vue#2.21.2/dist/bootstrap-vue.js"></script>
<script src="https://unpkg.com/faker#5.5.3/dist/faker.min.js"></script>
<div id="app" class="p-3">
<b-pagination v-model="current_page" :per-page="per_page" :total-rows="total_rows">
</b-pagination>
<h4>Table is refresh with a new item every 5 seconds.</h4>
<h6>Click on a row to expand the row</h6>
<b-table :items="computed_users" :fields="fields" bordered hover striped :current-page="current_page" :per-page="per_page" #row-clicked="onRowClicked">
<template #cell(avatar)="{ value }">
<b-avatar :src="value"></b-avatar>
</template>
<template #cell(name)="{ item: { personal_info: { first_name, last_name } }}">
{{ first_name }} {{ last_name }}
</template>
<template #row-details="{ item }">
<pre>{{ item }}</pre>
</template>
</b-table>
</div>

default value of autocomplete with vuetify

so i make an signup and address form for every user and i want every time the user is connected and navigate to the profile page, he will edit his details.
now i have async autocomplete from api that get for me all the items in object format,
so i tried to give the v-model an default value but it didn't change, i guess there is supposed to be connection between the items to the v-model, so i tried to fake the async search and get the items but still couldn't see the default value.
i don't care if the value wont be in the data i just want to see it visual
<script>
export default {
props: {
cmd: {
type: String
},
itemText: {
type: String
},
itemValue: {
type: String
},
label: {
type: String
},
city: {
type: Number
},
street: {
type: Number
},
type: {
type: String
},
defaultValue: {
type: String || Number
}
},
data() {
return {
loading: false,
items: [],
search: null,
select: null
};
},
watch: {
search(val) {
val && val !== this.select && this.querySelections(val);
},
select(val) {
if (val !== this.defaultValue) {
this.$emit("selected", { value: val[this.itemValue], text: val[this.itemText] });
}
}
},
async mounted() {
const defaultSelected = {};
defaultSelected[`${this.itemText}`] = this.defaultValue ? this.defaultValue.value : this.defaultValue;
defaultSelected[`${this.itemValue}`] = this.defaultValue ? this.defaultValue.id : this.defaultValue;
await this.querySelections(defaultSelected[`${this.itemText}`]);
console.log(this.items);
// this.select = defaultSelected[`${this.itemText}`];
},
methods: {
async querySelections(v) {
this.loading = true;
// Simulated ajax query
const data = (await this.$services.SearchService.searchAddress(v, this.cmd, this.city, this.street)).data;
console.log(data);
if (this.type === "city") {
this.items = data.Data[`CitiesRes`];
}
if (this.type === "street") {
this.items = data.Data[`StreetsRes`];
}
if (this.type === "streetNumber") {
this.items = data.Data["NumbersRes"];
}
this.loading = false;
},
asyncinsertDefualtData() {}
}
};
</script>
<template>
<v-autocomplete
v-model="select"
:loading="loading"
:items="items"
:search-input.sync="search"
:item-text="itemText"
:item-value="itemValue"
:value="defaultValue"
return-object
cache-items
flat
hide-no-data
hide-details
solo
:label="label"
></v-autocomplete>
</template>

Filtering a table by using vue multiselect

I'm trying to filter the results of a table by using vue-multiselect. I can see the selected values in the VUE dev tools as a part of multiselect component. How do I use these values to be used in filter() function to get the filtered table results.
Below you can see my JS script implementation and Template multiselect implementation as well.
JS Script
export default {
data: () => ({
policies: [],
selectedValues: [],
options: [],
}),
methods: {
filterByStatus: function({ label, value }) {
return this.policies.filter(data => {
let status= data.status.toLowerCase().match(this.selectedValues.toLowerCase());
},
Template
<multiselect
v-model="selectedValues"
:options="options"
:multiple="true"
label="label"
track-by="label"
placeholder="Filter by status"
#select="filterByStatus"
></multiselect>
Your select component is using the prop :multiple="true", this means the bound value selectedValues, with v-model, will return an array of policy objects.
Instead of using a filterByStatus function in the methods component options, create two computed properties.
One that computes an array of the selected policies statuses and another one that computes the filtered array of policies you want to display.
Script:
computed: {
selectedStatuses() {
const statuses = []
for (const { status } of this.selectedValues) {
statuses.push(status.toLowerCase())
}
return statuses
},
filteredPolicies() {
if (this.selectedStatuses.length === 0) {
return this.policies
}
const policies = []
for (const policy of this.policies) {
if (this.selectedStatuses.includes(policy.status.toLowerCase())) {
policies.push(policy)
}
}
return policies
}
}
Template:
<multiselect
v-model="selectedValues"
:options="options"
:multiple="true"
label="label"
track-by="label"
placeholder="Filter by status"
></multiselect>
Example:
Vue.config.productionTip = Vue.config.devtools = false
new Vue({
name: 'App',
components: {
Multiselect: window.VueMultiselect.default
},
data() {
return {
policies: [{
label: 'Policy A',
status: 'enabled'
}, {
label: 'Policy B',
status: 'disabled'
}, {
label: 'Policy C',
status: 'Deprecated'
}],
selectedValues: [],
options: [{
label: 'Enabled',
status: 'enabled'
}, {
label: 'Disabled',
status: 'DISABLED'
}, {
label: 'Deprecated',
status: 'DePrEcAtEd'
}]
}
},
computed: {
selectedStatuses() {
const statuses = []
for (const {
status
} of this.selectedValues) {
statuses.push(status.toLowerCase())
}
return statuses
},
filteredPolicies() {
if (this.selectedStatuses.length === 0) {
return this.policies
}
const policies = []
for (const policy of this.policies) {
if (this.selectedStatuses.includes(policy.status.toLowerCase())) {
policies.push(policy)
}
}
return policies
}
},
}).$mount('#app')
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<script src="https://unpkg.com/vue-multiselect#2.1.0"></script>
<link rel="stylesheet" href="https://unpkg.com/vue-multiselect#2.1.0/dist/vue-multiselect.min.css">
<div id="app">
<multiselect v-model="selectedValues" :options="options" :multiple="true" label="label" track-by="label" placeholder="Filter by status"></multiselect>
<pre>Policies: {{ filteredPolicies}}</pre>
</div>
It is better to keep the filter function inside computed.
computed:{
filterByStatus: function ({label, value}) {
return this.policies.filter((data) => {
return data.status && data.status.toLowerCase().includes(this.selectedValues.toLowerCase())
});
}
}
Using the filterByStatus in the template section will render the result in real time.
<div>{{filterByStatus}}</div>
you can use watch on selectedValues when any change or selection :
watch:{
selectedValues: function(value){
this.policies.filter(data => {
let status= data.status.toLowerCase().match(this.selectedValues.toLowerCase());
}
}
This how I did in one of my vue3 projects, where I had multiple dropdowns with multi-items can be selected to filter and with a text input box:
const filterByInput = (item: string) =>
item.toLowerCase().includes(state.searchInput.toLowerCase());
const filteredItems = computed(() => {
let fItems = JSON.parse(JSON.stringify(props.items));
if (state.searchInput) {
fItems = fItems.filter((item) => filterByInput(item.title));
}
if (state.filterByState.length) {
fItems = fItems.filter((item) => state.filterByState.includes(item.state));
}
if (state.filterByType.length) {
fItems = fItems.filter((item) => state.filterByType.includes(item.typeId));
}
if (state.filterByPublishing !== null) {
fItems = fItems.filter((item) => item.published === state.filterByPublishing);
}
return fItems;
// NOTE: other options that you can try (should be placed above `return`)
// const filterMultiSelect = (fItems, selectedItems, key) => {
// // OPT-1: foreach
// const arr = [];
// fItems.forEach((item) => {
// if (selectedItems.includes(item[key])) {
// arr.push(item);
// }
// });
// return arr;
// // OPT-2: filter
// return fItems.filter((item) => selectedItems.includes(item[key]));
// };
// if (state.filterByState.length) {
// fItems = filterMultiSelect(fItems, state.filterByType, 'typeId');
// }
});
You can find full code in this gist

Bootstrap-vue - Setting table variant dynamically

So I'm using Bootstrap Vue with this test app. I'm trying to change the variant of a table cell depending on the value of it. Unfortunately, the variant parameter will not take a function, so I'm out of ideas on how to achieve this.
This is my code:
var app = new Vue({
el: '#app',
data: {
items: [], //Will be populated through AJAX
fields: [
{
key: 'Vendedor',
label: 'Vendedor'
},
{
key: 'OBJETIVO',
label: 'Objetivo',
formatter: (value) => { return parseFloat(value).toFixed(2)},
variant: estiloObjetivo //THIS IS NOT WORKING
}
]
},
methods: {
Cargar: function () {
var salesperson = getCookie('salespersonCode');
var url_servicio = 'http://MywebService/';
var self = this;
$.ajax({
type: 'GET',
url: url_servicio + 'ventas/' + salesperson,
dataType: "json", // data type of response
success: function(data){
self.items = data
}
});
},
estiloObjetivo (value) {
if value > 0 //I need my cell variant to change depeding on this value
return 'danger'
else
return 'success'
}
}
})
This is my HTML part:
<div id="app">
<button v-on:click="Cargar">Cargar</button>
<b-table striped hover :fields="fields" :items="items"></b-table>
</div>
Any ideas on how to style a Bootstrap-vue cell dynamically?
This is the way it's done in the docs, it's actually set in the "items" array, but how is this useful in cases like mine where I get the data from a web service?:
{
salesperson: 'John',
Objetivo: 2000,
_cellVariants: { salesperson: 'success', Objetivo: 'danger'}
},
So I guess what I need is a way to set the I need is to set the _cellVariants parameter of each element in the 'items' array.
You likely need a computed property. Computed properties automatically update on changes to the reactive variables that they depend on.
The following example implements a computed property, styledItems, which you must use in place of items in the template. It returns a 1-deep copy of items, i.e. a new array containing a copy of each item, with the extra _cellVariants property added.
new Vue({
data: {
items: [ /* your data here */ ]
},
methods: {
estiloObjetivo: value => (value > 0) ? 'danger' : 'success'
},
computed: {
styledItems() {
return this.data.map(datum =>
Object.assign({}, datum, {
_cellVariants: {
Objetivo: this.estiloObjetivo(datum.Objetivo)
}
})
}
})
If you want to add variant to items you could use a computed property called cptItems and define it as follows:
computed:{
cptItems(){
return this.items.map((item)=>{
let tmp=item;
item.OBJETIVO>0?tmp.variant='danger':tmp.variant='success';
return tmp;
})
}
and use that property inside your template like :
<b-table .... :items="cptItems"></b-table>
I was sure the answers above would solve my own issue but they did not. I found a different way to color table cells: https://github.com/bootstrap-vue/bootstrap-vue/issues/1793
This is aside from using variants to color a table cell. Instead, we utilize tdclass and a function.
<script>
new Vue({
el: '#itemView',
data() {
return {
fields: [
{
key: 'Objetive',
sortable: true,
thClass: 'text-nowrap',
tdClass: (value, key, item) => {
return 'table-' + this.getColor(item);
}
}
],
};
},
methods: {
getColor(item) {
return item.Objetive > 0 ? 'danger' : 'success';
},
},
});
</script>
For my own use-case, I needed to compare two cells of the same row, then apply a class to one.
...
{
key: 'DEMAND_QTY',
sortable: true,
thClass: 'text-nowrap',
tdClass: (value, key, item) => {
return 'table-' + this.demandStatusColor(item);
},
},
{ key: 'TOTAL_DEMAND', sortable: true, thClass: 'text-nowrap' },
],
};
},
methods: {
demandStatusColor(item) {
return item.DEMAND_QTY < item.TOTAL_DEMAND ? 'danger' : 'success';
},
}
...
Perhaps this will help someone, if not OP.
#John answer worked for me. I don't have enough reputation to make comment or useful
tdClass: (type, key, item) => {
switch (type) {
case "value":
return "bg-warning text-white";
break;
case "value":
return "bg-danger text-white";
break;
case "value":
return "bg-info text-white";
break;
default:
break;
}
},

Categories

Resources