Holiday Notice: Support will be provided on a limited scale from December 24th, 2024, to January 2nd, 2025. Happy holidays and a wonderful New Year!


Topic: Multiple mdbSelect With Identical Options

atticuss free asked 4 years ago


I'm building a recipe application to learn a few different technologies. When creating a new recipe, the user can add an arbitrary number of ingredients. I solved this with the following snippet of relevant template code:

<mdb-tbl>
    <mdb-row
    v-for="idx in numIngredients"
    :index="idx"
    :key="idx"
    class="justify-content-start"
    >

    <mdb-col col="7">
        <mdb-select
        v-model:"ingredients"
        placeholder="Select an ingredient"
        label=""
        search
        >
        </mdb-select>
    </mdb-col>

    </mdb-row>
</mdb-tbl>

And within the script code, I load all the ingredients via axios:

created() {
    IngredientAPI.getIngredients().then(data => {
        this.ingredients = data.ingredients.map(ing => {
            return { text: ing.name, value: ing.uid };
        });
    });
}

This works great for a single mdbSelect, but I (obviously) need to add multiple ingredients. By using v-model, the two binding makes it so that any update to one mdbSelect updates all mdbSelect components. Expected behavior. However, I'm struggling with how to fix this behavior. I've tried a several solutions to no avail. For example:

<mdb-select
:options="ingredients"
:selected="getSelected(idx)"
@change="updateSelected($event, idx)"
placeholder="Select an ingredient"
label=""
search
/>

getSelected(idx) {
  console.log(`get selected: ${idx}`);
  return this.selectedIngredients[idx];
},
updateSelected(val, idx) {
  console.log(`update selected: ${val} - ${idx}`);
  this.selectedIngredients[idx] = { text: val, value: val };
},

Or:

<mdb-select
v-model="ingredients"
:selected="selectedIngredients[idx]"
placeholder="Select an ingredient"
label=""
search
/>

The obvious solution is to create a separate array for each mdbSelect; however, this feels incredibly hacky and would almost certainly cause performance issues once I have hundreds of ingredient choices. Is there a way to provide each mdbSelect with a list of options whilst maintaining a list of what each mdbSelect has selected independently? Thanks in advance.


Mikołaj Smoleński staff commented 4 years ago

In such case it's necessary to create a new list for each select. Best regards


atticuss free commented 4 years ago

Well that's pretty unfortunate. But I anticipated this and decided to not preload any items and instead rely on the @search and adisableFilter. However, I'm having trouble getting my array to be reactive to data changes. For example:

<mdb-select
disableFilter
@search="handleIngredientSearch($event, idx)"
v-model="ingredients[0]"
placeholder="Select an ingredient"
label=""
search

...

data() {
    return {
    newValues: [],
    render: false,
    ingredients: [[]],
    numIngredients: 1,
    tags: [],
    recipes: []
    };
},
methods: {
handleIngredientSearch(text, idx) {
    console.log(`text: ${text}`);
    console.log(`idx: ${idx}`);
    if (text.length >= 3) {
    IngredientAPI.searchIngredients(text).then(data => {
        this.ingredients[0] = data.ingredients.map(ing => {
        return { text: ing.name, value: ing.uid };
        });
        console.log(this.ingredients[0]);
    });
    }
},
}

this.ingredients[0] updates just fine, but the items are not added to the list presented by mdbSelect. I'm guessing this has to do with an implementation detail of how v-model handles two-way binding? It's worth noting this works fine if I just bind to ingredients, and that I'm only explicitly indexing at 0 as a POC.


atticuss free commented 4 years ago

Ah sorry -- still new to Vue and just found the reactivity caveats within the docs. If anyone stumbles across this later, the solution is to replace:

this.ingredients[0] = options;

with:

this.$set(this.ingredients, 0, options);

This will trigger state updates and update component data accordingly. More info for noobs like me:

https://vuejs.org/v2/guide/reactivity.html#For-Arrays

That said: I'd heavily recommend you consider expanding the mdbSelect component to be able to easily delineate between a list of options and the selected value. I can't imagine this use-case is very novel, and it hurts my inner engineer to see data pointlessly copied like in the originally proposed solution.



Please insert min. 20 characters.

FREE CONSULTATION

Hire our experts to build a dedicated project. We'll analyze your business requirements, for free.

Status

Resolved

Specification of the issue

  • ForumUser: Free
  • Premium support: No
  • Technology: MDB Vue
  • MDB Version: 6.7.2
  • Device: PC
  • Browser: Firefox
  • OS: OSX
  • Provided sample code: Yes
  • Provided link: No