I'm attempting to make a Google style table visualization that has built-in column filters and have pretty much everything figured out, except how to update properties for the correct table object when a page has more than one instance. I'm aware of the globalThis problem and have gotten around that with a variable that captures the identity of the table object, but it always ends up equaling the id of the last instance created.
I have searched stackoverflow's questions and also looked over some JS training, but still don't find a solution that works for my situation. Or maybe I have, but I didn't understand it well enough. I'm still quite green with JS.
Here's a sample of my code with noted trouble points. It may be a bit much to get the general point across, but I figure it may put my problem into perspective, if that's helpful.
// Declare a unique namespace.
var example = {};
// Attempt to work around globalThis confusion. Ends up pointing only to the last instance of MyTable.
var me = {};
// Class constructor. Parameter container is a DOM element on the client that will contain the chart.
example.MyTable = function(container) {
this.containerElement = container;
this.options = {
filters: [], // Array of objects.
// etc. ...
}
this.view = {rows: null, columns: null}
// Capture current object to avoid pointing at globalThis.
// Problematic with multiple MyTable objects on the page.
me = this;
// 'this.id' works as expected. 'me.id' ends up remaining as the last created MyTable and
// messes up other MyTable objects upon additional calls to 'draw()' via events.
this.id = this.containerElement.id;
}
// Main drawing logic.
// Parameters:
// 'data' is data object to handle.
// 'options' is a name/value map of options that differ from default specified in constructor.
// 'view' holds arrays of specified rows & columns to display or NULL to display all.
example.MyTable.prototype.draw = function(data, options, view) {
// 'this' works fine for 'options', since the table object has 'this' focus.
this.setOptions(options)
// 'this' works fine for 'view' on page load, but fails for events,
// due to the event target having 'this' focus.
this.setView(view)
// Code to assemble MyTable and insert into this.containerElement.
// etc. ...
// Create event listeners to set column filtering and sorting.
const cols = {};
for (let col = 0; col < qtyCols; col ) {
let colIndex = (this.view.columns ? this.view.columns[col] : col);
cols[colIndex] = {
sort: document.querySelector('#col' this.id '-' colIndex ' div.sort'),
filter: document.querySelector('#col' this.id '-' colIndex ' div.filter')
};
cols[colIndex].sort.addEventListener('click', this.sort); // This is what needs to be fixed.
cols[colIndex].filter.addEventListener('click', this.showFilterBox); // This too.
}
};
example.MyTable.prototype.setOptions = function(options){
// Do stuff to merge user and default options.
this.options = options; // Not actual code.
};
example.MyTable.prototype.setView = function(view){
// Do stuff to merge user and default view.
this.view = view; // Not actual code.
};
example.MyTable.prototype.sort = function(e){
// Using the event target works great for correct UI object reference, but...
let col = Number(e.target.closest('th').id.substring(e.target.closest('th').id.indexOf('-') 1));
if(col !== null){
// ... now I have a problem with addressing the correct table object properties.
// THIS HERE is my major problem.
if (me.options.sortColumn === col){me.options.sortAscending = !me.options.sortAscending;}
else{me.options.sortColumn = col; me.options.sortAscending = true;}
me.draw();
}
};
CodePudding user response:
The problem is where you add the event listeners. You don't provide a specific this
argument to those callback functions, so they are not called with it. So make sure those handlers are called on the current this
object:
cols[colIndex].sort.addEventListener('click', (e) => this.sort(e));
cols[colIndex].filter.addEventListener('click', (e) => this.showFilterBox(e));