Some notes on the structure and design decisions taken for this project.
Is a Union Type. Decided not to use enum because there's no functionality for the 0 or 1 values.
Used for functions passed to the listeners collection in the state manager to be called by the state manager in an event. Could also use type (items: Project[]) => void
when we need to be more restrictive to make sure these functions always contain parameters
Using interfaces when we want to add specific types and structures to certain classes or objects to implement certain methods.
Interface for classes that implement drag functionality (e.g. ProjectItem class)
dragStartHandler(event: DragEvent)
- when item becomes draggabledragEndHandler(event: DragEvent)
- when item stops to be draggable
Interface for classes that implement drop target functionality (e.g. ProjectList class)
dragOverHandler(event: DragEvent)
- permit the dropdropHandler(event: DragEvent)
- handle the dropdragLeaveHandler(event: DragEvent)
- visual feedback after drop or abort
Use as decorator function to bind methods to their class instance instead of the classic callback assignment to the event target for example.
\_target
- prototype of class, e.g. ProjectInput\_methodName
- e.g. 'activateSubmitButton'descriptor
- PropertyDescriptor
This function performs a variety of validation checks on input.
It uses a variable isValid
that is always true unless the following validation checks turn it false
. It is returned at the end and therefore, if any one check fails, the whole input validation failed.
Manages shared functions across classes.
protected listeners: ListenerFunctionWithoutArguments[] = [];
(same as:private listeners: (() => void)[] = [];
) This is a public method, that lets other classes add their functions to the listeners collection so that they can be shared across different classesprotected updateListeners()
- this function calls all callback functions in the listener collection. This can trigger dynamic re-rendering after an event
This is a Singleton
class for managing project state. This class can only be instantiated once (as global object to be used throughout the app). This is achieved via the private constructor
.
The addProject
method is public so it can be called from outside of class to add new projects. Projects will be added to active
session by default.
This is an abstract
class because we only need this class for inheritance and it shall not be instantiated.
abstract
methods are for inheritance only. The implementation is up to the subclass but they are forced to at least have these methods.
The Component class uses generic types to allow for different types but have a common ground.
We do an existence check on the template id.
const templateElement = document.getElementById(templateId);
if (templateElement) {
this.templateElement = templateElement as HTMLTemplateElement;
}
This could also be written with a !
operator at the end, to tell TypeScript, that this id will always exist.
const templateElement = document.getElementById(templateId)!;
attachActiveElements(position)
adds the grabbed template element to the active div element
The activate()
method attaches the submitHandler
function when the form is submitted (on submit
event).
Common Problem:
The this
context within a callback will be bound to the target of the event (not the class)!.
The .bind()
method can be used to bind the this
context to the appropriate class. However, this can be achieved with a method decorator (Autobind
) as well.
this.formElement.addEventListener("submit", this.submitHandler.bind(this)); // binding 'this' context to the class
-
event.preventDefault()
removes default behavior (e.g. to send HTTP request upon submission). This is used to implement custom behavior (e.g. what to do when form is submitted). -
submitHandler
function checks if input is a tuple (viaArray.isArray()
because a tuple is essentially an array). If true, we got a valid input tuple.
The activate()
method registers the renderProjects
function to the state manager, so it can be called from elsewhere.
The renderContent()
method fills in the template element tags in the active element.
For the renderProjects
method, the @Autobind
decorator is applied to ensure it retains the correct '.this' context when called (e.g. from the state manager instance)
Implements the Draggable
class, so that the we're forced to implement the Draggable
methods.
The types HTMLUListElement
(from T
in base class) corresponds to the element where our project items will be placed in and HTMLLIElement
(from U
in the base class) is the item we want to place in T
.
stateManager
is a global instance (Singleton
) that can be used to interact between classes
When these classes are instantiated the rendering is executed and will show up in browser:
new ProjectInput();
new ProjectList("active");
new ProjectList("finished");
- Improve validation function so that an alert is shown for the specific validation check that failed.
- Use Decorators for validation checks.
- Connect database to store projects (in
stateManager.storedProjects
viastateManager.addProject()
) -> will be rendered withProjectList.renderProjects()
. - When rendering project list, it would be better for performance to not fetch the whole list every time, but only append new items.