Get data from nested json and display using Vue-Multiselect - javascript

Basically what I want to achieve is GET data from server and display the data in the nested json to display in Multiselect, Vue-Multiselect
After displaying, i'm able to add new tags to it if i want (Being able to update it).
I'm able to get the objects from the nested json to display in multiselect, but I'm not sure how to customize it to only show the name.
Current behaviour:
So expected behaviour would be, only Sofa, Table and Chair should be shown in the multiselect:
Is there a way for me to only display like the picture above?
After implementing #Ikbel's way of getting the json object and showing only the required name. Now I have another problem which is I get duplicates of the options whenever I add a new tag to it.
This is my Vue Code:
<template>
<multiselect :multiple="true"
v-model="data.subCategoryNames"
:hide-selected="true"
:selected="data.subCategoryNames"
:options="computedSubCategoryNames"
:taggable="true"
#tag="addTag"
>
</multiselect>
</template>
methods: {
addTag (newTag) {
// this.options.push(newTag)
this.data.subCategoryNames.push(newTag)
}
}
computed: {
computedSubCategoryNames () {
return this.allSubCategoryNames.map((item) => {
this.options.push(item.subCategoryName)
this.data.subCategoryNames.push(item.subCategoryName)
return item.subCategoryName
})
}
}
Which shows this:
Thank you for your help!

