Lift - Autocomplete with Ajax Submission

后端 未结 2 995
难免孤独
难免孤独 2021-02-09 02:32

I would like to use an autocomplete with ajax. So my goal is to have:

  • When the user types something in the text field, some suggestions provided by the server

相关标签:
2条回答
  • 2021-02-09 03:31

    Here's what you need to do.

    1) Make sure you are using Lift 2.5-SNAPSHOT (this is doable in earlier versions, but it's more difficult)

    2) In the snippet you use to render the page, use SHtml.ajaxCall (in particular, you probably want this version: https://github.com/lift/framework/blob/master/web/webkit/src/main/scala/net/liftweb/http/SHtml.scala#L170) which will allow you to register a server side function that accepts your search term and return a JSON response containing the completions. You will also register some action to occur on the JSON response with the JsContext.

    3) The ajaxCall above will return a JsExp object which will result in the ajax request when it's invoked. Embed it within a javascript function on the page using your snippet.

    4) Wire them up with some client side JS.

    Update - Some code to help you out. It can definitely be done more succinctly with Lift 2.5, but due to some inconsistencies in 2.4 I ended up rolling my own ajaxCall like function. S.fmapFunc registers the function on the server side and the function body makes a Lift ajax call from the client, then invokes the res function (which comes from jQuery autocomplete) on the JSON response.

    My jQuery plugin to "activate" the text input

    
    (function($) {
        $.fn.initAssignment = function() {
         return this.autocomplete({
             autoFocus: true,
             source: function(req, res) {
                  search(req.term, res);
             },
             select: function(event, ui) {
                 assign(ui.item.value, function(data){
                    eval(data);
                 });
                 event.preventDefault();
                 $(this).val("");
             },
             focus: function(event, ui) {
                 event.preventDefault();
             }
         });
        }
    })(jQuery);
    

    My Scala code that results in the javascript search function:

    
    def autoCompleteJs = JsRaw("""
            function search(term, res) {
            """ +
                 (S.fmapFunc(S.contextFuncBuilder(SFuncHolder({ terms: String =>
                    val _candidates = 
                      if(terms != null && terms.trim() != "")
                        assigneeCandidates(terms)
                      else
                        Nil
                    JsonResponse(JArray(_candidates map { c => c.toJson }))
                 })))
                 ({ name => 
                   "liftAjax.lift_ajaxHandler('" + name 
                 })) + 
                 "=' + encodeURIComponent(term), " +
                 "function(data){ res(data); }" +
                 ", null, 'json');" +
            """
            }
            """)
    

    Update 2 - To add the function above to your page, use a CssSelector transform similar to the one below. The >* means append to anything that already exists within the matched script element. I've got other functions I've defined on that page, and this adds the search function to them.

    
    "script >*" #> autoCompleteJs
    

    You can view source to verify that it exists on the page and can be called just like any other JS function.

    0 讨论(0)
  • 2021-02-09 03:39

    With the help of Dave Whittaker, here is the solution I came with.

    I had to change some behaviors to get:

    • the desired text (from autocomplete or not) in an ajaxText element
    • the possibility to have multiple autocomplete forms on same page
    • submit answer on ajaxText before blurring when something is selected in autocomplete suggestions.

    Scala part

    private def getSugggestions(current: String, limit: Int):List[String] = {
      /* returns list of suggestions */
    }
    
    private def autoCompleteJs = AnonFunc("term, res",JsRaw(
      (S.fmapFunc(S.contextFuncBuilder(SFuncHolder({ terms: String =>
        val _candidates =
          if(terms != null && terms.trim() != "")
            getSugggestions(terms, 5)
          else
            Nil
        JsonResponse(JArray(_candidates map { c => JString(c)/*.toJson*/ }))
      })))
      ({ name =>
        "liftAjax.lift_ajaxHandler('" + name
      })) +
      "=' + encodeURIComponent(term), " +
      "function(data){ res(data); }" +
      ", null, 'json');"))
    
    
    def xml = {
      val id = "myId" //possibility to have multiple autocomplete fields on same page
      Script(OnLoad(JsRaw("jQuery('#"+id+"').createAutocompleteField("+autoCompleteJs.toJsCmd+")")))     ++
      SHtml.ajaxText(cell.get, s=>{ cell.set(s); SearchMenu.recomputeResults; Noop}, "id" -> id)
    }
    

    Script to insert into page header:

    (function($) {
        $.fn.createAutocompleteField = function(search) {
            return this.autocomplete({
                autoFocus: true,
                source: function(req, res) {
                    search(req.term, res);
                },
                select: function(event, ui) {
                    $(this).val(ui.item.value);
                    $(this).blur();
                },
                focus: function(event, ui) {
                    event.preventDefault();
                }
            });
        }
    })(jQuery);
    

    Note: I accepted Dave's answer, mine is just to provide a complete answer for my purpose

    0 讨论(0)
提交回复
热议问题