Общее требование любого веб-приложения: позволить пользователю быстро перейти к интересующим записям, указав соответствующие фильтры. В заголовке рисунка показаны два способа установки фильтров: путем выбора из [ограниченного количества] существующих значений в определенном столбце - здесь Местоположение - и путем указания строки поиска, значение которой должно появиться в записях, отображаемых после фильтрации.

Oracle JET - это инструментарий, поддерживающий разработку полнофункциональных веб-приложений. В Oracle JET эта функция фильтрации является обычным требованием. В этой статье я кратко рассмотрю, как:

  • создать элемент множественного выбора и как заполнить его данными из атрибута Location записей в таблице
  • обрабатывать событие (де) выбора в множественном выборе - что приводит к фильтрации записей, показанных в таблице
  • создать поле поиска и перехватить изменения в поле поиска
  • обработчик сброса поля поиска
  • вызов REST API при изменении поля поиска

Я не претендую на то, чтобы представить наилучший способ реализации этой функциональности. Я недостаточно хорошо владею JET, чтобы сделать такое заявление, и я видел слишком много различных реализаций в документации Oracle, статьях в блогах, учебных пособиях и т. Д., Чтобы иметь возможность указать на один подход, который выделяется (для текущей версии JET) . Однако реализация, которую я здесь демонстрирую, кажется достаточно хорошей в качестве отправной точки.

