Created
August 20, 2017 06:42
-
-
Save lucasjellema/e133e5e769c13ba8507a3bee0ebc30d1 to your computer and use it in GitHub Desktop.
Oracle JET - Table Filtering with multiselect filter and search field filter (with Node REST API)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var express = require('express'); | |
var path = require('path'); | |
var favicon = require('serve-favicon'); | |
var logger = require('morgan'); | |
var cookieParser = require('cookie-parser'); | |
var bodyParser = require('body-parser'); | |
var routes = require('./routes/index'); | |
var users = require('./routes/users'); | |
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 | |
}); | |
// view engine setup | |
app.set('views', path.join(__dirname, 'views')); | |
app.set('view engine', 'jade'); | |
// uncomment after placing your favicon in /public | |
//app.use(favicon(path.join(__dirname, 'public', 'favicon.ico'))); | |
app.use(logger('dev')); | |
app.use(bodyParser.json()); | |
app.use(bodyParser.urlencoded({ extended: false })); | |
app.use(cookieParser()); | |
app.use(express.static(path.join(__dirname, 'public'))); | |
app.use('/', routes); | |
app.use('/users', users); | |
// catch 404 and forward to error handler | |
app.use(function(req, res, next) { | |
var err = new Error('Not Found'); | |
err.status = 404; | |
next(err); | |
}); | |
// error handlers | |
// development error handler | |
// will print stacktrace | |
if (app.get('env') === 'development') { | |
app.use(function(err, req, res, next) { | |
res.status(err.status || 500); | |
res.render('error', { | |
message: err.message, | |
error: err | |
}); | |
}); | |
} | |
// production error handler | |
// no stacktraces leaked to user | |
app.use(function(err, req, res, next) { | |
res.status(err.status || 500); | |
res.render('error', { | |
message: err.message, | |
error: {} | |
}); | |
}); | |
module.exports = app; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
[ | |
{ | |
"DEPARTMENT_ID": 5, | |
"DEPARTMENT_NAME": "Fontys Hogeschool" | |
}, | |
{ | |
"DEPARTMENT_ID": 10, | |
"DEPARTMENT_NAME": "Administration" | |
}, | |
{ | |
"DEPARTMENT_ID": 20, | |
"DEPARTMENT_NAME": "Marketing" | |
}, | |
{ | |
"DEPARTMENT_ID": 30, | |
"DEPARTMENT_NAME": "Purchasing" | |
}, | |
{ | |
"DEPARTMENT_ID": 40, | |
"DEPARTMENT_NAME": "Human Capital" | |
}, | |
{ | |
"DEPARTMENT_ID": 50, | |
"DEPARTMENT_NAME": "Shipping" | |
}, | |
{ | |
"DEPARTMENT_ID": 60, | |
"DEPARTMENT_NAME": "IT" | |
}, | |
{ | |
"DEPARTMENT_ID": 70, | |
"DEPARTMENT_NAME": "Public Relations" | |
}, | |
{ | |
"DEPARTMENT_ID": 80, | |
"DEPARTMENT_NAME": "Sales" | |
}, | |
{ | |
"DEPARTMENT_ID": 90, | |
"DEPARTMENT_NAME": "Executive" | |
}, | |
{ | |
"DEPARTMENT_ID": 100, | |
"DEPARTMENT_NAME": "Finance" | |
}, | |
{ | |
"DEPARTMENT_ID": 110, | |
"DEPARTMENT_NAME": "Accounting" | |
}, | |
{ | |
"DEPARTMENT_ID": 120, | |
"DEPARTMENT_NAME": "Treasury" | |
}, | |
{ | |
"DEPARTMENT_ID": 130, | |
"DEPARTMENT_NAME": "Corporate Tax" | |
}, | |
{ | |
"DEPARTMENT_ID": 140, | |
"DEPARTMENT_NAME": "Control And Credit" | |
}, | |
{ | |
"DEPARTMENT_ID": 150, | |
"DEPARTMENT_NAME": "Shareholder Services" | |
}, | |
{ | |
"DEPARTMENT_ID": 160, | |
"DEPARTMENT_NAME": "Benefits" | |
}, | |
{ | |
"DEPARTMENT_ID": 170, | |
"DEPARTMENT_NAME": "Manufacturing" | |
}, | |
{ | |
"DEPARTMENT_ID": 180, | |
"DEPARTMENT_NAME": "Construction" | |
}, | |
{ | |
"DEPARTMENT_ID": 190, | |
"DEPARTMENT_NAME": "Contracting" | |
}, | |
{ | |
"DEPARTMENT_ID": 200, | |
"DEPARTMENT_NAME": "Operations" | |
}, | |
{ | |
"DEPARTMENT_ID": 210, | |
"DEPARTMENT_NAME": "IT Support" | |
}, | |
{ | |
"DEPARTMENT_ID": 220, | |
"DEPARTMENT_NAME": "NOC" | |
}, | |
{ | |
"DEPARTMENT_ID": 230, | |
"DEPARTMENT_NAME": "IT Helpdesk" | |
}, | |
{ | |
"DEPARTMENT_ID": 240, | |
"DEPARTMENT_NAME": "Government Sales" | |
}, | |
{ | |
"DEPARTMENT_ID": 250, | |
"DEPARTMENT_NAME": "Retail Sales" | |
}, | |
{ | |
"DEPARTMENT_ID": 260, | |
"DEPARTMENT_NAME": "Recruiting" | |
}, | |
{ | |
"DEPARTMENT_ID": 270, | |
"DEPARTMENT_NAME": "Payroll" | |
} | |
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<h1>HRM Content</h1> | |
<div> | |
The Departments Details | |
</div> | |
<br/> | |
<div id="hrm-content" class="demo-apphrm"> | |
<div class="oj-row"> | |
<div> | |
<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> | |
<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> | |
<div id="deptList" class="oj-md-9 oj-col"> | |
<table id="dept-table" summary="Departments List" data-bind=" | |
ojComponent: {component: 'ojTable', | |
data: dataSource, | |
columns: [{headerText: 'Department Id', | |
field: 'DepartmentId'}, | |
{headerText: 'Department Name', | |
field: 'DepartmentName'}, | |
{headerText: 'Location', | |
field: 'Location'}] | |
}"> | |
</table> | |
</div> | |
</div> | |
</div> | |
</div> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
define(['ojs/ojcore', 'knockout', 'utils', 'jquery', 'ojs/ojmodel', 'ojs/ojknockout-model', 'ojs/ojknockout', 'ojs/ojmodel', 'promise', 'ojs/ojtable', 'ojs/ojarraytabledatasource', 'ojs/ojcollectiontabledatasource', | |
'ojs/ojselectcombobox'], | |
function (oj, ko, utils, $) { | |
/** | |
* The view model for the hrm content view template | |
*/ | |
function hrmViewModel() { | |
var self = this; | |
var dataAPI_URL = 'http://127.0.0.1:3000/departments'; | |
// values for the locations shown in the multiselect | |
self.locationOptions = ko.observableArray([]); | |
// bound to search field | |
self.nameSearch = ko.observable(''); | |
// datasource for the table component | |
self.dataSource = ko.observable(); | |
self.fetchDepartments = function () { | |
new self.DeptCollection().fetch({ | |
success: function (collection, response, options) { | |
self.handleDepartmentsFetch(collection); | |
} | |
}); | |
} | |
// 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(); | |
} | |
}) | |
// event handler for reset button (for search field) | |
self.clearClick = function (data, event) { | |
self.nameSearch(''); | |
self.searchDepartments(); | |
return true; | |
} | |
self.optionChangedHandler = function (event, data) { | |
if (data.option == "value") { | |
// REFILTER the data in self.DeptCol into the collection backing the table | |
self.prepareFilteredDepartmentsCollection(self.deppies, getCurrentlySelectedLocations()); | |
} | |
} | |
function getDepartmentsURL(operation, collection, options) { | |
var url = dataAPI_URL + "?name=" + self.nameSearch(); | |
return url; | |
}; | |
self.Department = oj.Model.extend({ | |
urlRoot: dataAPI_URL, | |
parse: function (response) { | |
return { | |
DepartmentId: response['DEPARTMENT_ID'], | |
DepartmentName: response['DEPARTMENT_NAME'], | |
Location: response['location'] | |
}; | |
}, | |
idAttribute: 'DepartmentId' | |
}); | |
self.DeptCollection = oj.Collection.extend({ | |
customURL: getDepartmentsURL, | |
model: new self.Department() | |
}); | |
// whenever departments have been fetched, this function is called to: | |
// -derive the values in the multiselect component for locations | |
// -set the data source that fuels the table component | |
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); | |
self.locationOptions.valueHasMutated(); | |
// set the selected locations on the select component based on all distinct locations available | |
$("#selectLocation").ojSelect({ "value": Array.from(locationData) }); | |
// now prepare the filtered departments for the data source | |
self.deppies = collection; | |
self.prepareFilteredDepartmentsCollection(collection, locationData); | |
}; | |
// 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) | |
} | |
//load the departments | |
self.fetchDepartments(); | |
}// hrmViewModel | |
return hrmViewModel(); | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment