Ant Design Vue Select Display Existing Items - vue.js

Using Ant Design in Vue. In a Multi-select component, user is able to select and unselect associated objects. However, the existing tokens of preexisting associated objects should show the item.name, but instead show "undefined". It also does not show a checkmark next to existing objects in the select box. However, on submit, the "undefined" object is submitted correctly. New choices display within the select box correctly.
Here is the element in the view:
<a-form-item :label="`${contact.name}'s Outlets`"
:wrapperCol="{lg: {span: 20}, sm: {span: 24} }">
<a-select
mode="multiple"
v-model="contact.outlets"
style="width: 100%"
placeholder="Please select"
>
<a-select-option v-for="outlet in outlets" :key="outlet.name" :value="outlet.id">
{{ outlet.name }}
</a-select-option>
</a-select>
</a-form-item>

As indicated in comments, you used an object element in contact.outlets[] for the initial value of the select. However, the a-select's value properties are bound to each element's id, so no match is found:
<a-select-option
v-for="outlet in outlets"
:key="outlet.name"
:value="outlet.id" 👈 option value is an `id`
>
Solution
Instead of the object as initial value, use the object's id.
Composition API
export default {
setup() {
const outlets = reactive([
{ id: 1, name: 'Outlet A' },
{ id: 2, name: 'Outlet B' },
👇
{ id: 3, name: 'Outlet C' },
]); 👇
const contact = reactive({ name: 'John', outlets: [3] });
return { outlets, contact };
}
}
demo 1
Options API
export default {
data() {
const outlets = [
{ id: 1, name: 'Outlet A' },
{ id: 2, name: 'Outlet B' },
👇
{ id: 3, name: 'Outlet C' },
]; 👇
const contact = { name: 'John', outlets: [3] };
return { outlets, contact };
}
}
demo 2

Related

Data Object unable to summation inside the computed properties in VUE js 3

Let see the following code segment in vue.js 3,
<template>
<div>
<h2>Computed Total - {{getPrice()}}</h2>
</div>
</template>
<script>
export default {
name: "ComputedProperties",
data(){
return{
items: [
{
id: 1,
title: 'TV',
price: 100,
},
{
id: 2,
title: 'Phone',
price: 200,
},
{
id: 3,
title: 'Laptop',
price: 300
}
]
}
},
computed: {
getPrice(){
return items.reduce((total, curr) => (total = total + curr.price), 0)
}
}
}
</script>
After run this code it is showing, error 'items' is not defined no-undef
As we know that computed property doesn't need to declarer any argument. Then what's wrong with the code?
There are few problems with your code:
When using computed property, don't use () - it is a property, not a function
<h2>Computed Total - {{ getPrice }}</h2>
All members of data, computed or methods when used in the script part of the Vue component, must be referenced with this ...for example this.items
computed: {
getPrice() {
return this.items.reduce((total, curr) => total + curr.price, 0)
}
}

Unable to populate data array for VueJS Calendar plugin with dynamic Vuex store data

I have a Calendar Component in a VueJS app generated by the vue-sweet-calendar plugin. The component renders properly using the static data, but when I comment that data out (as seen in the code below) and try to fill the array with real data, the array generated is empty.
Here is the component:
<template>
<div id="calendar-card">
<!-- Using Component -->
<calendar
:eventCategories="eventCategories"
:events="events"
ref="calendar"
/>
</div>
</template>
<script>
// Importing Component and style
import { Calendar } from 'vue-sweet-calendar'
import 'vue-sweet-calendar/dist/SweetCalendar.css'
import ActsService from '#/services/ActsService'
export default {
name: 'CalendarCard',
data() {
return {
eventCategories: [
{
id: 1,
title: 'Personal',
textColor: 'white',
backgroundColor: 'Blue'
},
{
id: 2,
title: 'Company-wide',
textColor: 'white',
backgroundColor: 'red'
}
],
events: [
// SAMPLE Calendar data commented out
// {
// title: 'Event 1',
// start: '2019-10-02',
// end: '2019-10-02',
// repeat: 'never',
// categoryId: 2
// },
// {
// title: 'Event 2',
// start: '2019-10-08',
// end: '2019-10-08',
// repeat: 'never',
// categoryId: 2
// },
// {
// title: 'Event 3',
// start: '2019-10-10',
// end: '2019-10-10',
// repeat: 'never',
// categoryId: 2
// },
// {
// title: 'Event 4',
// start: '2019-10-23',
// end: '2019-10-23',
// repeat: 'never',
// categoryId: 2
// }
]
}
},
computed: {
userActs() {
return this.$store.getters.userActs
},
},
mounted() {
this.$store.dispatch("getUserActs");
this.setEvents();
},
methods: {
goToday() {
this.$refs.calendar.goToday()
},
setEvents() {
console.log('setting events using these userActs: ', this.$store.state.userActs)
this.$store.state.userActs.forEach(function(item) {
events.push({
item: {
title: item.deed,
start: item.created_at,
end: item.created_at,
repeat: 'never',
categoryId: 2
}
})
})
console.log('these are the calendar events', this.events);
}
},
components: {
Calendar // Registering Component
}
}
</script>
In the Chrome Vue console, I can see state.userActs populated with 4 Act objects, however, console.log(this.$store.state.userActs) returns an array with 0 elements.
In the Chrome Vue console, I can see state.userActs populated with 4 Act objects, however, console.log(this.$store.state.userActs) returns an array with 0 elements.
This sounds like your Vuex store is populated / updated after your component is mounted.
I would suggest creating a computed property for events so that it automatically updates when new store data appears.
For example
computed: {
userActs() {
return this.$store.getters.userActs
},
events() {
return this.userActs.map(item => ({
title: item.deed,
start: item.created_at,
end: item.created_at,
repeat: 'never',
categoryId: 2
}))
}
}
Also remove events from your data. You will no longer need a setEvents() method either.

Expand and collapse treeview using button | Vuetify

I got some problem with Vuetify treeview component. My goal is:
When I select some treeview's element and press expand /collapse button I want to see all children for this element, and then when I press the button once again I want to collapse all selected element.
Here's my code:
<template>
<v-container fluid>
<v-btn justify-center #click="expandCollapse"> Expand or collapse </v-btn>
<v-treeview
class="ml-4"
v-model="tree"
:open="items"
:items="items"
activatable
item-key="name"
>
<template slot="prepend" slot-scope="{ item }">
<v-list-tile-avatar
size="30"
style="min-width: 40px;"
tile
>
<img :src="imageType(item.type)" alt=""/>
</v-list-tile-avatar>
</template>
</v-treeview>
</v-container>
</template>
<script>
export default {
data: () => ({
items: [
{
name: 'Factory A',
type: 'board',
children: [
{
name: 'Line 1',
children: [{
name: 'Machine ABC',
type: 'machine'
}],
type: 'board'
},
{
name: 'Line 2',
children: [{
name: 'Machine ABC 02',
children: [{
name: 'Part A',
type: 'part'
},
{
name: 'Part B',
type: 'part'
},
{
name: 'Part C',
type: 'part'
},
{
name: 'Part D',
type: 'part'
}
],
type: 'machine'
}],
type: 'board'
},
{
name: 'Line 3',
children: [{
name: 'Machine ABC 03',
type: 'machine'
}],
type: 'board'
},
{
name: 'Line 4',
children: [{
name: 'Machine ABC 04',
type: 'machine'
}],
type: 'board'
}
]
}
]
}),
methods: {
imageType (type) {
switch (type) {
case 'board':
return require('#/assets/images/board.svg')
case 'machine':
return require('#/assets/images/machine.svg')
case 'part':
return require('#/assets/images/part.svg')
}
},
// ADDED
bfs: function (tree, key, collection) {
if (!tree[key] || tree[key].length === 0) return
for (var i = 0; i < tree[key].length; i++) {
var child = tree[key][i]
collection.push(child)
this.bfs(child, key, collection)
}
},
expandCollapse (item) {
const childs = []
const selectedIDs = []
childs.push(item)
this.bfs(item, 'children', childs)
}
}
}
</script>
Ok, I got the solution. I just added two if-statements to function.
if (this.open.indexOf(selectedIDs[0]) === -1) {
this.open = this.open.concat(childs.map(node => node.id))
} else {
this.open = this.open.filter((item) => !selectedIDs.includes(item))
}
Use the open and active event listeners to update the open/closed items.
To get the active items use the update:active event.
To get the open items use the update:open event.
on expandCollapse use the active items and the open items to determine whether to open or close, and then update the open to reflect the change. This part is just iterating through the items and running a comparing to active and open

VueJS Virtual field on array item

I am building something with VueJS and I have some problem to select an item in a list:
Let's imagine the following VueJS component:
new Vue({
el: '#app',
data: {
list: [
{
id: 1,
title: 'My first Item',
selected: false
},
{
id: 2,
title: 'My second Item',
selected: false
}
]
}
})
With the selected property, I can apply a class or not to the item:
<div id="app">
<ul>
<li #click="item.selected = !item.selected" :class="item.selected ? 'active' : ''" v-for="item in list">{{ item.title }}</li>
</ul>
</div>
But now, let's imagine that I grab my data from an API, I still want to be able to select the items:
new Vue({
el: '#app',
data: {
list: []
},
created: function () {
// Let's imagine that this is an Ajax Call to a webservice
this.$set('list', [
{
id: 1,
title: 'My first Item'
},
{
id: 2,
title: 'My second Item'
}
])
}
})
Now, my html can't work anymore because the data has not a selected property.
So how could I do such a thing?
Here are two JsFiddle that explain the problem:
The working one
The non working one
Please read docs on vue lifecycle :
id prefer you set the list to be a computed property that always checks for returned items : ie ,
new Vue({
el: '#app',
data: {
list: []
},
computed: {
list (){
// Let's imagine that this is an Ajax Call to a webservice
let returned = [
{
id: 1,
title: 'My first Item'
},
{
id: 2,
title: 'My second Item'
}
]
return returned
}
}
})

VueJS filterBy with dynamic argument

I have a VueJS object with data that looks like the following:
phases: [
{ id: 1, ... },
{ id: 2, ... },
{ id: 3, ... },
],
roles: [
{ phaseId: 1, title: "Account Manager" },
{ phaseId: 1, title: "Creative Director" },
{ phaseId: 2, title: "Account Manager" },
{ phaseId: 2, title: "Creative Director" },
]
And my v-for loop looks like the following:
<article v-for="phase in phases" class="phase">
... Other content ...
<div v-for="role in roles | filterBy phase.id in 'phaseId' ">
<p>${role.title}</p>
</div>
</article>
I'm trying to filter the roles array to only show roles with the "parent" phase given by the ID. Since the roles for-loop will be running multiple times, each time it will only show the appropriate roles.
Is there any way to accomplish this?
+1 #gurghet, filters are deprecated.
Use a method
data: {
phases: [
{ id: 1, ... },
{ id: 2, ... },
{ id: 3, ... },
],
roles: [
{ phaseId: 1, title: "Account Manager" },
{ phaseId: 1, title: "Creative Director" },
{ phaseId: 2, title: "Account Manager" },
{ phaseId: 2, title: "Creative Director" },
]
},
methods: {
filtered(id){
return this.roles.filter(rol => rol.phaseId === id)
}
}
Template:
<article v-for="phase in phases" class="phase">
... Other content ...
<div v-for="role in filtered(phase.id) ">
<p>${role.title}</p>
</div>
</article>
Exmaple
One can use a mixin that calls the actual filter
1. First we need to register a filter globally
Vue.filter('normalizetext', function (value) {
if (!value) return '';
value = value.toString();
value = value.replace('_', ' ').toLowerCase();
return value.charAt(0).toUpperCase() + value.slice(1);
})
2.Create a mixin
import Vue from 'vue';
export default {
/**
* The methods that the mixin used for filer functions.
*/
methods: {
/**
* Method used to check if specific element is found in an filer
*
* #param {filer} filtername filer to find created filter
*
* #returns {any} filtered output
*/
dynamicFilter(filterName, value) {
return Vue.filter(filterName)(value);
}
}
};
3.Calling filter
<template>
{{
dynamicFilter(data.FilterName, value)
}}
</template>