0

I currently have a page where I'm able to switch Algolia indices with this:

<template>
    <button @click="selectedIndex = a">List A</button>
    <button @click="selectedIndex = b">List B</button>

    <A v-if="selectedIndex === a" />
    <B v-if="selectedIndex === b" />
</template>

<script>
import A from '@/A.vue';
import B from '@/B.vue';

export default {
    components: {
        A,
        B
    },

    data() {
        return {
            selectedIndex: `a_${this.$root.index}`,
            query: ''
        };
    },

    computed: {
        a() {
            return `a_${this.$root.index}`;
        },

        b() {
            return `b_${this.$root.index}`;
        }
    }
};
</script>

This is in a file called Index.vue. The different indices are in files A.vue and B.vue.

But now I need to be able to do the same content switching using a vue-multiselect in A.vue and B.vue.

Currently in A.vue, I have

<template>
    <ais-instant-search
        :search-client="searchClient"
        :index-name="a"
        :routing="routing"
    >
    <multiselect
        v-model="selectedIndex"
        :options="switcherOptions"
        :searchable="false"
        :close-on-select="true"
        :show-labels="false"
        placeholder="Choose"
    >
        <template slot="singleLabel" slot-scope="{ option }">
            {{ option.text }}
        </template>
        <template slot="option" slot-scope="{ option }">
            {{ option.text }}
        </template>
    </multiselect>
    </ais-instant-search>
</template>    

export default {
    components: {
        Multiselect
    },

    data() {
            searchClient: algoliasearch(window.algolia.id, window.algolia.key),
            selectedIndex: { value: 'a', text: 'List A' },
            switcherOptions: [
                { value: 'a', text: 'List A' },
                { value: 'b', text: 'List B' }
            ]
        };
    },
};

What I don't know how to do now is send the value from the multi-select from A.vue back up to Index.vue where the different indices are defined.

1 Answer 1

0

First observation that I have, in your A.vue you are binding variable named a to your index, but do not have a defined in your data, same thing for routing.

What is the point of computed properties a and b, they are just returning strings, with do not do any computing, this could probably be defined in data:

    data() {
        return {
            selectedIndex: 'a_index',
            query: '',
            a: 'a_index',
            b: 'b_index',
        };
    },

Are A.vue and B.vue identical components? If only bindings are different, you can probably combine them into one component, and just pass different props to them. Hard to know for sure without seeing complete code.

So one way of doing this is emitting events from child to parent components. Documentation reference: https://vuejs.org/guide/essentials/event-handling.html

On your multiselect component add @select="$emit("indexSelected", selectedIndex)"

Like so:

<multiselect
        v-model="selectedIndex"
        :options="switcherOptions"
        :searchable="false"
        :close-on-select="true"
        :show-labels="false"
        placeholder="Choose"
        @select="$emit("indexSelected", selectedIndex)"
    >

This will emit event with name of indexSelected and included selectedIndex value in its payload.

Then in Index.vue you need to add these event listeners to both A and B Components:

    <A v-if="selectedIndex === a" @indexSelected="selectedIndex = $event.value === 'a' ? 'a_index' : 'b_index'" />
    <B v-if="selectedIndex === b" @indexSelected="selectedIndex = $event.value === 'a' ? 'a_index' : 'b_index'" />

Or if you would like cleaner template you can create a method:

methods: {
  updateSelectedIndex(event){
    this.selectedIndex = event.value === 'a' ? 'a_index' : 'b_index';
  }
}

And then update template to:

    <A v-if="selectedIndex === a" @indexSelected="updateSelectedIndex" />
    <B v-if="selectedIndex === b" @indexSelected="updateSelectedIndex" />

EDIT:

I think this is kind of what you are looking for(reading between the lines lol), this rolls Index.vue, A.vue, B.vue into one component, because what you are trying to achieve is a lot simpler this way IMO. Obviously your actual app is more complex, so apply this as needed.

IndexAB.vue would look like this, values replaces with algolia demo, so substitute as needed:

<template>
  <div>
    <p>
      <strong>Changing Index with buttons: </strong>
      <button
        v-for="option in searchIndexOptions"
        :key="option.value"
        @click="searchIndexName = option"
      >
        {{ option.text }}
      </button>
    </p>
    <ais-instant-search
      :search-client="searchClient"
      :index-name="searchIndexName.value"
    >
      <p>
        <strong>Changing Index with vue-multiselect: </strong>

        <VueMultiselect
          v-model="searchIndexName"
          :options="searchIndexOptions"
          :searchable="false"
          :close-on-select="true"
          track-by="value"
          label="text"
          placeholder="Change Search Index Here"
        >
          <template v-slot:singleLabel="{ option }">
            <strong>{{ option.text }}</strong>
          </template>
          <template v-slot:option="{ option }">
            <strong>{{ option.text }}</strong>
          </template>
        </VueMultiselect>
      </p>
      <ais-search-box />
      <ais-hits>
        <template v-slot:item="{ item }">
          <h2>{{ item.name }}</h2>
        </template>
      </ais-hits>
    </ais-instant-search>
  </div>
</template>    

<script>
import VueMultiselect from "vue-multiselect";
import algoliasearch from "algoliasearch/lite";
import "instantsearch.css/themes/satellite-min.css";
import "vue-multiselect/dist/vue-multiselect.css";

export default {
  name: "IndexAB",
  components: { VueMultiselect },
  data: () => ({
    searchClient: algoliasearch("latency", "6be0576ff61c053d5f9a3225e2a90f76"),
    searchIndexName: { value: "instant_search", text: "List A" }, // Defaults to instant_search/List A
    searchIndexOptions: [
      { value: "instant_search", text: "List A" },
      { value: "airbnb", text: "List B" },
      { value: "airports", text: "List C" },
    ],
  }),
};
</script>

And sandbox: https://codesandbox.io/s/compassionate-ptolemy-9ljmhh?file=/src/components/IndexAB.vue

I had started with Vue 3 sandbox, so few things will be sligtly different(like import of vue-multiselect, and v-slot syntax)

Sign up to request clarification or add additional context in comments.

4 Comments

I've edited my answer. I tried reducing the code a bit to make it easier to read but reduced it too much. a and b are functions because they're actually taking a value from a data-attribute set on the HTML file that's pulling in all the Vue stuff. So I've tried adding @indexSelected="selectedIndex = $event.value" to the two components but on change I get nothing. And the reason they're two separate components is because they're displaying results from two different Algolia indices.
I’m on mobile now, will re read your question in the morning. But as far as A and B. Just pass the algolia index as prop from Index.vue. Something like this: <A :index=selectedIndex” />vuejs.org/guide/components/props.html#static-vs-dynamic-props
@Tyssen Answer updated, let me know if I'm close to what you are trying to achive :)
Thanks, that's a really nice solution, but as you mentioned, my app is a bit more complex. Each of the indexes actually pulls in a different component for the ais-hits and has slightly different searching tools too which is why they're separated into different components. So I think I still need a wait for the value of the multiselect to emit back up to the parent.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.