I’m building an app in vanilla JavaScript, trying to keep it Object Oriented.
I’ve split it into many component classes, each roughly “wrapping” the logic for one DOM element, e.g. a form or table.
Components are recycled across multiple pages, which becomes a pain when trying to determine which components to load and getting them to talk to each other indirectly through mediators.
My pseudocode:
// Classes split across many files
class UsersSmartTableWrapper {
editButtons = (//Array of button HTML elements which allow edits );
constructor() {
this.tableElement = document.getElementByID('TableID')
this.editButtons = ...;
this.editButtons.forEach(b => // add click event listeners)
}
}
class EditUserFormWrapper {
onSubmit(event) {
// Prevent page reload, build custom API data structure and POST request etc
// and once done
this.formElement.dispatchEvent(new Event('UsersUpdated'))
}
constructor(apiWrapperService) {
this.formElement = document.getElementByID('EditUsersForm');
this.apiWrapperService = apiWrapperService;
}
}
class EditUsersFormSmartTableMediator() {
onUsersUpdated() {
// Update table
}
onDataTableButtonEditRequest() {
//Open form and bind user data to it to edit
}
constructor(editUserFormWrapper, usersSmartTableWrapper ) {
this.editUserFormWrapper = editUserFormWrapper;
this.usersSmartTableWrapper = UsersSmartTableWrapper ;
//Add event listeners to child components here
}
}
class AnotherMediatorWithEvenMoreElements {
constructor() {
}
}
... many other componenets
My solution in the entry point looks something like this:
//main.js
window.addEventListener('load', ()=>{
let href = window.location.href;
// This one is easy to bring out of the big if else, as it is not dependent on a DOM element
// that may/ may not be present.
let apiWrapper = new ApiWrapper();
if (href.startsWith('Users/Details')) {
let editUserForm = new EditUserFormWrapper(apiWrapper)
} else if (href.startsWith('Users/Datatable')) {
let dataTable = new UsersSmartTableWrapper();
// I have to do this / for _every_ page that has the EditUserForm element.
// which isn't all pages!
let editUserForm = new EditUserFormWrapper(apiWrapper)
let mediator = EditUsersFormSmartTableMediator(editUserForm, dataTable );
} else if (href.startsWith('Users/AnotherPage')) {
new AnotherMediatorWithEvenMoreElements(... dependencies go here) // etc etc...
}
})
To me, this solution feels “hacky”. It results in repeatedly writing “new Component” at multiple points in the if/ else to build DOM dependent components. This goes against DRY and gets worse as more components are added to the app.
I know that this is the argument for Single Page Apps like Angular but for this project that isn’t an option.
Is there a design pattern for OOP JavaScript that can build components dependent on the current page in situations like this? Is depending on the href like this good practice?