import { Bindings, initializeBindings } from "@coveo/atomic"; import { Component, Element, h, State, forceUpdate } from "@stencil/core"; import { PagerState, Pager, SearchStatusState, buildSearchStatus, buildPager, Unsubscribe, } from "@coveo/headless"; import { waitForAtomic } from "../../utils/atomic"; /** * Sample custom Atomic component, initializing itself against a parent search interface in order to retrieve the bindings. * * This component showcases a custom-made pagination component, for educational purposes. * * In a real life scenario, we recommend using either [atomic-pager](https://docs.coveo.com/en/atomic/latest/reference/components/atomic-pager/) or [atomic-load-more-results](https://docs.coveo.com/en/atomic/latest/reference/components/atomic-load-more-results/) instead. */ @Component({ tag: "sample-component", styleUrl: "sample-component.css", shadow: true, }) export class SampleComponent { // The Atomic bindings to be resolved on the parent atomic-search-interface. // Used to access the Headless engine in order to create controllers, dispatch actions, access state, etc. private bindings?: Bindings; // We recommend recording possible errors thrown during the configuration. private error?: Error; // Headless controller that contains the necessary methods. private pagerController!: Pager; // When disconnecting components from the page, we recommend removing // state change listeners as well by calling the unsubscribe methods. private pagerUnsubscribe: Unsubscribe = () => {}; private statusUnsubscribe: Unsubscribe = () => {}; private i18nUnsubscribe = () => {}; @Element() private host!: Element; // Headless controller state property, using the `@State()` decorator. // Headless will automatically update these objects when the state related // to the controller has changed. @State() private pagerState!: PagerState; @State() private statusState!: SearchStatusState; // We recommend initializing the bindings and the Headless controllers // using the `connectedCallback` lifecycle method with async/await. // Using `componentWillLoad` will hang the parent atomic-search-interface initialization. public async connectedCallback() { try { // Wait for the Atomic to load and bindings to be resolved. await waitForAtomic(); this.bindings = await initializeBindings(this.host); // Initialize controllers. const statusController = buildSearchStatus(this.bindings.engine); this.pagerController = buildPager(this.bindings.engine); // Subscribe to controller state changes. this.statusUnsubscribe = statusController.subscribe( () => (this.statusState = statusController.state) ); this.pagerUnsubscribe = this.pagerController.subscribe( () => (this.pagerState = this.pagerController.state) ); // (Optional) To use if component needs to rerender when the Atomic i18n language changes. // If your component does not use any strings or does not support multiple languages, // you can ignore everything related to i18n. const updateLanguage = () => forceUpdate(this); this.bindings!.i18n.on("languageChanged", updateLanguage); this.i18nUnsubscribe = () => this.bindings!.i18n.off("languageChanged", updateLanguage); } catch (error) { console.error(error); this.error = error as Error; } } // The `disconnectedCallback` lifecycle method should be used to unsubcribe controllers and // possibly the i18n language change listener. public disconnectedCallback() { this.pagerUnsubscribe(); this.statusUnsubscribe(); this.i18nUnsubscribe(); } private get pages() { const pages = this.pagerState!.currentPages; return pages.map((page) => this.buildPage(page)); } private buildPage(page: number) { const isSelected = this.pagerController.isCurrentPage(page); return ( ); } private get previous() { return ( ); } private get next() { return ( ); } public render() { if (this.error) { return (

Error when initializing the component, please view the console for more information.

); } if (!this.bindings || !this.statusState.hasResults) { return; } return ( ); } }