2

I am trying to create a JQuery Autocomplete Widget which returns Paginated Answers. I am using the only solution I have found on the web which I am trying to customize for my needs. Everything seems to be working great and the logic seems to be correct too. The problems comes from the Next and Prev buttons, which are supposed to make Ajax request for more answers.

When I write a search term, the answers come back great with the buttons on top. Вhen I click on a button it closes the menu with the search-answers...But when i put a breakpoint in my controller the buttons do work, but than the JS breaks and the answers are not show but just the buttons. I am not great with JavaScript, any ideas would be appreciated... Below is the creation of the widget.

  $("#autoBox").paginatedAutocomplete({
            pageSize: 5,
            sourceUrl: '/Filter/AutocompleteSearch'
        });


        $.widget("custom.paginatedAutocomplete", $.ui.autocomplete, {

        options: {
            minLength: 4,
            delay: 500,
            source: function (request, response) {

                var self = this;

                $.ajax({
                    url: this.options.sourceUrl,
                    type: "GET",
                    dataType: "json",
                    data: {
                        pageSize: self.options.pageSize,
                        search: request.term,
                        pageIndex: self._pageIndex
                    },
                    success: function (data) {

                        var items = data.suggestions;

                        self._totalItems = data.total;

                        // Create a menu item for each of the items on the page.
                        response($.map(items, function (item) {
                            return {
                                label: javaDropLabel(item.catclass, item.cat, item.value, item.data),
                                value: item.value + ", " + item.data,
                                id: item.id,
                                obj: item.objId,
                                cat: item.catclass
                            }
                        }));
                    },
                    error: function (status, error) {
                        window.bodyBlockRemove();
                        var r = jQuery.parseJSON(response.responseText);
                        alert("Message:" + r.Message);
                    }
                });
            },
            focus: function () {

                // prevent value inserted on focus
                return false;
            }

        },
        _renderItem: function (ul, item) {
            //custom view for the answers
            return $("<li>")
              .append(item.label)
              .appendTo(ul);
        },
        search: function (value, event) {

            // Start a fresh search; Hide pagination panel and reset to 1st page.
            this._pageIndex = 0;

            $.ui.autocomplete.prototype.search.call(this, value, event);
        },
        select: function (item) {
            // Keep track of the selected item.
            self._previousSelectedItem = item;

        },
        close: function (event) {

            // Close the pagination panel when the selection pop-up is closed.
            this._paginationContainerElement.css('display', 'none');

            $.ui.autocomplete.prototype.close.call(this, event);
        },
        _previousSelectedItem: null,
        _totalPages: null,
        _totalItems: null,
        _pageIndex: 0,
        _create: function () {

            // Create the DOM structure to support the paginated autocomplete.

            this.element.after("<div class='ui-autocomplete-pagination-results'></div>");
            this._resultsContainerElement = this.element.next("div.ui-autocomplete-pagination-results");
            this._resultsContainerElement.append("<div class='ui-autocomplete-pagination-container'>" +
                "<button type='button' class='previous-page' style='float: left;'></button>" +
                "<button type='button' class='next-page' style='float: left;'></button>" +
                "<div style='float:left; width:65%;' class='ui-autocomplete-pagination-details'>" +
                "</div>" +
                "</div>");
            this._paginationContainerElement = this._resultsContainerElement.children("div.ui-autocomplete-pagination-container");
            this._nextPageElement = this._paginationContainerElement.find("button.next-page");
            this._previousPageElement = this._paginationContainerElement.find("button.previous-page");
            this._paginationDetailsElement = this._paginationContainerElement.find("div.ui-autocomplete-pagination-details");

            this._nextPageElement.button({ text: false, icons: { primary: "ui-icon ui-icon-arrowthick-1-e" } });
            this._previousPageElement.button({ text: false, icons: { primary: "ui-icon-arrowthick-1-w" } });

            // Append the menu items (and related content) to the specified element.
            if (this.options.appendTo !== null) {
                this._paginationContainerElement.appendTo(this._resultsContainerElement);
                this._resultsContainerElement.appendTo(this.options.appendTo);
                this.options.appendTo = this._resultsContainerElement;
            }
            else {
                this.options.appendTo = this._resultsContainerElement;
            }

            // Hide default JQuery Autocomplete details (want to use our own blurb).
            $(this.element).next("span.ui-helper-hidden-accessible").css("display", "none");

            // Event handler(s) for the next/previous pagination buttons. - PROBLEM
            this._on(this._nextPageElement, {
                click: this._nextPage
            });
            this._on(this._previousPageElement, {
                click: this._previousPage
            });

            // Event handler(s) for the autocomplete textbox.
            this._on(this.element, {
                blur: function (event) {
                    // When losing focus hide the pagination panel
                    this._pageIndex = 0;
                },
                paginatedautocompleteopen: function (event) {

                    // Autocomplete menu is now visible.

                    // Update pagination information.

                    var self = this,
                        paginationFrom = null,
                        paginationTo = null,
                        menuOffset = this.menu.element.offset();

                    self._totalPages = Math.ceil(self._totalItems / self.options.pageSize);

                    paginationFrom = self._pageIndex * self.options.pageSize + 1;
                    paginationTo = ((self._pageIndex * self.options.pageSize) + self.options.pageSize);

                    if (paginationTo > self._totalItems) {
                        paginationTo = self._totalItems;
                    }

                    // Align the pagination container with the menu.
                    this._paginationContainerElement.offset({ top: menuOffset.top, left: menuOffset.left });

                    // Modify the list generated by the autocomplete so that it appears below the pagination controls.
                    this._resultsContainerElement
                        .find("ul").prependTo(".ui-autocomplete-pagination-results");

                    this._paginationDetailsElement.html(paginationFrom.toString() + " - " + paginationTo.toString() + " / " + self._totalItems.toString());
                }
            });

            // Event handler(s) for the pagination panel.
            this._on(this._paginationContainerElement, {

                mousedown: function (event) {

                    // The following will prevent the pagination panel and selection menu from losing focus (and disappearing).

                    // Prevent moving focus out of the text field
                    event.preventDefault();

                    // IE doesn't prevent moving focus even with event.preventDefault()
                    // so we set a flag to know when we should ignore the blur event
                    this.cancelBlur = true;
                    this._delay(function () {
                        delete this.cancelBlur;
                    });
                }
            });

            // Now we're going to let the default _create() to do it's thing. This will create the autocomplete pop-up selection menu.
            $.ui.autocomplete.prototype._create.call(this);

            // Event handler(s) for the autocomplete pop-up selection menu.
            this._on(this.menu.element, {

                menuselect: function (event, ui) {

                    var item = ui.item.data("ui-autocomplete-item");    // Get the selected item.

                    window.goToLink(item.obj, item.id, item.cat);
                }
            });

        },
        //PROBLEM METHODS 
        _nextPage: function (event) {
            if (this._pageIndex < this._totalPages - 1) {

                this._pageIndex++;
                $.ui.autocomplete.prototype._search.call(this, this.term);
              //  setTimeout(function () { alert('некст'); }, 5000);
            }
        },
        _previousPage: function (event) {
            if (this._pageIndex > 0) {
                this._pageIndex--;
              //  setTimeout(function () { alert('бифор'); }, 5000);
                $.ui.autocomplete.prototype._search.call(this, this.term);
            }
        },
        _change: function (event) {

            // Clear the textbox if an item wasn't selected from the menu.
            if (((this.selectedItem === null) && (this._previousSelectedItem === null)) ||
            (this.selectedItem === null) && (this._previousSelectedItem !== null) && (this._previousSelectedItem.label !== this._value())) {

                // Clear values.
                this._value("");
            }

            $.ui.autocomplete.prototype._change.call(this, event);
        },
        _destroy: function () {
            this._paginationContainerElement.remove();
            this._resultsContainerElement.remove();
            $.ui.autocomplete.prototype._destroy.call(this);
        },
        __response: function (content) {
            var self = this;

            self._totalItemsOnPage = content.length;

            if (self._totalItemsOnPage > 0) {
                self._paginationContainerElement.css('display', 'block');
            }
            else {
                self._paginationContainerElement.css('display', 'none');
            }

            $.ui.autocomplete.prototype.__response.call(this, content);
        }
    });

    $.widget.bridge("paginatedAutocomplete", $.custom.paginatedAutocomplete);

