From 0807597a3c2161c7951d017e740c992c3b1ea320 Mon Sep 17 00:00:00 2001 From: Shannon Date: Tue, 16 Dec 2014 15:50:54 +1100 Subject: [PATCH] Fixes: U4-5977 The debounce timeout on the main search is too big which produces a lag in searching & U4-5994 Global search needs to only show results for the 'last' search request made and ignore previous ones --- .../directives/umbtreesearchbox.directive.js | 24 +++++++-- .../src/common/resources/entity.resource.js | 40 +++++++++----- .../src/common/services/search.service.js | 4 +- .../src/controllers/search.controller.js | 54 ++++++++++++------- 4 files changed, 84 insertions(+), 38 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/umbtreesearchbox.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/umbtreesearchbox.directive.js index 09411112cc..c9dd185495 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/umbtreesearchbox.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/umbtreesearchbox.directive.js @@ -5,7 +5,7 @@ * @element ANY * @restrict E **/ -function treeSearchBox(localizationService, searchService) { +function treeSearchBox(localizationService, searchService, $q) { return { scope: { searchFromId: "@", @@ -34,19 +34,37 @@ function treeSearchBox(localizationService, searchService) { scope.showSearch = "false"; } + //used to cancel any request in progress if another one needs to take it's place + var canceler = null; + function performSearch() { if (scope.term) { scope.results = []; + + //a canceler exists, so perform the cancelation operation and reset + if (canceler) { + console.log("CANCELED!"); + canceler.resolve(); + canceler = $q.defer(); + } + else { + canceler = $q.defer(); + } var searchArgs = { - term: scope.term + term: scope.term, + canceler: canceler }; + //append a start node context if there is one if (scope.searchFromId) { searchArgs["searchFrom"] = scope.searchFromId; } + searcher(searchArgs).then(function (data) { scope.searchCallback(data); + //set back to null so it can be re-created + canceler = null; }); } } @@ -57,7 +75,7 @@ function treeSearchBox(localizationService, searchService) { performSearch(); } }); - }, 20)); + }, 200)); var searcher = searchService.searchContent; //search diff --git a/src/Umbraco.Web.UI.Client/src/common/resources/entity.resource.js b/src/Umbraco.Web.UI.Client/src/common/resources/entity.resource.js index 8e5ed0a9b6..267df06ca3 100644 --- a/src/Umbraco.Web.UI.Client/src/common/resources/entity.resource.js +++ b/src/Umbraco.Web.UI.Client/src/common/resources/entity.resource.js @@ -263,20 +263,26 @@ function entityResource($q, $http, umbRequestHelper) { * @returns {Promise} resourcePromise object containing the entity array. * */ - search: function (query, type, searchFrom) { + search: function (query, type, searchFrom, canceler) { var args = [{ query: query }, { type: type }]; if (searchFrom) { args.push({ searchFrom: searchFrom }); } + var httpConfig = {}; + if (canceler) { + httpConfig["timeout"] = canceler; + } + return umbRequestHelper.resourcePromise( - $http.get( - umbRequestHelper.getApiUrl( - "entityApiBaseUrl", - "Search", - args)), - 'Failed to retrieve entity data for query ' + query); + $http.get( + umbRequestHelper.getApiUrl( + "entityApiBaseUrl", + "Search", + args), + httpConfig), + 'Failed to retrieve entity data for query ' + query); }, @@ -301,15 +307,21 @@ function entityResource($q, $http, umbRequestHelper) { * @returns {Promise} resourcePromise object containing the entity array. * */ - searchAll: function (query) { + searchAll: function (query, canceler) { + + var httpConfig = {}; + if (canceler) { + httpConfig["timeout"] = canceler; + } return umbRequestHelper.resourcePromise( - $http.get( - umbRequestHelper.getApiUrl( - "entityApiBaseUrl", - "SearchAll", - [{ query: query }])), - 'Failed to retrieve entity data for query ' + query); + $http.get( + umbRequestHelper.getApiUrl( + "entityApiBaseUrl", + "SearchAll", + [{ query: query }]), + httpConfig), + 'Failed to retrieve entity data for query ' + query); } }; diff --git a/src/Umbraco.Web.UI.Client/src/common/services/search.service.js b/src/Umbraco.Web.UI.Client/src/common/services/search.service.js index ab4bac2235..f945070a2a 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/search.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/search.service.js @@ -88,7 +88,7 @@ angular.module('umbraco.services') throw "args.term is required"; } - return entityResource.search(args.term, "Document", args.searchFrom).then(function (data) { + return entityResource.search(args.term, "Document", args.searchFrom, args.canceler).then(function (data) { _.each(data, function (item) { configureContentResult(item); }); @@ -138,7 +138,7 @@ angular.module('umbraco.services') throw "args.term is required"; } - return entityResource.searchAll(args.term).then(function (data) { + return entityResource.searchAll(args.term, args.canceler).then(function (data) { _.each(data, function(resultByType) { switch(resultByType.type) { diff --git a/src/Umbraco.Web.UI.Client/src/controllers/search.controller.js b/src/Umbraco.Web.UI.Client/src/controllers/search.controller.js index db3cc3e0bc..386e9a6712 100644 --- a/src/Umbraco.Web.UI.Client/src/controllers/search.controller.js +++ b/src/Umbraco.Web.UI.Client/src/controllers/search.controller.js @@ -7,7 +7,7 @@ * Controls the search functionality in the site * */ -function SearchController($scope, searchService, $log, $location, navigationService) { +function SearchController($scope, searchService, $log, $location, navigationService, $q) { $scope.searchTerm = null; $scope.searchResults = []; @@ -87,25 +87,41 @@ function SearchController($scope, searchService, $log, $location, navigationServ $scope.selectedItem = group.results[itemIndex]; } - //watch the value change but don't do the search on every change - that's far too many queries - // we need to debounce - var debounced = _.debounce(function () { - if ($scope.searchTerm) { - $scope.isSearching = true; - navigationService.showSearch(); - $scope.selectedItem = undefined; - searchService.searchAll({ term: $scope.searchTerm }).then(function (result) { - $scope.groups = _.filter(result, function(group){return group.results.length > 0;}); - }); - }else{ - $scope.isSearching = false; - navigationService.hideSearch(); - $scope.selectedItem = undefined; - } - }, 300); + //used to cancel any request in progress if another one needs to take it's place + var canceler = null; - - $scope.$watch("searchTerm", debounced); + $scope.$watch("searchTerm", _.debounce(function (newVal, oldVal) { + $scope.$apply(function() { + if ($scope.searchTerm) { + if (newVal !== null && newVal !== undefined && newVal !== oldVal) { + $scope.isSearching = true; + navigationService.showSearch(); + $scope.selectedItem = undefined; + + //a canceler exists, so perform the cancelation operation and reset + if (canceler) { + console.log("CANCELED!"); + canceler.resolve(); + canceler = $q.defer(); + } + else { + canceler = $q.defer(); + } + + searchService.searchAll({ term: $scope.searchTerm, canceler: canceler }).then(function(result) { + $scope.groups = _.filter(result, function (group) { return group.results.length > 0; }); + //set back to null so it can be re-created + canceler = null; + }); + } + } + else { + $scope.isSearching = false; + navigationService.hideSearch(); + $scope.selectedItem = undefined; + } + }); + }, 200)); } //register it