how to integrate react-select with mobx? - mobx

I'm working with react-select, to add a multi-select control in my web-app. http://jedwatson.github.io/react-select/
I'm looking for a good example to inject mobx observables into the control.
The first challenge I have is initialising the options in an async way (options are fetched from server, but after that I want regular filtering to apply, no need for async-options).
Any good examples out there?
import Select from 'react-select';
<Select
multi
disabled={this.state.disabled}
value={this.state.value}
placeholder="Select your favourite(s)"
options={this.state.options}
onChange={this.handleSelectChange}
/>

As noted in MobX documentation, due to limitations in ES5, ObservableArray still has some quirks. react-select is one of those libraries that don't work with ObservableArray out of the box. There are 2 ways to solve this.
Method 1
you already have an observable array with label and value
#observable myOptions = [
{label: 'foo', value: 123},
{label: 'bar', value: 345}
]
then you can use the ObservableArray's .peek() method to return a regular array for read-only purposes
<Select
options={this.myOptions.peek()}
onChange={this.handleSelectChange}
/>
Method 2
You have the observable array in some other format, returned from server. This is a more common case, actually.
#observable peopleData = [
{name: 'foo', _id: 123, address: 'fooville'},
{name: 'bar', _id: 345, address: 'barville'}
]
in this case, you can create a #computed value, which also returns a plain array:
#computed get peopleOptions() {
return this.peopleData.map(x => ({ label: x.name, value: x._id })
}
and connect the Select component to the computed value, which will auto-update each time new data from server are fetched.
<Select
options={this.peopleOptions}
onChange={this.handleSelectChange}
/>
in both cases, this.handleSelectChange should just update the original data.

Related

Vuetify / Vue (2) data table not sorting / paginating upon new bound prop

Keeping the table as basic as possible to figure this out. I am struggling to learn how to create server side sorting/pagination to function in vuetify. When I add the :options or :server-items-length bind the table no longer sorts or paginates.
Without either of those, I get a default listing of 10 items per row - and the sorting all works perfectly fine as well the pagination. However, parameters in the api require a page item count thus forcing a hardcoded number or using the :options bind. If i just place a hard coded number things work fine, but when I bind I get proper items per page but no sorting and pagination.
Very simple data table:
<v-data-table
:items="ItemResults.items"
:headers="TableData.TableHeaders"
:loading="TableData.isLoading"
>
</v-data-table>
//Base data returns, with headers and options as well the array that items are stored in.
data() {
return {
ItemResults:[],
TableData: {
isLoading: true,
TableHeaders: [
{ value: "title", text: "Title" },
{ value: 'artist', text: 'Artist' },
{ value: 'upc', text: 'UPC' },
{ value: "retailPrice", text: "Price/Quantity"},
],
},
options:
{
page: 1,
itemsPerPage: 15
},
}
},
//Then last, my async method to grab the data from the api, and place it in the itemresults array.
async getProducts(){
this.TableData.isLoading = true;
const { page, itemsPerPage } = this.options;
var temp = await this.$axios.get(`Inventory/InventoryListing_inStock/1/${page}/${itemsPerPage}`);
this.ItemResults = temp.data;
this.TableData.isLoading = false;
return this.ItemResults;
},
I have tried following Vuetify Pagination and sort serverside guide, but I'm not sure where they are recommending to make the axios call.
The lead backend dev is working on setting a sorting function up in the api for me to call paramaters to as well - Im not sure how that will function along side.
but I dont know how to have this controlled by vuetify eithier, or the best practice here.
EDIT:
I've synced the following:
:options.sync="options"
:sort-by.sync="sortBy"
:sort-desc.sync="sortDesc"
but i think I dont need to sync the last two. My options:
options:
{
page: 1,
itemsPerPage: 15,
sortBy: ['title'],
sortDesc: [false]
},
and in my data I put the array for sort by and sort desc
sortBy: [
'title', 'artist', 'upc', 'retailPrice'
],
sortDesc:[true, false],
pagination is now working, and sort ascending is now working, but when I click to descend the header I get an error that the last two params are empty on update to / / instead of /sortBy/sortDesc result. So its not listing the values on changes.
When your component mounts, you need to fetch the total number of items available on the server and the first page of data, and set these as :items and :server-items-length. The latter will (as you observed) disable paging and sorting, as your server will take care of it, and it is used to calculate the total number of pages given a selected page size.
Now you need to know when to reload data. For this, you either bind options to the v-data-table with two-way binding (.sync) and set a watcher on it, or you listen to the update:options event. And whenever options change, you fetch the selected data from the server.
That's basically all it takes.
In your template:
<v-data-table
:items="items"
:headers="headers"
:loading="true"
:server-items-length="numberOfItemsOnServer"
#update:options="options => loadPage(options)"
/>
In your component:
methods: {
async loadPage(options){
this.items = [] // if you want to show DataTable's loading-text
this.items = await fetch('yourUrl' + new URLSearchParams({
// build url params from the options object
offset: (options.page - 1) * options.itemsPerPage,
limit: options.itemsPerPage,
orderBy: options.sortBy.map((sb,ix) => [sb, options.sortDesc[ix] ? 'desc' : 'asc'])
}))
}
}

Vuetify v-treeview doesn't delete node

