SapUI5: Прерываем oData запрос (abort oData request)
Одна из самых частых задач в SAP HCM для программиста — поиск. Но чем более не точный поиск и чем больше полей, по которым мы ищем, тем дольше будут отрабатывать наши запросы, создавая программисту лишние проблемы. И об одной такой проблеме мы и поговорим!
Представим такую задачу: Необходимо реализовать живой поиск сотрудников в системе. Пользователь может ввести в поле поиска:
- Табельный номер.
- Фамилия, Имя, Отчество.
- Должность (наименование или id).
- Подразделение (наименование или id).
- и т. п.
Пользователь набирает в строку поиска: «Ива» и у нас запускается поиск данной подстроки в большом количестве таблиц. Затем пользователь уточняет свой запрос, продолжая ввод строки: «Иванов», в следствии чего запускается еще один поиск в системе. В итоге у нас получается два запроса и вполне очевидно, что второй запрос будет выполнен быстрее, чем первый. По факту выполнения второго запроса будет сформирован ответ и отдан на обработку на frontend часть, которая, допустим, нарисует ответ во всплывающем окне. Но, первый запрос, хоть позже, но тоже будет выполнен. И получится так, что ответ первого запроса перетрет ответ второго запроса. Эту проблему мы и решим в тестовом примере!
Тестовый пример — https://github.com/kannade/sapui5_abort_request.
Основной метод поиска onLineSearch, где мы смотрим на то, что строка больше трех символов и только в этом случае запускаем поиск:
30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 | onLineSearch: function(oEvent) { var oSource = oEvent.getSource(); var sNewValue = oEvent.getParameter("newValue"); if (sNewValue.length >= 3) { if (this._oPopover && this._oPopover.isOpen()) { this.setFilter(sNewValue); } else { this.openBy(oSource, sNewValue); } } else if (this._oPopover) { this._oPopover.close(); if (sNewValue === "") { this.fireSuggestionCompleted({ selData: null, source: this._oSource }); } } }, |
Если строка состоит из 3-х или больше символов, то переходим в метод setFilter. В данном методе мы создаем таймер с продолжительностью 0.7 секунды. Данный таймер позволяет нам отправлять запрос на сервер только в том случае, если после последнего ввода символа в строку поиска прошло более 0.7 секунды (позволяем пользователю максимально уточнить свой запрос, только после этого передаем наш запрос на backend):
51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 | setFilter: function(sValue) { clearTimeout(this._timerSearch); this._timerSearch = setTimeout(function() { if (typeof this._oRequest === "object") { if (this._oRequest.abort) { this._oRequest.abort(); } } this._oPopover.setBusy(true); this._readData(sValue).then( function(res) { var oContentModel = { "aItems": res }; var oItemModel = new sap.ui.model.json.JSONModel({}); oItemModel.setData(oContentModel); this._oList.setModel(oItemModel); this._oList.bindAggregation("items", "/aItems", this._oTemplate2); this._oPopover.setBusy(false); }.bind(this) ); }.bind(this), 700); }, |
Обратим внимание на 57 строку, где мы выполняем метод abort у запроса, если он есть. Тем самым делаем предыдущий запрос больше неактуальным и решаем главную проблему данного поста — оставляем только самый новый запрос актуальным, не позволяя более ранним запросам, которые могут выполнятся дольше, «портить» ответы запросов, которые выполнились раньше.
И теперь сам поиск, где мы запишем сам запрос в переменную this._oRequest:
77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 | _readData: function(sValue) { var oModel = this._oList.getModel("oMainModel"); var promise = new Promise(function(resolve, reject) { if (!oModel) { reject(false); return; } this._oRequest = oModel.read("/searchSet", { filters: [ new sap.ui.model.Filter("string", FilterOperator.Contains, sValue) ], urlParameters: { "$top": 99, "$skip": 0 }, success: function(oData, response) { this._oRequest = undefined; resolve(oData.results); }.bind(this), error: function() { this._oRequest = undefined; reject(false); }.bind(this) }); }.bind(this)); return promise; } |
Демонстрация примера, где метод abort закоменчен. В поле ввода набираем строку «qw1», ловим точку прерывания. Затем набираем строку «qw2» и ловим еще одну точку прерывания. Закрываем второй сеанс, во всплывающем окне получаем ответ от сервера при запросе строки «qw2». Далее завершаем первый сеанс и во всплывающем окне перетирается ответ предыдущего, более актуального, запроса:
А теперь пример работы с методом abort. В данном случае первый запрос будет сброшен и будет учитываться только самый последний ввод в поле поиска:
Буду рад, если вы поделитесь своим решением данной проблемы в комментариях ;).