vue-material
vue-material copied to clipboard
[MdAutocomplete] options with objects.
As #1215, I fixed by #1218 to make searchTerm
sync with v-model
, but that also make it crash while the options are objects. It's could reproduce in documentation example Custom Template - With highlight text with 6f51eb91338a0fa845a99b2a65f1c76f8d549196 (and it's a bug now in beta 6). If I want to make #1215 support object, I should know what to show as searchTerm
. I think it's not easy because MdAutocomplete
get the plain text via Node.textContent
.
As #1241, I think users prefer that the result is the thing shown on the searchTerm
not the thing they select via autocomplete. It's good if I could still get the object while I type the searchTerm
and match a statement.
To sum up, I want to figure out a way to bind the option object and the plain text for the object.
Another issue is that MdIcon
cannot be used in md-autocomplete-item
slots or the searchTerm
will show the text in MdIcon
after selected.
Any idea?
Is there a workaround for this?
Bump. I had to resort to using another autocomplete lib because I couldn't workaround this, but would rather use md-autocomplete if this were possible
solution:
md-autocomplete v-model="searchTerm" @md-selected="onSelect"
onSelect (val) { this.value = val }
@rkakrik your solution did not work for me. The @md-selected="onSelect" will grab the right value (object), but md-autocomplete component still throws errors if the objects in your list are not "stringy" ... so you have to implement .toLowerCase and .toString on each (or maybe you are already working with "stringy" objects - I wasn't). For example (hacky/ugly, but works, and maintained re-usability for me to just put the object mapping directly in md-options):
<md-autocomplete
v-model="SelectDialog.SelectedItem"
:md-options="SelectDialog.SelectList.map(x=>({
'Id':x.Id,
'Name':x.Name,
'toLowerCase':()=>x.Name.toLowerCase(),
'toString':()=>x.Name
}))"
>
<label>Select</label>
<template slot="md-autocomplete-item" slot-scope="{ item, term }">
<md-highlight-text :md-term="term">{{ item }}</md-highlight-text>
</template>
<template slot="md-autocomplete-empty" slot-scope="{ term }">
"{{ term }}" not found!
</template>
<div class="md-helper-text" v-if="SelectDialog.SelectedItem">
<strong>Item:</strong>{{SelectDialog.SelectedItem.Name}} ({{SelectDialog.SelectedItem.Id}})
</div>
</md-autocomplete>
Perhaps something fancy can be done with this type check and the behavior of isinstanceof used by vue prop type checks to catch linting or compile-time errors. Mybe just adding some working version of my example to the docs would do the trick.
... the silly thing is that the native <select>
in html already had this concept of "value" and "label" separate...
I'd suggest leveraging that somehow with a slot. The v-model should be the selected OBJECT, and the text should come from selected object's "label" (defined by slot)? Not sure if it's possible.
Alternatively, you can add a prop for one to pass a "labeler" function (the default being .toString). Labeler function is evaluated to show list items and set text in text box. V-model would still hold ref to selected OBJECT, not the label.
@fizxmike https://github.com/vuematerial/vue-material/pull/1338
@fizxmike your solution partially worked for me, instead of the item name showing it was showing the entire object.
My workaround was as follows:
<md-autocomplete
v-model="selectedLabel"
:md-options="getSanitizedLabels">
<label>Label</label>
<template slot="md-autocomplete-item" slot-scope="{ item, term }">
<md-highlight-text :md-term="term">{{ item.Name }}</md-highlight-text>
</template>
</md-autocomplete>
computed: {
getSanitizedLabels() {
return this.labels.map(label => ({
'Id': label.id,
'Name': label.name,
'toLowerCase': () => label.name.toLowerCase(),
'toString': () => label.name
}));
}
}
Although this works for me, it gives an error message when selecting an item:
[Vue warn]: Invalid prop: type check failed for prop "mdTerm". Expected String, got Object.
Yea... this, and other issues re-inventing (bad) wheels, have "drove" me to Angular (TypeScript) with Angular Material components. Google's "wheels" tend to work just fine (most of the time).
Hi all,
I'm facing the same issue, solution provided by @DavidHtx and @fizxmike works but both partially, as in it works passing object and remaping using the promise, but it will still throw : [Vue warn]: Invalid prop: type check failed for prop "mdTerm". Expected String, got Object. if you want to select something the second time from the list. Still works thou but chrome console throws it.
Any idea when this will be fixed or if it will be fixed?
@vzapo try add term.Name
in md-term
<template slot="md-autocomplete-item" slot-scope="{ item, term }">
<md-highlight-text :md-term="term.Name">{{ item.Name }}</md-highlight-text>
</template>
thanks.
@vzapo try add
term.Name
inmd-term
<template slot="md-autocomplete-item" slot-scope="{ item, term }"> <md-highlight-text :md-term="term.Name">{{ item.Name }}</md-highlight-text> </template>
Same issue here, I've tried everything, and still getting this.searchTerm.toLowerCase
is not a function.
Is vue-material still supported? Or should we switch to another component?
Thanks
Any news with this issue? Including autocomplete on documentation website is not working properly with objects. (See Custom template with highlight text, where you cannot select an option and then everything breaks)
I resorted to not using v-model, and using :value and @input and handling it myself.
However now the issue is that the value prop is not watched, so when I'm updating it manually (resetting it to empty basically after I did my thing) it's not resetting also on the component.
So still unusable.
If this would work, at least we'd have that way of working around this bug
Separate object field and search term, change search term onSelected, it works!
If you read the source code of MdAutocomplete component you will find v-model is bind to "searchTerm". The author means to bind the search keyword to v-model of this input rather than the result string/object it self. So if we follow this idea, use xxxSearchTerm, onSelected trigger to update our actual data. Just like @rkakrik did, but you need to bind another searchTerm to v-model, not the form field it self.
For the clear form issue, I don't think it's a problem. Because whether the searchTerm is part of form or not is just your choice. In my use case, I just put the search term out of form.
<md-autocomplete
v-model="customerSearchTerm"
:md-options="customers"
@md-changed="getCustomers"
@md-opened="getCustomers"
@md-selected="selectCustomer"
>
<label>Customer</label>
<template slot="md-autocomplete-item" slot-scope="{ item }">
{{ item }}
</template>
</md-autocomplete>
data() {
return {
booking: { id: "", customer: {} },
customers: [],
customerSearchTerm: ""
};
},
methods: {
async getCustomers(q) {
this.customers = (await User.get({ keyword: q })).body;
},
selectCustomer(c) {
this.booking.customer = c;
this.customerSearchTerm = c.name; // !!!here's the thing!!!
}
},
async mounted() {
if (this.$route.params.id !== "add") {
this.booking = (await Booking.get({ id: this.$route.params.id })).body;
this.customerSearchTerm = this.booking.customer.name; // !!!here's the thing!!!
}
}
@fizxmike your solution partially worked for me, instead of the item name showing it was showing the entire object.
My workaround was as follows:
<md-autocomplete v-model="selectedLabel" :md-options="getSanitizedLabels"> <label>Label</label> <template slot="md-autocomplete-item" slot-scope="{ item, term }"> <md-highlight-text :md-term="term">{{ item.Name }}</md-highlight-text> </template> </md-autocomplete>
computed: { getSanitizedLabels() { return this.labels.map(label => ({ 'Id': label.id, 'Name': label.name, 'toLowerCase': () => label.name.toLowerCase(), 'toString': () => label.name })); } }
Although this works for me, it gives an error message when selecting an item:
[Vue warn]: Invalid prop: type check failed for prop "mdTerm". Expected String, got Object.
This seems to work for me without any error messages! Only issue is that when I select an item the auto complete drop down stays open! Which I am sure is an easy fix!
@uicestone
Thank you for the solution. Now it doesn't throw errors anymore.
However I am able to search only once.
After I search and select an option from the option list for the first time, I remove the search term and try again, this time option list do not show up.
I also looked at the options data in vue devtools and the options are retrieved correctly as usual, the UI just doesn't show up.
Any suggestion would be appreciated.
Thanks
@anurat probably a bit late, but I was able to make it work adding the following
{
"uuid": "12345-61c5-4bba-a3bb-4614c10283e3",
"person_dni": 32109344,
toLowerCase() {
return this.uuid
},
toString() {
return this.person_dni
}
},
{
"uuid": "935744-fd96-438c-85ae-d1391f9c54f2",
"person_dni": 1234567,
toLowerCase() {
return this.uuid
},
toString() {
return this.person_dni
}
}
]
If I return the same value for toLowerCase() and toString() the dropdown is shown only once... I didn't investigate the root cause, but this workaround works fine
Even the document does not solve the problem