Search code examples
javascriptsolid-js

In Solidjs, how do I create form submissions with multiple paramters similar to this onInput change?


I have a backend service that can be called using http://localhost:7070/search?q=someterm and I'm trying to implement a frontend using Solidjs. I used their documentation and examples to come up with this very simple, working component:

import { Component, createResource, createSignal } from 'solid-js';

const searchQuery = async (text: any) =>
  (await fetch(`http://localhost:7070/search\?q=${text}`)).json();

  const [query, setSearchQuery] = createSignal();
  const [queryResponse] = createResource(query, searchQuery);

const Search: Component = () => {
  return (
    <div>
        <div>
            <label for="query"></label>
            <input id="query" onInput={(e) => setSearchQuery(e.currentTarget.value)} />
        </div>
        <span>{queryResponse.loading && "Loading..."}</span>
        <div>
            <pre>{JSON.stringify(queryResponse(), null, 2)}</pre>
        </div>
        <button type="submit" >Search</button>
    </div>
  )
}

export default Search;

The query does work and I can see the JSON response in the correct div. However, the search occurs as I am typing due to the onInput. I want to only active the query via clicking the button, which has proven to be a lot more difficult that I anticipated.

Looking through several tutorials, I attempted to separate out my code into a useSearch.ts. I created a full type for the query. Even though it only contains a single text field right now, I do anticipate adding several other query parameters. This is my attempt at a useSearch.ts:

import { createSignal, createResource } from "solid-js";
import { createStore } from "solid-js/store";
import { render } from "solid-js/web";

export type SearchResponse = {
    id: string;
    createdAt: string;
    uri: string;
    content: string;
};

export type SearchRequest = {
    text: string;
};

const useSearch = () => {

    const searchQuery = async (text: String) =>
        (await fetch(`http://localhost:7070/search\?q=${text}`)).json();

    const runSearch = async (req: SearchRequest) => {
        // should be submitting your form to some backend service
        console.log(`submitting ${req.text}`);
        const res = await searchQuery(req.text);
        setQueryResponse(res);
    };

    const [query, setQuery] = createStore<SearchRequest>({
        text: ""
    });

    const [queryResponse, setQueryResponse] = createSignal([]);

    const updateSearchField = (fieldName: string) => (event: Event) => {
        const inputElement = event.currentTarget as HTMLInputElement;
        if (inputElement.type === "checkbox") {
            setQuery({
            [fieldName]: !!inputElement.checked
            });
        } else {
            setQuery({
            [fieldName]: inputElement.value
            });
        }
    }

    return { query, runSearch, updateSearchField, queryResponse }
}

export { useSearch }

and My component calls it here:

import { Component, createResource, createSignal } from 'solid-js';
import { createStore } from "solid-js/store";
import { useSearch } from './useSearch';

const Search: Component = () => {

  const { query, runSearch, updateSearchField, queryResponse } = useSearch();

  const handleSubmit = (event: Event): void => {
    event.preventDefault();
    runSearch(query);
  };

  return (
    <form onSubmit={handleSubmit}>
        <div>
            <label for="query"></label>
            <input id="query" onInput={(e) => updateSearchField("queryString")} />
        </div>
        
        <div>
            <pre>{JSON.stringify(queryResponse(), null, 2)}</pre>
        </div>
        <button type="submit" onclick={() => runSearch}>Search</button>
    </form>
  )
}

export default Search;

I see the submitting entry in the browser console/logs, but I don't get a response. I realize I've got a lot of issues here (like how I should be correctly URL encoding the query parameters), but I just want to get the basics of submitting a call to my service via the button and displaying the results. My questions:

  • How do I get the 2nd version working, with a button click to get the results?
  • How do I correctly deserialize the results from that call into the type I defined?
  • What's the best practice for setting up signals and resources, to correctly get the query and result to render in my component for solidjs.

Solution

  • Here is my solution. It adds support for search being passed in by query parameters as well.

    import { useSearchParams } from '@solidjs/router';
    import { Component, Show, createSignal, onMount } from 'solid-js';
    
    const searchQuery = async (text: string) =>
      (await fetch(`http://localhost:7070/search\?q=${text}`)).json();
    
    
    const Search: Component = () => {
    
      const [searchParams, setSearchParams] = useSearchParams<{ q: string }>();
    
      const [query, setSearchQuery] = createSignal(searchParams.q);
      const [queryResponse, setQueryResponse] = createSignal("");
      const [loading, setLoading] = createSignal(false);
    
      const search = async (q: string) => {
        setLoading(true)
        const queryResult = await searchQuery(q)
        setQueryResponse(JSON.stringify(queryResult, null, 2))
        setSearchParams({q: q})
        setLoading(false)
      }
    
      const handleSubmit = async (event: Event) => {
        event.preventDefault();
        search(query())
      };
    
      onMount(async () => {
        if(searchParams.q != "") {
            search(query())
          }
      })
    
      return (
        <form onSubmit={handleSubmit}>
            <div>
                <label for="query"></label>
                <input id="query" value={query()} onInput={e => setSearchQuery(e.target.value)} />
            </div>
            <Show when={loading()}>
                Loading...
            </Show>
            <Show when={!loading()}>
                <pre>{queryResponse()}</pre>
            </Show>  
            <button type="submit" >Search</button>
        </form>
      )
    }
    
    export default Search;