AutoComplete for Search Widget

Topics: Controls
Feb 12, 2014 at 2:42 AM
Hi All!

I would like to customize my Search widget to add autocomplete and use the Category List as input for suggested strings.

Anyone knows where to start? I see that the widget is using blog:SearchBox but I don't see the actual textbox control that I can replace with AutoComplete Ajax Control Toolkit.

Thanks!
Feb 12, 2014 at 3:16 AM
Hi Galagerardo,

Here some files to look at to get you started :)


http://blogengine.codeplex.com/SourceControl/latest#BlogEngine/BlogEngine.Core/Search.cs


http://blogengine.codeplex.com/SourceControl/latest#BlogEngine/BlogEngine.NET/AppCode/Controls/SearchBox.cs

The "Search Widget" in a sense of a "Widget" is not really 100% a widget but more of a "module" since it is included the main core.

So where your "Normal" widgets are only located in two locations

/AppCode/Controls/
/Widgets


The "Search Widget" is also has file located in the

/BlogEngine.Core/ folder too.

As far I can quickly find the two files above should be it other than the category related files.


Main point you have to "Dig" through the code :)


Would like to hear your progress on this in the future.

Sounds interesting.
Feb 13, 2014 at 5:33 PM
Edited Feb 13, 2014 at 7:56 PM
This uses a javascript plugin called autocomplete.
In the "dist" folder in the download find jquery.autocomplete.min and drop into blogs Scripts/Auto folder.

In theme CSS add following for starters:
.autocomplete-suggestions { border: 1px solid #999; background: #FFF; overflow: auto; }
.autocomplete-suggestion { padding: 2px 5px; white-space: nowrap; overflow: hidden; }
.autocomplete-selected { background: #F0F0F0; }
.autocomplete-suggestions strong { font-weight: normal; color: #3399FF; }
Modified SearchBox.cs control from BE 2.8
namespace App_Code.Controls
{
    using System;
    using System.Text;
    using System.Web;
    using System.Web.UI;
    using System.Linq;

    using BlogEngine.Core;

    /// <summary>
    /// Includes a reference to a JavaScript.
    ///     <remarks>
    /// This control is needed in order to let the src
    ///         attribute of the script tage be relative to the root.
    ///     </remarks>
    /// </summary>
    public class SearchBox : Control
    {

        #region Public Methods

        /// <summary>
        /// Renders the control as a script tag.
        /// </summary>
        /// <param name="writer">
        /// The writer.
        /// </param>
        public override void RenderControl(HtmlTextWriter writer)
        {
            writer.Write(this.BuildHtml() + this.AddScript());            
        }

        #endregion

        #region Methods

        /// <summary>
        /// The build html.
        /// </summary>
        private string BuildHtml()
        {
            var text = this.Context.Request.QueryString["q"] != null
                           ? HttpUtility.HtmlEncode(this.Context.Request.QueryString["q"])
                           : string.Empty;
            var sb = new StringBuilder();

            sb.AppendLine("<div id=\"searchbox\">");
            sb.Append("<label for=\"searchfield\" style=\"display:none\">Search</label>");
            sb.AppendFormat(
                "<input type=\"text\" id=\"searchfield\" autocomplete=\"off\" onkeypress=\"if(event.keyCode==13) return BlogEngine.search('{1}')\" onfocus=\"BlogEngine.searchClear('{2}')\" onblur=\"BlogEngine.searchClear('{2}')\" />",
                text,
                Utils.RelativeWebRoot,
                text.Replace("'", "\\'"));
            sb.AppendFormat(
                "<input type=\"button\" value=\"{0}\" id=\"searchbutton\" onclick=\"BlogEngine.search('{1}');\" onkeypress=\"BlogEngine.search('{1}');\" />",
                BlogSettings.Instance.SearchButtonText,
                Utils.RelativeWebRoot);

            if (BlogSettings.Instance.EnableCommentSearch && BlogSettings.Instance.ShowIncludeCommentsOption)
            {
                var check = this.Context.Request.QueryString["comment"] != null ? "checked=\"checked\"" : string.Empty;
                sb.AppendFormat("<div id=\"search-include-comments\"><input type=\"checkbox\" id=\"searchcomments\" {0} />", check);
                if (!string.IsNullOrEmpty(BlogSettings.Instance.SearchCommentLabelText))
                {
                    sb.AppendFormat(
                        "<label for=\"searchcomments\">{0}</label></div>", BlogSettings.Instance.SearchCommentLabelText);
                }
            }

            sb.AppendLine("</div>");
            return sb.ToString();
        }        

        private string AddScript()
        {
            var sb = new StringBuilder();
            sb.AppendLine("<script type=\"text/javascript\">");
            sb.AppendLine("$(function() {");
            sb.AppendLine("var cats = [");
            foreach (Category cat in Category.Categories)
            {
                if (cat.Posts.Count > 0)
                {
                    sb.Append("'" + cat.Title + "',");
                }
            }
            if (sb[sb.Length - 1] == ',')
            {
                sb.Length -= 1;
            }
            sb.AppendLine("];");
            sb.AppendLine("$('#searchfield, #q').autocomplete({lookup: cats, delimiter: ' '});");            
            sb.AppendLine("});");
            sb.AppendLine("</script>");
            return sb.ToString(); ;
        }

        #endregion
    }
}
Note: This will work in search page and with one SearchBox control per page.
Some themes use the SearchBox control at page top, so it's that or add widget - breaks with both.
This is bare bones, you can add functionality or code to use different plugin.
EDIT: Search term space delimiter added to control AddScript()
Feb 14, 2014 at 3:30 AM
Hi Andy!

Thanks for all the efforts.

In jquery.autocomplete.js, I changed the minChars from 1 to 3. This seems to better. It doesn't get a lot of matches but when it does get a match, it's dead on!

Yes, I'm really happy with this. Much better than the normal search.