function javaDropLabel(catclass, cat, value, data) {

        var l = "<div><span class='" + catclass + "'>" + cat + "</span>" + value + ", " + data + "</div>";
        return l;

    }

After more testing/trying I figured that if I remove manually the display:none property in the DOM from the elements with the answers and the buttons everything works great with the buttons. I guess I have to figure out how to teach the widgets function not to hide the menu only when its clicked on a button. Step by Step... I got to this page: Prevent Jquery autocomplete options from closing after each selection

First I tried and created logic into the CLOSE: function(ev)... as it was shown. But the problem was every time I hit next/prev buttons the menu blinks quite obvious and shows the next answers.. so I figured I have to use the CLOSE only when the menu must be finally closed. Than I extended the _CHANGE in order to call the CLOSE method if you click somewhere not on the search answers. The final code below:

        $.widget("custom.paginatedAutocomplete", $.ui.autocomplete, {

        options: {
            minLength: 4,
            delay: 500,
            source: function (request, response) {

                var self = this;

                $.ajax({
                    url: this.options.sourceUrl,
                    type: "GET",
                    dataType: "json",
                    data: {
                        pageSize: self.options.pageSize,
                        search: request.term,
                        pageIndex: self._pageIndex
                    },
                    success: function (data) {

                        var items = data.suggestions;

                        self._totalItems = data.total;

                        // Create a menu item for each of the items on the page.
                        response($.map(items, function (item) {
                            return {
                                label: javaDropLabel(item.catclass, item.cat, item.value, item.data),
                                value: item.value + ", " + item.data,
                                id: item.id,
                                obj: item.objId,
                                cat: item.catclass
                            }
                        }));
                    },
                    error: function (status, error) {
                        window.bodyBlockRemove();
                        var r = jQuery.parseJSON(response.responseText);
                        alert("Message:" + r.Message);
                    }
                });
            },
            focus: function () {
                // prevent value inserted on focus
                return false;
            }        
        },
        _renderItem: function (ul, item) {
            //custom view for the answers
            return $("<li>")
              .append(item.label)
              .appendTo(ul);
        },
        search: function (value, event) {
            // Start a fresh search; Hide pagination panel and reset to 1st page.
            this._pageIndex = 0;
            $.ui.autocomplete.prototype.search.call(this, value, event);
        },
        select: function (item) {
            // Keep track of the selected item.
            self._previousSelectedItem = item;          
        },
        close: function (event) {},
        _previousSelectedItem: null,
        _totalPages: null,
        _totalItems: null,
        _pageIndex: 0,
        _create: function () {

            // Create the DOM structure to support the paginated autocomplete.

            this.element.after("<div class='ui-autocomplete-pagination-results'></div>");
            this._resultsContainerElement = this.element.next("div.ui-autocomplete-pagination-results");
            this._resultsContainerElement.append("<div class='ui-autocomplete-pagination-container'>" +
                "<button type='button' class='previous-page' style='float: left;'></button>" +
                "<button type='button' class='next-page' style='float: left;'></button>" +
                "<div style='float:left; width:65%;' class='ui-autocomplete-pagination-details'>" +
                "</div>" +
                "</div>");
            this._paginationContainerElement = this._resultsContainerElement.children("div.ui-autocomplete-pagination-container");
            this._nextPageElement = this._paginationContainerElement.find("button.next-page");
            this._previousPageElement = this._paginationContainerElement.find("button.previous-page");
            this._paginationDetailsElement = this._paginationContainerElement.find("div.ui-autocomplete-pagination-details");

            this._nextPageElement.button({ text: false, icons: { primary: "ui-icon ui-icon-arrowthick-1-e" } });
            this._previousPageElement.button({ text: false, icons: { primary: "ui-icon-arrowthick-1-w" } });

            // Append the menu items (and related content) to the specified element.
            if (this.options.appendTo !== null) {
                this._paginationContainerElement.appendTo(this._resultsContainerElement);
                this._resultsContainerElement.appendTo(this.options.appendTo);
                this.options.appendTo = this._resultsContainerElement;
            }
            else {
                this.options.appendTo = this._resultsContainerElement;
            }

            // Hide default JQuery Autocomplete details (want to use our own blurb).
            $(this.element).next("span.ui-helper-hidden-accessible").css("display", "none");

            // Event handler(s) for the next/previous pagination buttons.
            this._on(this._nextPageElement, {
                click: this._nextPage
            });
            this._on(this._previousPageElement, {
                click: this._previousPage
            });

            // Event handler(s) for the autocomplete textbox.
            this._on(this.element, {
                blur: function (event) {
                    // When losing focus hide the pagination panel
                    this._pageIndex = 0;
                },
                paginatedautocompleteopen: function (event) {

                    // Autocomplete menu is now visible.
                    // Update pagination information.

                    var self = this,
                        paginationFrom = null,
                        paginationTo = null,
                        menuOffset = this.menu.element.offset();

                    self._totalPages = Math.ceil(self._totalItems / self.options.pageSize);

                    paginationFrom = self._pageIndex * self.options.pageSize + 1;
                    paginationTo = ((self._pageIndex * self.options.pageSize) + self.options.pageSize);

                    if (paginationTo > self._totalItems) {
                        paginationTo = self._totalItems;
                    }

                    // Align the pagination container with the menu.
                    this._paginationContainerElement.offset({ top: menuOffset.top, left: menuOffset.left });

                    // Modify the list generated by the autocomplete so that it appears below the pagination controls.
                    this._resultsContainerElement
                        .find("ul").prependTo(".ui-autocomplete-pagination-results");

                    this._paginationDetailsElement.html(paginationFrom.toString() + " - " + paginationTo.toString() + " / " + self._totalItems.toString());
                }
            });

            // Event handler(s) for the pagination panel.
            this._on(this._paginationContainerElement, {

                mousedown: function (event) {
                    // The following will prevent the pagination panel and selection menu from losing focus (and disappearing).
                    // Prevent moving focus out of the text field
                    event.preventDefault();
                    // IE doesn't prevent moving focus even with event.preventDefault()
                    // so we set a flag to know when we should ignore the blur event
                    this.cancelBlur = true;
                    this._delay(function () {
                        delete this.cancelBlur;
                    });
                }
            });
            // Now we're going to let the default _create() to do it's thing. This will create the autocomplete pop-up selection menu.
            $.ui.autocomplete.prototype._create.call(this);

            // Event handler(s) for the autocomplete pop-up selection menu.
            this._on(this.menu.element, {
                menuselect: function (event, ui) {
                    var item = ui.item.data("ui-autocomplete-item");    // Get the selected item.
                    window.goToLink(item.obj, item.id, item.cat);
                }
            });

        },       
        _nextPage: function (event) {
            if (this._pageIndex < this._totalPages - 1) {
                this._pageIndex++;
                $.ui.autocomplete.prototype._search.call(this, this.term);
            } else {
                this._pageIndex = 0;
                $.ui.autocomplete.prototype._search.call(this, this.term);
            }
        },
        _previousPage: function (event) {
            if (this._pageIndex > 0) {
                this._pageIndex--;             
                $.ui.autocomplete.prototype._search.call(this, this.term);
            } else {
                this._pageIndex = this._totalPages - 1;
                $.ui.autocomplete.prototype._search.call(this, this.term);
            }
        },
        _change: function (event) {

            // Clear the textbox and closes the menu if an item wasn't selected from the menu but was clicked somewhere.
            if (((this.selectedItem === null) && (this._previousSelectedItem === null)) ||
            (this.selectedItem === null) && (this._previousSelectedItem !== null) && (this._previousSelectedItem.label !== this._value())) {
                // Clear values.
                this._value("");
                //Hide the buttons
                this._paginationContainerElement.css('display', 'none');
                //close the menu
                $.ui.autocomplete.prototype.close.call(this, event);
            }          
            $.ui.autocomplete.prototype._change.call(this, event);
        },
        _destroy: function () {
            this._paginationContainerElement.remove();
            this._resultsContainerElement.remove();
            $.ui.autocomplete.prototype._destroy.call(this);
        },
        __response: function (content) {
            var self = this;

            self._totalItemsOnPage = content.length;

            if (self._totalItemsOnPage > 0) {
                self._paginationContainerElement.css('display', 'block');
            }
            else {
                self._paginationContainerElement.css('display', 'none');
            }

            $.ui.autocomplete.prototype.__response.call(this, content);
        }
    }); 

Now I only have to fix the design..but it worx great !

Customized Autocomplete with Pagination

CHooooo
  • 21
  • 3
  • Maybe I'm missing it, but I don't see any actual question that you may be asking? The autocomplete widget may not be the best starting place for what you did - you had to override a load of the standard functionality, and in the process, changed how it works from a users point of view. – John - Not A Number Sep 01 '17 at 01:36
  • I had a question at the start... which was able tor resolve myself. As for the widget...I was told by my boss I have to use exactly this one...and It has to be made to look and work so and so.. :_) ..You Know.. 'Its software..anything could be done' kinda attitude – CHooooo Sep 01 '17 at 07:08

0 Answers0