Dynamically updating v-combobox items list - vue.js

Is it possible somehow to update Vuetify v-combobox items as the user is typing? I want to change the list of available items depending on what the users started typing, to create an address input with suggestions from a geolocation API.
This is what I tried: #update:search-input='fetchAddresses'
And in fetchAddresses: this.items = newListOfItems
However, while #update:search-input fires as expected, the combobox list will only be updated after losing focus. Can I somehow trigger it to be updated? (This may very well be an X/Y problem, so any hints about other approaches are welcome)
My current, ugly, hack is to force the whole combobox component to re-render with the current value set, and then refocus on it. (There is an activateMenu() method on the combobox that I could use to make sure the list reopened:
this.$nextTick(() => {
if (this.$refs.addressCombobox) {
this.$refs.addressCombobox.focus()
this.$refs.addressCombobox.activateMenu()
}
})
I'm using Vue 2.

In my case no-filter solved this problem
<v-combobox no-filter ... >

Related

How to programmatically expand all rows in a v-data-table component (Vuetify version 1.5)

v-data-table component has a propery "expanded" that allows to show some additional info for each row.
It works fine and I need to expand all the rows immediately when a page is loaded.
Is there a way to do that?
It is used version 1.5 of Vuetify Framework.
https://v15.vuetifyjs.com/en/components/data-tables
This is fairly easy in the new Version of Vuetify, you just use the property expanded which holds an Array of the items that are currently expanded.
<v-data-table
id="issues-table"
:value="selectedIssues"
:expanded="expandedIssues"
:items="issues"
>
<v-switch #change="(v) => expandAllIssues(items, v)" />
expandAllIssues (items, status) {
if (status) {
this.expandedIssues = items
} else {
this.expandedIssues = []
}
}
In V1.5.xx of Vuetify you don't have that luxury, but when I took a look, Vuetify works with a similar system under the hood. Firstly you should set the expand prop on the table to true, so it can expand multiple rows.
You can define a reference on your data table and then access it via this.$refs.myDataTableRef. You then realise that they store the expanded rows in an Object called expanded. You can set the rows either true by the id/name of the row in this object or you simply set the whole row objects in the vuetify expanded object to true.
I have pasted the Codepen example of their old data table and made a simple expand all button, that you can obviously change to your desires, but it should make the concept clear.
Codepen example

Initially hide first group in Vue-Formulate repeatable group

I'm using Vue-Formulate's Repeatable Groups. For my requirements:
A group is optional to add
If a button is pressed to add a group, then the fields in the group must be validated
The form should not initially show the group; it should show the button to add a group
For example, the desired initial appearance is in the following screenshot, which I generated after clicking the "remove" / X button in the linked codesandbox:
I've mocked this up at codesandbox here: Vue Formulate Group with Button to Start
Is this possible? If so, how?
May 2021 UPDATED WORKAROUND
In #braid/vue-formulate#2.5.2, the workaround below (in Research: A hack that seems to UPDATE: USED TO work) no longer works, using a slot to override the close button, save a ref, and trigger a click does. See also the related feature request at https://github.com/wearebraid/vue-formulate/issues/425.
<script>
export default {
// ... fluff omitted
async mounted() {
await this.$nextTick();
if (!this.hasMessages) {
// See also feature request at https://github.com/wearebraid/vue-formulate/issues/425
this.$refs.closeButton.click();
}
},
};
</script>
<template>
<FormulateInput
type="group"
name="rangeMessages"
:minimum="0"
repeatable>
<!-- See https://vueformulate.com/guide/inputs/types/group/#slots -->
<template #remove="{removeItem}">
<button ref="closeButton" #click.prevent="removeItem"/>
</template>
</FormulateInput>
</template>
Research - Vue-Formulate's Docs
In Vue-Formulate's docs on input with type="group"'s props and slots, there is a minimum prop. I've set that to zero, but that doesn't change the initial appearance. I do see multiple slots, but I'm not quite sure which one to use or if I could use any of them to achieve what I want; it seems like default, grouping, and repeatable might be useful in preventing the initial display of the first group.
Research - Vue-Formulate's Tests
I see that FormulateInputGroup.test.js tests that it('repeats the default slot when adding more', so the default slot is the content that gets repeated. According to the docs, the default slot also receives the index as a slot prop, so that could be useful.
Research - Vue Debugger
The item which I want to initially remove is at FormulateInputGroup > FormulateGrouping > FormulateRepeatableProvider > FormulateRepeatable > FormulateInput:
When I remove the initial item to match the desired initial layout, the group hierarchy changes to:
<FormulateInput><!-- the input type="group" -->
<FormulateInputGroup>
<FormulateGrouping/>
<FormulateAddMore>...</FormulateAddMore>
</FormulateInputGroup>
</FormulateInput>
Based on this change, I would expect that I need to modify FormulateGrouping to get the desired initial appearance, but I haven't found in the source what items are available to me there.
Research: A hack that seems to UPDATE: USED TO work
This hack worked in v2.4.5, but as of 2.5.2, it no longer works. See top of post for an updated workaround.
In the mounted hook, when I first render the form, I can introspect
into the formValues passed to v-model to see if the group lacks an
initial elements that is filled out. If so, then I can make use of a
ref msgs on the FormulateInput of type group to then call
this.$refs.msgs.$children[0].$children[0].removeItem(), which
triggers an initial remove of the (empty) item. This is super hacky,
so I'd prefer a better way, but it kind of works. The only problem is
that it validates the fields when clicking on the button, before any
input has been entered.
This is a fair question, and Vue Formulate used to support the behavior of just using an empty array to begin with, however it became clear that it was confusing to users that their fields would not show up without an empty object [{}] when they bound the model, so a change was made to consider an initial value of an empty array an "empty" field and pre-hydrate it with a value. Once that initial hydration is completed however, you can easily re-assign it back to an empty array and you're good to go. This is easily done in the mounted lifecycle hook:
...
async mounted () {
await this.$nextTick()
this.formData.groupData = []
}
...
Here's a fork of your code sandbox: https://codesandbox.io/s/vue-formulate-group-with-button-to-start-forked-32jly?file=/src/components/Reproduction.vue
Provided solutions weren't working for me but thanks to previous answer I've managed to find this one:
mounted(){
Vue.set(this.formData, "groupData", [])
},
which does same effect as
data(){
formData: {
groupData: [],
},
},
mounted(){
this.formData.groupData = []
},

Vue js event not fired for item filtered out of list

I have a list of items. Each item has a "complete" flag. If this flag is true, the item shows in view A. If it is false, it shows in view B. The items are retrieved via an API request, with a client-side filter applied through a computed method on the base dataset. So for view A it will return items with complete=true, for view B, it will return items with complete=false.
The complete flag is editable (via a checkbox). I have a watcher function on the complete field that results in a PATCH being made to the item to toggle the complete field on the server.
Here's the issue. Since the vue app is filtering items based on the complete flag, any time that field changes, the item is removed from the view, so the watcher never gets triggered and the PATCH is never made.
It makes sense that the item's removal would cause the filter function to be re-run, however it seems odd that the watcher is killed before it can run.
So my question is, how do I achieve this - a list of filtered objects where the filter criteria can be changed, and have that change recognized via a watcher or similar?
You're probably better off using an #input event on the checkbox, and tying that to the api call:
<input type="checkbox" #input="onCheckChange">
...
methods: {
onCheckChange() {
// Perform API call here.
}
}

Deselect v-list-item when changing its value

I have a v-list-item-group with some v-list-item's inside with an associated value. The problem is that when I change the value of the selected v-list-item it doesn't react. I want it to be selected only when the new value matches the v-list-item-group's model and be unselected otherwise. What is the best way to achieve such a behaviour?
Example. When you edit the list item value, for example, by changing the lower left field from "Bluetooth" to "Bluetooth1", I want the list item to get deselected automatically because its value isn't longer matching v-list-item-group's model ("Bluetooth1" != "Bluetooth"). And when you change it back to "Bluetooth" I want it to get selected again.
The one on the right modifies the v-list-item-group's model and this works as expected.
My actual use case is rendering lists of thousands of elements quickly without rendering them all. To achieve it I use a recycle-scroller component inside my v-list-item-group to manage its items. It creates a few dozens of v-list-items and as user scrolls it reuses them by moving them around and replacing their value, icon and text with corresponding data from my array. The problem is that when user selects some list item then scrolls the list, the selected item gets reused. Despite the fact that it now has a different value, text and icon it still looks as selected. So they see it as if some different item was selected.
I hope this works for you:
<v-list-item-group v-model="model" :color="color">
// other html
Then add a computed property:
computed: {
color(){
if (this.model == this.items[1].text) {
return 'indigo'
} else {
return 'black'
}
}
}

Extjs4 Combo's and Stores: Remove filter when queryMode=local?

I'm getting frustrated because my store keeps getting filtered whenever I use it to back a combofield. Is there any way I can disable this?
The Scenario
I have a Store with a data field on it; an array of objects loaded when the store is instantiated. I use this store to drive a bunch of combo's in different areas of my app. Unfortunately, my combos are applying filters on the store, causing other combos using the same store to only display the filtered values later on, not the whole list.
Workarounds
My goofy workaround is to call combo.getStore().clearFilter() after I'm done with the combo, but that's going to get old very quick, and probably introduce a bug somewhere, I'm sure.
If I remove queryMode:'local' from my combo's config, all is well, except that now the handy type-ahead feature no longer works; I'm just shown a list of items in a drop-down that I can't even navigate around my typing letters of matching items. That's worse than a regular html select tag!
Any ideas?
Thanks!
You can't do that since the filtering is applied not on the combo but on the store. You could try creating multiple instances of the same store and work with that. Though I don't know if it'll work.
Ext.create('combo', {
//other config
store : Ext.create('my.store')
});
It'll work if you make the combo non-editable since no filtering can be applied then. But, as you say, you need the type ahead feature, you'll need to create multiple instances of the store.
In light of the fact that combos will add filters on the backing store, hence affecting all combos that use the store within my application, I've opted to add an override to the combo class so it will clear the filter on the store when the combo box is destroyed.
Ext.define('MAP.override.Combo', {
override : 'Ext.form.field.ComboBox',
initComponent : function()
{
this.callParent(arguments);
this.on('beforedestroy',function(combo){
if(combo.leaveFilter === true) return;
console.log('clearing filter on store');
combo.getStore().clearFilter();
});
}
});
it's a bit of a hack, but I do allow for the escape hatch of indicating not to clear the filters, too.
The simplest way I have found to handle this solution is to add the following listener to the combo:
listeners: {
beforequery: function(queryPlan){
queryPlan.query = true;
}
}
by default queryPlan.query is the text currently in the combo field which is used for filtering. Setting it to false cancels the query, but setting it to true allows the query to go through without a filter value, therefore keeping all values in the drop down list for all combo fields.
I've had similar problem with ExtJS 4.2 and combo. Store kept being filtered but I couldn't use clearFilter() because after that combo was unusable. My solution, which worked, is this listener on combo:
listeners: {
blur: function(combo) {
if (combo.queryFilter) {
combo.queryFilter.setValue('');
combo.getStore().filter();
}
}
}