Модуль HRM - это вкладка, которую я добавил в демонстрационное приложение Work Better. У него есть собственная ViewModel (hrm.js) и собственное HTML-представление (hrm.html). Я реализовал очень простой REST API в Node (http: // host: port / sizes? Name =), который предоставляет отделы в документе JSON.

Источники находятся в этом Gist: https://gist.github.com/lucasjellema/e133e5e769c13ba8507a3bee0ebc30d1

Отправная точка

Отправной точкой в ​​этой статье является простое приложение JET с вкладкой, содержащей таблицу, в которой отображаются записи отдела, полученные из REST API. Реализация этого приложения не является чем-то особенным и не является темой данной статьи.

Цель этой статьи - показать, как добавить возможность фильтрации записей в этой таблице - сначала путем выбора местоположений, для которых следует отображать отделы, с помощью виджета множественного выбора. Фильтрация выполняется на клиенте по набору отделов, полученных из серверной службы. На втором этапе добавляется фильтрация по имени с использованием поля поиска. Этот уровень фильтрации выполняется сервером, который предоставляет REST API.

Создание и заполнение элемента множественного выбора для местоположений

Элементом множественного выбора в данном случае является компонент Oracle JET ojSelect (см. Поваренную книгу). `Элемент показывает раскрывающийся список опций, которые можно выбрать, отображает текущие выбранные опции и позволяет отменить выбор выбранных опций.

HTML-код, используемый для добавления на страницу компонента множественного выбора, показан здесь:

<label for="selectLocation">Locations</label> 
<select id="selectLocation" 
  data-bind="ojComponent: { 
    component: 'ojSelect' , 
    options: locationOptions, 
    multiple: true , 
    optionChange: optionChangedHandler, 
    rootAttributes: {
        style:'max-width:20em'
    }
  }">
</select>

Атрибут options ссылается на свойство locationOptions модели ViewModel, которое возвращает значения выбора (возможности) - подробнее об этом позже. Атрибут multiple имеет значение true, чтобы можно было выбрать несколько значений, а атрибут optionChange ссылается на optionChangedHandler, функция в ViewModel, которая обрабатывает события изменения параметра, которые публикуются всякий раз, когда параметры выбираются или отменяются.

Когда отделы получены из REST API, locationOptions заполняются путем определения уникальных значений атрибута Location во всех записях отдела. Впоследствии все местоположения устанавливаются как выбранные значения в выбранном компоненте - как мы начали с нефильтрованного набора отделов. функция handleDepartmentsFetch вызывается всякий раз, когда свежие данные были получены из REST API.

// values for the locations shown in the multiselect self.locationOptions = ko.observableArray([]); self.handleDepartmentsFetch = function (collection) {
    var locationData = new Set(); 
    //collect distinct locations and add to locationData array 
    var locations = collection.pluck('Location'); 
    // get all values for Location attribute 
    // distill distinct values 
    var locationData = new Set(locations.filter((elem, index, arr) => arr.indexOf(elem) === index)); 
    // rebuild locationOptions 
    self.locationOptions.removeAll(); 
    var uniqueLocationsArray = []; 
    for (let location of locationData) { 
        uniqueLocationsArray.push({ 'value': location, 'label': location }); 
    } 
    ko.utils.arrayPushAll(self.locationOptions(), uniqueLocationsArray); 
    // tell the observers that this observable array has been updated 
    // (as result, the Multiselect UI component will be refreshed) 
    self.locationOptions.valueHasMutated(); 
    // set the selected locations on the select component based on all distinct locations available 
    $("#selectLocation").ojSelect({ "value": Array.from(locationData) });
};

См. Суть.

Мне не удалось установить выбранные значения в компоненте select путем обновления наблюдаемого массива, который поддерживает атрибут value компонента ojSelect. В качестве обходного пути я теперь использую прямую манипуляцию с использованием программной манипуляции с помощью jQuery selection ($ («# selectLocation»)) компонента ojSelect.

Обработка события выбора (де) выбора в множественном выборе

Когда пользователь изменяет набор выбранных значений в множественном выборе местоположений, мы хотим, чтобы набор отделов, показанных в таблице, обновлялся - сужался или расширялся, в зависимости от того, было ли местоположение удалено или добавлено к выбранным элементам.

Компонент ojSelect имеет атрибут optionChange, который в данном случае ссылается на функцию optionChangeHandler. Эта функция проверяет тип изменения параметра (равно «данным»?), А затем вызывает функцию prepareFilteredDepartmentsCollection при передаче коллекции self.deppies, которая была инициализирована во время выборки из REST API . Эта функция клонирует коллекцию всех отделов, извлеченных из REST API, и затем фильтрует ее на основе selectedLocations.

// returns an array of the values of the currently selected options in select component with id selectLocation
 self.getCurrentlySelectedLocations = function () {
 return $(“#selectLocation”).ojSelect(“option”, “value”);
 }
// prepare (possibly filtered) set of departments and set data source for table
 self.prepareFilteredDepartmentsCollection = function (collection, selectedLocations) {
 if (collection) {
 // prepare filteredDepartmentsCollection
 var filteredDepartmentsCollection = collection.clone();
var selectedLocationsSet = new Set(selectedLocations);
 var toFilter = [];
 // find all models in the collection that do not comply with the selected locations
 for (var i = 0; i < filteredDepartmentsCollection.size(); i++) {
 var deptModel = filteredDepartmentsCollection.at(i);
 if (!selectedLocationsSet.has(deptModel.attributes.Location)) {
 toFilter.push(deptModel)
 }
 }
 // remove all departments that do not qualify according to the locations that are (not) selected
 filteredDepartmentsCollection.remove(toFilter);
// update data source with fresh data and inform any observers of data source (such as the table component)
 self.dataSource(
 new oj.CollectionTableDataSource(filteredDepartmentsCollection));
 self.dataSource.valueHasMutated();
 }// if (collection)

Смотри по Гисту.

Когда создается коллекция отфильтрованных отделов, self.dataSource обновляется новым CollectionTableDataSource. С помощью вызова self.dataSource.valueHasMutated () мы явно запускаем подписчиков на источник данных - компонент Table.

Создайте поле поиска и перехватите изменения в поле поиска

Поле поиска - это просто элемент inputText с некоторым украшением. С полем поиска связана кнопка для сброса (очистки) поля поиска.

HTML-код для этих элементов:

<div class=”oj-flex-item oj-sm-8 “>
 <div class=”oj-flex-item” style=”max-width: 400px; white-space: nowrap”>
 <input aria-label=”search box” placeholder=”search” data-bind=”value: nameSearch, valueUpdate: ‘afterkeydown’, ojComponent: {component: ‘ojInputText’, rootAttributes:{‘style’:’max-width:100%;’}}”
 />
 <div id=”searchIcon” class=”demo-icon-sprite demo-icon-search demo-search-position”></div>
 <button id=”clearButton” data-bind=”click: clearClick,
 ojComponent: {
 component: ‘ojButton’, 
 label: ‘Clear’, 
 display: ‘icons’,
 chroming: ‘half’,
 icons: {start:’oj-fwk-icon oj-fwk-icon-cross03'}}”>
 </button> 
 </div>
 </div>

Смотрите на Gist.

Поле поиска привязано к nameSearch, наблюдаемому в ViewModel. Когда пользователь редактирует содержимое поля поиска, наблюдаемое обновляется, и запускаются все подписчики. Одним из таких подписчиков является функция self.search () - это вычисляемая функция KnockOut, которая зависит от nameSearch. Когда функция запускается - изменением значения nameSearch - она ​​проверяет, состоит ли строка поиска из трех или более символов, и если да, запускает новую выборку отделов из REST API, вызывая функцию fetchDepartments ().

// bound to search field 
self.nameSearch = ko.observable(''); 
// this computed function is implicitly subscribed to self.nameSearch; any changes in the search field will trigger this function 
self.search = ko.computed(function () { 
  var searchString = self.nameSearch(); 
  if (searchString.length > 2) { 
    self.fetchDepartments(); 
  } 
});
function getDepartmentsURL(operation, collection, options) { 
  var url = dataAPI_URL + "?name=" + self.nameSearch(); 
  return url; 
};

Функция getDepartmentsURL () вызывается непосредственно перед выборкой Departments. Он возвращает URL-адрес, используемый для выборки из REST API. Эта функция добавит параметр запроса к URL-адресу со значением наблюдаемого nameSearch.

Обработка сброса поля поиска

Кнопка «Очистить», показанная в предыдущем фрагменте HTML, связана с обработчиком события щелчка: function clearClick. Эта функция сбрасывает наблюдаемый объект nameSearch и явно объявляет его значение обновленным, чтобы активировать подписчиков на наблюдаемый объект nameSearch. Одним из таких подписчиков является функция self.search (), которая будет запускаться этим и будет выполнять повторную выборку отделов из REST API.

// event handler for reset button (for search field) 
self.clearClick = function (data, event) { 
  self.nameSearch(''); 
  self.searchDepartments(); 
  return true; 
}

REST API

REST API реализован с помощью Node и Express. Это очень просто; изначально он просто возвращает содержимое статического файла (Department.json) с записями отдела. Он немного расширен для обработки параметра запроса имени, чтобы возвращать только выбранные отделы. Обратите внимание, что эта реализация не самая эффективная. Для целей этой статьи он выполнит свою работу.

var app = express();
var locations = ['AMSTERDAM','ZOETERMEER','NIEUWEGEIN','MAASTRICHT']
var departments  = JSON.parse(require('fs').readFileSync('./departments.json', 'utf8'));
  // add a location to each record
  for (i = 0; i < departments.length; i++) {
    departments[i].location = locations[Math.floor(Math.random() * locations.length)] ;
  } 
app.get('/departments', function (req, res) { //process
    var nameFilter = req.query.name; // read query parameter name (/departments?name=VALUE)
    // filter departments by the name filter 
        res.send( departments.filter(function (department, index, departments)
                                 { return !nameFilter ||department.DEPARTMENT_NAME.toLowerCase().indexOf(nameFilter)>-1; 
                                 })
            ); //using send to stringify and set content-type
       });

Смотрите на Gist.

Ресурсы

Первоначально опубликовано на сайте technology.amis.nl 20 августа 2017 г.