Ok #mary. Here is a better solution. Simply add label="subCategoryName" to your multiselect component to make it show subCategoryName only instead of the whole object. So no need for a computed property.
track-by should be used to avoid duplicate values.
Here is a working example.
let Multiselect = VueMultiselect.Multiselect
let vm = new Vue({
el: '#app',
data: {
data: {
subCategoryNames: [],
options: [
{subCategoryName: 'Chair', count: 2},
{subCategoryName: 'Table', count: 5},
],
},
},
components: {
Multiselect
},
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.4.2/vue.min.js"></script>
<script src="https://unpkg.com/vue-multiselect#2.0.0"></script>
<link rel="stylesheet" href="https://unpkg.com/vue-multiselect#2.0.0/dist/vue-multiselect.min.css">
<div id="app">
<multiselect :multiple="true"
v-model="data.subCategoryNames"
:hide-selected="true"
:selected="data.subCategoryNames"
:options="data.options"
:taggable="true"
label="subCategoryName"
track-by="subCategoryName"
>
</multiselect>
{{data.subCategoryNames}}
</div>

Use a computed property to extract the subcategory names. Here is how you can do it.
Bind your multiselect options to a computed subCategoryNames instead. Let's call it computedSubCategoryNames, and use the array method map() to extract subCategoryName from subCategoryNames. Here is an example:
<multiselect :options="computedSubCategoryNames">
</multiselect>
And in define computedSubCategoryNames:
computed: {
// Returns ['Chair', 'Sofa', 'Table']
computedSubCategoryNames() {
return this.subCategoryNames.map(function(item) {
return item.subCategoryName
})
}
}

Please refer below URL, onether approach to show multiselect drop down in android
https://asnehal.wordpress.com/2012/04/03/multi-select-drop-down-list-in-android/

Related

Using embedded attributes in Vuetify Calendar

I'm trying to use the v-calendar component from Vuetify.
I saw in the documentation I can use the event-start prop if my events don't have the same attributes' names.
The problem is that my events have embedded attributes and I don't know if event-start handles this case.
My events :
events: [
{
id: 'b9d93291-6d95-47b9-994a-ee9f266fb6b8',
type: 'reservation_item',
attributes: {
start_date: '2020-09-23T00:00:00.000Z',
end_date: '2020-09-25T00:00:00.000Z',
},
},
]
The events example from vuetify :
events: [
{
name: 'Weekly Meeting',
start: '2020-09-07 09:00',
end: '2020-09-07 10:00',
},
],
I tried to do something like that but it doesn't work.
<v-calendar
ref="calendar"
locale="fr-fr"
:now="today"
:value="today"
:events="events"
event-start="attributes.start"
color="primary"
type="month"
></v-calendar>
After spelunking the source code for the vuetify plugin, the latter expects that the value be present in the event object, as a direct property. So you cannot acces other nested "children", it has to be a direct property.
There are two alternatives to make this work:
1- map your events array by moving the properties inside attributes to the root of your object then pass this prop to v-calendar : event-start="startDate"
2- Create a javascript class (MyEvent) with a fromJson method that take the raw JSON from your API ( this way you encapsulate the JSON into domain objects) and return an array of MyEvent instances. this way you can do for example : events[0].start and you don't even have to pass it as a value to the event-start prop, since by default it expects a start attribute as a default value.
Another advantage of this alternative, is that since the event is now encapsulated into its own javascript class, you can add helper methods, or getters/setter or any logic that would otherwise be inside your "view" logic, and contribute to have a better separation of concerns.
To make this works, I had to change my events data
<template>
<v-calendar
ref="calendar"
locale="fr-fr"
:events="myEvents"
event-start="start"
color="primary"
type="month"
></v-calendar>
</template>
data: () => ({
events: [
{
id: 'b9d93291-6d95-47b9-994a-ee9f266fb6b8',
type: 'reservation_item',
attributes: {
start_date: '2020-09-23T00:00:00.000Z',
end_date: '2020-09-25T00:00:00.000Z',
},
},
],
}),
computed: {
myEvents() {
const reservations = this.reservations
reservations.forEach((element) => {
element.start = element.attributes.start_date
element.name = 'test'
element.end = element.attributes.end_date
})
return reservations
},
}

How to assign a random value to a data attribute from a given array during v-for in Vue.js?

On my main Vue instance I have a data attribute that is an array. It has 4 items:
var app = new Vue({
el: '#app',
efeitosAos: ['fade', 'flip-up', 'slide-up', 'zoom-in'],
aos: 'fade-in'
}
As you can see, it has names of effects from the AOS library. My idea is to get a random value from it and pass it to one of my components.
Each component is called inside a v-for:
<post v-for="post, index in posts" :key="post.id" :post="post" :efeito="aos">
{{randomizaAos()}}
</post>
This is my component:
const post = {
data: function(){
return {
duration: 1000,
delay: 50,
}
},
props: {
efeito: '',
post: {
id: '',
titulo: '',
resumo: '',
imagem: '',
},
},
template: '\
<section class="guarda-post" :data-aos="efeito" :data-aos-duration="duration" :data-aos-delay="delay">\
<img :src="post.imagem" class="img-fluid">\
<h4 class="titulo-post">{{post.titulo}}</h4>\
<p>{{post.resumo}}</p>\
</section>'
};
On my component, I have a prop called efeito. It should receive the value of my data aos, that comes from my main Vue instance. My idea is to use a method to change this aos data. So far, I got this:
methods:{
randomizaAos: function(){
var efeitoAleatorio = Math.floor(Math.random() * this.efeitosAos.length);
this.aos = this.efeitosAos[efeitoAleatorio];
console.log(this.aos);
}
}
The problem is that when I run the randomizaAos method, I get an infinite loop, and I don't know why. It's weird, because if I leave only the console.log inside the method, I get 4 messages, that is precisely the size of my posts array. But when I use the Math stuff and give to my aos data the value found, I get the infinite loop. Why is that? Any help?
Set the value of the efeito prop in the mounted method, so it won't be re-renderized everytime

Vue-tables-2 detecting which filter was applied

I am trying to set up a basic server side vue-tables-2 with two filters - one is a dropdown and the other is a search field. I am having trouble detecting which of the two filters were applied within the requestFunction() so I can send a request over to the server. Currently, I am just trying to console log the input filter name and value as the filter is applied / input is changed.
JSFiddle:
https://jsfiddle.net/kbpq5vb3/39/
HTML
<h1 class="vue-title">Vue Tables 2 Demo</h1>
<div id="app">
<v-server-table url="https://jsonplaceholder.typicode.com/users" :columns="columns" :options="options"></v-server-table>
</div>
VueTable:
Vue.use(VueTables.ServerTable);
new Vue({
el: "#people",
data: {
columns: ['name', 'username'],
options: {
requestAdapter(data) {
console.log(data.query); // detect which filter was applied / which input changed
},
responseAdapter(resp) {
return {
data: resp,
count: resp.length
}
},
filterByColumn: true,
filterable: ['name', 'username'],
listColumns: {
name: [{
id: 'ervin',
text: 'Ervin'
}, {
id: 'chelsey',
text: 'Chelsey'
}]
}
}
}
});
According to the vue-tables-2 documentation:
vue-tables.filter/tableName/FILTER fires off when a filter is changed.
After looking a bit deeper, it really is as easy as listening for an event:
Vue developer tools really make this type of stuff easy to diagnose. Now, you'd want to listen to the custom event. You can learn how to do that over on the Vue documentation.
Finally, here is a working fiddle that shows how you listen to these events: https://jsfiddle.net/kbpq5vb3/55/
Hope this helps!

How do you process an array outside the vue instance and assign the processed data to a (currently empty) array inside the vue instance?

I have an array of blog post inside a script tag on the page that looks like this:
var posts = [
{article_name: "name", category: "test".....},
{article_name: "name2", category: "test2".....},
{...}
]
There are 30 objects in each array. I need to grab the categories in each object and assign it to the data prop in a vue instance. I have an empty array inside the vue instance like:
var blog_posts_nav = new Vue({
el: '#blog-posts-nav',
data: {
tags: []
}
})
I want to have each individual "category" in the 30 objects mapped to the "tags" array in the data prop. I tried doing it via the created() hook but it seems that the created hook can't access data? I tried:
created() {
posts.forEach( function (item) {
this.tags.push(item.category)
});
}
But I get an error in the console that says tags is undefined. Any help how I would deal with this? Basically I want to do work on a set of data and assign it to an array inside vue before outputting it to the page and in a manner that vue can interact with the data.
So first and foremost, is the created() hook (or any hook) the best way to go about this? Or should I be using something like methods or computed?
What is the "best practices" way and how would I go about achieving this?
It's not the created() method that can't access this, it's the inner context of forEach.
You can capture this to a local constant first, then access it inside the loop.
console.clear()
var posts = [
{article_name: "name", category: "test" },
{article_name: "name2", category: "test2" },
]
Vue.component("tags",{
template:`
<div>
{{tags}}
</div>
`,
data(){
return {
tags: [],
}
},
created() {
const vm = this;
console.log('created', vm.tags)
posts.forEach( function (item) {
vm.tags.push(item.category)
console.log('created, posts.forEach', vm.tags)
});
}
})
new Vue({
el: "#app"
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.3/vue.js"></script>
<div id="app">
<tags></tags>
</div>

Vue.js bind object properties

Why I can't bind the object properties in Vue? The object addr is not reactive immediately, but test is reactive, how come? In this case, how should I bind it?
HTML
<div id="app">
<input type="text" id="contactNum" v-model="addr.contactNum" name="contactNum">
<input type="text" id="test" v-model="test" name="test">
<br/>
{{addr}}<br/>
{{addr.contactNum}}<br/>
{{test}}
</div>
Javascript
var vm = new Vue({
el: '#app',
data: {
addr: {},
test: ""
}
});
Jsfiddle
During initialisation Vue sets up getters and setters for every known property. Since contactNum isn't initially set up, Vue doesn't know about that property and can not update it properly. This can be easly fixed by adding contactNum to your addr object.
var vm = new Vue({
el: '#app',
data: {
addr: {
contactNum: "" // <-- this one
},
test: ""
}
});
The above is called reactivity in Vue. Since Vue doesn't support adding properties dynamically to its reactivity system, we may need some kind of workaround. A possible solution is provided by the API. In case of dynamically added properties we can use Vue.set(vm.someObject, 'b', 2).
Doing so the markup would need to get some update. Instead of using v-model it'd be better to use an event listener like #input. In this case our markup could look like this.
<input type="text" id="contactNum" #input="update(addr, 'contactNum', $event)" name="contactNum">
So basically the function will get triggered every time the input elements value changes. Obviously doing so will also require some adjustments on the JS part.
var vm = new Vue({
el: '#app',
data: {
addr: {},
test: ""
},
methods: {
update: function(obj, prop, event) {
Vue.set(obj, prop, event.target.value);
}
}
});
Since Vue triggers Vue.set() on any reactive element, we simply call it on our own because Vue doesn't recognizes a dynamically added property as a reactive one. Of course, this is only one possible solution and there may be lots of other workarounds. A fully working example can be seen here.
As per my comments, there are several things that you want to consider:
The reason why your code is not working is due to the inherent inability of JS to watch for changes in object properties. This means that even though addr is reactive, any properties added to addr that is not done when it is declared will make it non-reactive. Refer to the VueJS docs for more details: https://v2.vuejs.org/v2/guide/reactivity.html#Change-Detection-Caveats
If you are going to have an arbitrary number of input fields, you are probably better of composing a custom input component, and simply use v-for to iteratively inject input fields based on the number of input fields you have.
Now back to the second point, if you know what fields addr will have, you can simply declare it in your app. We create a new updateFormData method, which is called by the component:
data: {
addrFields: ['contactNum', ...],
addr: {},
test: ""
},
methods: {
updateFormData: function(id, value) {
this.$set(this.addr, id, value);
}
}
We can still store your form data in the addr object, which will be updated by the updateFormData method based on the received payload using .$set(). Now, we can then create a custom Vue component for your input element.
In the example below, the component will iterate through all your addrFields, and pass down the addrField as a prop using :id="addrField". We also want to make sure that we capture the custom-named updated event emitted from within the component.
<my-input
v-for="(addrField, i) in addrFields"
:key="i"
:id="addrField"
v-on:inputUpdated="updateFormData"></my-input>
The template can look something like the following. It simply uses the id prop for both its id, name, and placeholder attribute (the latter for easy identification in the demo). We bind the #change and #input events, forcing it to trigger the updated callback:
<script type="text/template" id="my-input">
<input
type="text"
:id="id"
:name="id"
:placeholder="id"
#input="updated"
#change="updated">
</script>
In the component logic, you let it know that it will receive id as a prop, and that it should emit an inputUpdated event using $.emit(). We attach the ID and value as payloads, so that we can inform the parent what has updated:
var myInput = Vue.component('my-input', {
template: '#my-input',
props: {
id: {
type: String
}
},
methods: {
updated: function() {
this.$emit('inputUpdated', this.id, this.$el.value);
}
}
});
With the code above, we have a working example. In this case, I have created an arbirary array of input fields: contactNum, a, b, and c:
var myInput = Vue.component('my-input', {
template: '#my-input',
props: {
id: {
type: String
}
},
methods: {
updated: function() {
this.$emit('updated', this.id, this.$el.value);
}
}
});
var vm = new Vue({
el: '#app',
data: {
addrFields: ['contactNum', 'a', 'b', 'c'],
addr: {},
test: ""
},
methods: {
updateFormData: function(id, value) {
this.$set(this.addr, id, value);
}
}
});
<script src="https://unpkg.com/vue#2.1.3/dist/vue.js"></script>
<div id="app">
<my-input
v-for="(addrField, i) in addrFields"
:key="i"
:id="addrField"
v-on:updated="updateFormData"></my-input>
<input type="text" id="test" v-model="test" name="test" placeholder="test">
<br/>
<strong>addr:</strong> {{addr}}<br/>
<strong>addr.contactNum:</strong> {{addr.contactNum}}<br />
<strong>test:</strong> {{test}}
</div>
<script type="text/template" id="my-input">
<input
type="text"
:id="id"
:name="id"
:placeholder="id"
#input="updated"
#change="updated">
</script>
Edit your Vue data with this since it's getter and setter methods are not set up. Also, check out Declarative Reactive Rendering on Vue docs here:
data: {
addr: {
contactNum: "" // <-- this one
},
test: ""
}

Categories