I am a beginner in vuejs and vuetify and I try to add and remove nodes in a treeview.
When I do a .push(item) the treeview updates but when I delete an item from my array, the treeview does not update.
Here is an example of my code
My treeview
<v-treeview
[...]
:items="formatedItems"
item-key="slug"
[...]
>
[...]
</v-treeview>
formatedItems is declared in the data like this :
data: () => ({
[...]
formatedItems: [{
id: 0,
slug: null,
name: i18n.t(****),
children: []
}]
[...]
}),
This code correctly adds a child in my treeview :
this.formatedItems[0].children.push({
id: element.id,
slug: element.slug,
name: element.name,
children: []
});
On the other hand, this code removes the item from my array but doesn't update the treeviews :
_.remove(this.formatedItems[0].children, function(n) {
return n.slug == element.slug;
});
this.formatedItems = this.formatedItems; // I tried this to trigger a potential magical update of vuejs but nothing...
I don't know what to try anymore, that's why I come to ask you the question:
.push being a build-in function and knowing that I use a lodash function for deletion, is there something I didn't understand about the use of vuejs that causes my treeview not to be updated?
Thank you for your attention
Looks like the problem somewhere else. I've tried a basic example and all works fine.
Except I used filter method for deletion.
codepen.io/DavidGolodetsky/pen/eYZMbgm

Prop in template not showing with VueJS

I've been trying to pass a prop in a component's template.
I think I'm missing some points here, and I didn't start to include my components in single files yet.
app.js
Vue.component('chat-response', {
props: ['response', 'senderClass'],
template: '<div>From {{ senderClass }} : {{ response.text }}</div>'
})
var app = new Vue({
el: '#app_chat',
data: {
responseList: [
{ id: 0, text: 'Response 1', type: 'Owner' },
{ id: 1, text: 'Response 2', type: 'Other' },
{ id: 2, text: 'Response 3', type: 'None' }
]
}
})
page.html
...
<chat-response v-for="response in responseList"
v-bind:key="response.id"
v-bind:response="response"
v-bind:senderClass="response.type"></chat-response>
...
Output :
From : Response 1
From : Response 2
From : Response 3
As we see, senderClass won't show up. I've tried different methods and only got errors that I could understand after reading around.
I don't wish to use response.type instead of senderClass because in the meantime, I'm setting senderClass after mounted with a real css class.
Maybe it's my approach that's completely wrong, could you give me some hints ?
I think the name of your property is wrong. Just change in page.html v-bind:senderClass="response.type" to v-bind:sender-class="response.type"
http://jsfiddle.net/eywraw8t/310360/
HTML attribute names are case-insensitive. Any uppercase character will be interpreted as lowercase. So camelCased prop names need to use their kebab-cased equivalents.
Apart from what Jns said You could get rid of v-bind altogether and just use :varibaleName for bindings.
Link to fiddle
https://jsfiddle.net/50wL7mdz/654614/#&togetherjs=J9vgbNaR7m

Statically passing arrays to a Vue component

I need to statically pass an array to my Vue component, called ajax-table. I can't seem to find a way to do it, so I came up with this:
<ajax-table
header-names="Code, Name, Description, Type"
field-names="code, name, description, major_type">
</ajax-table>
Inside the component, I do this:
export default {
props: [
'headerNames',
'fieldNames'
],
data: function () {
return {
columnHeaders: [],
columnFields: []
}
},
created() {
this.columnHeaders = this.headerNames.split(",").map(x => x.trim());
this.columnFields = this.fieldNames.split(",").map(x => x.trim());
}
}
Now, columnHeaders and columnFields contain the header-names and field-names that I passed statically to the component.
My question:
Is there a better way to do this?
You should be able to directly pass the array to props using v-bind: directive or : for short:
<ajax-table
:header-names="['Code', 'Name', 'Description', 'Type']"
:field-names="['code', 'name', 'description', 'major_type']">
</ajax-table>
Now props headerNames and fieldNames are arrays, which you can use in the component.

Component's Array index data not updating

I need to update some Array data in my VueJS component which is rendering as a list in the template via v-for.
When I update the whole Array, I see that the list updates in the DOM. But, if I update only an index, the list does not update.
Here are the two relevant methods:
methods: {
loadOnlyOneIndex: function() {
this.data[0] = {
title: 'Hello error',
slug: 'hello',
desc: 'will not work'
};
},
loadEverything: function() {
this.data = [{
title: 'Hello new title',
slug: 'hello',
desc: 'this will work'
}, {
title: 'Hello new title 2 !',
slug: 'hello2',
desc: 'really work'
}];
}
}
Here is a fiddle.
From the documentation:
Due to limitations in JavaScript, Vue cannot detect the following changes to an array:
When you directly set an item with the index, e.g. vm.items[indexOfItem] = newValue
When you modify the length of the array, e.g. vm.items.length = newLength
To get Vue to react to the change of an array's index, use the Vue.set() method.
In your case, you should use Vue.set(this.data, 0, newValue) in your loadOnlyOneIndex method.
Here's a working fiddle.
Every Vue instance also has an alias to the Vue.set method via vm.$set (where vm is the Vue instance).
So, you could also use this.$set(this.data, 0, newValue) in your loadOnlyOneIndex method.
This is helpful when using Single File Components, or anywhere where you don't have a direct reference to the Vue object.