algolia / vue-instantsearch

πŸ‘€ Algolia components for building search UIs with Vue.js

Home Page:https://www.algolia.com/doc/guides/building-search-ui/what-is-instantsearch/vue

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Bug with initialUiState.

bootsmann1995 opened this issue Β· comments

Bug 🐞

What is the current behavior?

<ais-instant-search-ssr
					:initial-ui-state="initialUiState"
					> 

ais-instant-search-ssr does not support :initial-ui-state feature. I need this on server side since I want to preconfigure my search based on something from my headless cms. And I can't do this in my setup.

I'm using Nuxt Algolia implemented like in your docs:

data() {
		const mixin = createServerRootMixin({
			searchClient: getSearchClient(this.$config ?? process.env),
			indexName: this.$config?.ALGOLIA_INDEX_NAME ?? process.env.ALGOLIA_INDEX_NAME!,
			routing: {
				router: nuxtRouter(this.$router),
			},
			initialUiState: {
				[this.$config?.ALGOLIA_INDEX_NAME]: {
					refinementList: {
						business: ["koed"] <--- THIS IS HERE I WANT TO USE MY CMS DATA PROPERTY
					}
				},
			}
		});
		return {
			...mixin.data(),
		};
	},
	provide() {
		return {
			// Provide the InstantSearch instance for SSR
			$_ais_ssrInstantSearchInstance: this.instantsearch,
		};
	},
	serverPrefetch() {
		return this.instantsearch?.findResultsState(this).then((algoliaState: any) => {
			this.$ssrContext.nuxt.algoliaState = algoliaState;
		});
	},
	beforeMount() {
		const results =
			(this.$nuxt.context && this.$nuxt.context.nuxtState.algoliaState) ||
			(window as unknown as IAlgoliaWindow).__NUXT__.algoliaState;

		(this as any).instantsearch.hydrate(results);

		// Remove the SSR state so it can't be applied again by mistake
		delete this.$nuxt.context.nuxtState.algoliaState;
		delete (window as unknown as IAlgoliaWindow).__NUXT__.algoliaState;
	},

I can't use my properties in data() from headless cms so I need some way to set this initialUiState when my cms data is available-

What is the expected behavior?

The expected behavior is I can set my initialUiState programatically. something like this:

data() {
		const cmsProp = this.data.property;
		
		const mixin = createServerRootMixin({
			searchClient: getSearchClient(this.$config ?? process.env),
			indexName: this.$config?.ALGOLIA_INDEX_NAME ?? process.env.ALGOLIA_INDEX_NAME!,
			routing: {
				router: nuxtRouter(this.$router),
			},
			initialUiState: {
				[this.$config?.ALGOLIA_INDEX_NAME]: {
					refinementList: {
						business: [cmsProp ] <--- USED HERE.
					}
				},
			}
		});
		return {
			...mixin.data(),
		};
	},
	

What is the version you are using?

"algoliasearch": "^4.9.3",
"vue-instantsearch": "^3.7.0",

Hi @bootsmann1995 usually when you want to add state from the server, you'd rely the routing feature, and so you'd be able to pass a state via the url like this: https://github.com/algolia/vue-instantsearch/blob/master/examples/ssr/src/main.js#L44-L45.

Can this work for you? Otherwise, PRs are more than welcome!

@tkrugg Thanks for the answer :)

How would you implement it with an object? and isn't this Vue3 ? i'm using nuxt, and it's only in vue2 for now.

i'm currenly using this router:

import VueRouter from 'vue-router';
import { Dictionary } from 'vue-router/types/router';

type RouteState = { [key: string]: unknown };


export function nuxtRouter(vueRouter: VueRouter) {
  return {
    read() {
      return vueRouter.currentRoute.query;
    },
    write(routeState: RouteState) {
      // Only push a new entry if the URL changed (avoid duplicated entries in the history)
      if (this.createURL(routeState) === this.createURL(this.read())) {
        return;
      }
      vueRouter.push({
        query: routeState as Dictionary<string | (string | null)[] | null | undefined>,
      });
    },
    createURL(routeState: RouteState) {
      return vueRouter.resolve({
        query: routeState as Dictionary<string | (string | null)[] | null | undefined>,
      }).href;
    },
    onUpdate(cb) {
      if (typeof window === 'undefined') return;

      /* tslint:disable-next-line */
      this._onPopState = (event: IEvent) => {
        const routeState = event.state;
        if (!routeState) {
          cb(this.read());
        } else {
          cb(routeState);
        }
      };
      window.addEventListener('popstate', this._onPopState);
    },
    dispose() {
      if (typeof window === 'undefined') return;

      window.removeEventListener('popstate', this._onPopState);
    },
  } as INuxtRouter;
}

it works fine with server navigation. But when routing with nuxt-router it clears the url

otherwise is it possible to set the uiState after render? @tkrugg @Haroenv

you can set the ui state after render with instantsearchInstance.setUiState, but I still don't get why routing doesn't work for your use case.

Continuing to ping individual people instead of using support@algolia.com however is messing with our processes and as always you haven't given a sandbox for debugging, could you stop doing that please?

Haroenv yes ofc. sorry for this. It's just because this issue is under time pressure. I want to set the route based on an object fetched from an api. But if i push my parameter to my router on created it just clears my parameters after load? I sent my algolia routing further up in this thread. It works when routing directly to the page. but not when using vue-router navigation.

If you use routing, you can't also use initial UI state, they both act on the same information and routing takes precedence. What you should do is:

  1. add a new property "initial state" or something to the router you created
  2. pass the cms state to the router
  3. merge with the router from the url

Hope that makes sense!

Thank you for the answer, do you have an example on this? otherwise i'll try make it work with these steps.

But if I don't have the data from asyncData() in this hook, this sets me back to square one still right?

the data in asyncData happens after data if I remember it correctly, without a full example of what you want to do it will be hard to help you out. I recommend simplifying the logic so the state can be derived directly from the page instead of needing a network request

Okay do you have a nuxt template. then I'll create a full example for you to clarify :)
sorry for the confusion.

But it's because we give the content editor the possibility to pre select a value for the uistate on the cms. and we fetch data from cms in asyncData bc of the static site generation.