Search code examples
jqueryajaxclojurecompojure

Clojure: How to implement a search suggestion functionality with Jquery in Clojure/Compojure


I have been battling with how to create a search suggestion box or auto complete box in compojure using jquery and this is what i have but still not working

In my routes.home i have below, i can see my list as output from get-client-list output is like {:id 2, :name "foobar"}

(defn get-list! [{:keys [params]}]
  (log/infof "Pulling client list from db" params)
  (let [list (into {} (db/get-client-list params))]

    ;;list is like hugsql
    (log/infof "List of names -> [%s]" list)
    (if (empty? list)
      (log/errorf "No record found [%s]" list)
      (do
        (log/infof "The list is ->[%s]" list)

         ;; seems am missing something here, dont know
         ;;code for readlist.html is still far below
        (layout/render "readlist.html" (:records list))))))


(defn about-page [{:keys [flash]}]
   (layout/render "home.html"
     (merge nil (select-keys flash [:name :amount :value :er :errors]))))


(defroutes home-routes
   (GET "/getinfo" {:keys [headers params body] :as request}
         (do
           (if (empty? (:user (:session request)))
             (do
               (log/info "Unauthorised attempt to /getinfo URL:" request)
               (response/found "/"))
             (about-page request))))
   (POST "/getinfo" request
           (if (empty? (:user (:session request)))
             (do
               (log/info "Unauthorised attempt to /getinfo URL:" request)
               (response/found "/"))
             (msg! request)))

   (GET "/clientlist" request (get-list! request)))

My home.html

<div class="col-sm-6">
            <p>
                Name:
                <input class="form-control"
                       id="search-box"
                       type="text"
                       name="clientname"
                       value="{{name}}" />
            </p>

            <div id="suggestion-box"></div>
            {% if errors.name %}
            <div class="alert alert-danger">{{errors.name|join}}</div>
            {% endif %}
        </div>

        <div class="col-sm-6">
            <p>
                Amount(in figures):
                <input class="form-control"
                       type="number"
                       name="amount"
                       value="{{amount}}" />
            </p>
            {% if errors.amount %}
            <div class="alert alert-danger">{{errors.amount|join}}</div>
            {% endif %}
        </div>

Then in my base.html, i placed this script within the <head> there

<script src="https://code.jquery.com/jquery-2.1.1.min.js" type="text/javascript"></script>
  <script>
      $(document).ready(function(){
          $("#search-box").keyup(function(){
              $.ajax({
                  type: "GET",

                  //calling this URL in route.home
                  // why are you not working?
                  url: "/clientlist",
                  data:'keyword='+$(this).val(),
                  beforeSend: function(){
                      $("#search-box").css("background","#FFF url(LoaderIcon.gif) no-repeat 165px");
                  },
                  success: function(data){
                      $("#suggestion-box").show();
                      $("#suggestion-box").html(data);
                      $("#search-box").css("background","#FFF");
                  }
              });
          });
      });

      function selectList(val) {
          $("#search-box").val(val);
          $("#suggestion-box").hide();
      }
  </script>

readlist.html code is below

{% for item in records %}
 <li onClick="selectList('{{item.name|join}}');">
  {{item.name|join}}
</li>
{% endfor %}

Please assist, spent hours trying to figure it out. Thanks


Solution

  • From your code, i guess the problem is that the suggestion is not coming up, which means /clientlist is not doing what its supposed to. Your .js script seems fine so it seems the problem is with /clientlist

    Try this, its crude but will work, you can make it simpler by building on it

    (defn process-url-encoded
     "parses the url sent from the .js function"
      [req]
      (let [input (or (:query-string req) (:input-string req))]
        (let [input-vec (apply hash-map (str/split input #"[&=]"))]
          (walk/keywordize-keys input-vec))))
    
    
    (defn get-list! [req]
      (log/infof "Request sent from .js function")
      (try
        (let [{keyword     :keyword} (process-url-encoded req)
              keywd (str "%" keyword "%")
              param (hash-map :name-like keywd)
              list (into {} (db/get-client-list param))]
              (log/infof "The list is -> %s" list)
              (layout/render "readlist.html" {:record list}))
        (catch Exception e
          (log/errorf "No value supplied -> %s" (.getMessage e))
          (layout/render "readlist.html" {:record {}}))))
    

    Then your readlist.html should have this

    <ul id="need-to-style-this-section">
    
        <li onClick="selectList('{{record.name}}');">
             <!-- name should be your database column name-->
            {{record.name}}
        </li>
    </ul>
    

    Also, since you are using clojure template, its likely its based on hugsql syntax, so this should be appropiate

    -- :name get-client-list :? :*
    -- :doc GETS client list
    SELECT name FROM table-name
    WHERE name LIKE :name-like
    ORDER BY name ASC
    

    Have fun!