Defining an Event Map - ldco2016/microurb_web_framework GitHub Wiki

Screen Shot 2021-09-07 at 11 12 37 AM

I can now show some HTML to a user which is a great step forward, but now I think I need to work on ability to have some event handlers tied to this HTML as well.

Let's say I add a button to my template with a text of Click Me like so:

export class UserForm {
  constructor(public parent: Element) {}

  template(): string {
    return `<div>
      <h1>User Form</h1>
      <input />
      <button>Click Me</button>
    </div>`;
  }

  render(): void {
    const templateElement = document.createElement("template");
    templateElement.innerHTML = this.template();
    console.log(this.parent);

    this.parent.append(templateElement.content);
  }
}

If I now head back to the browser, the button appears but if I click it nothing happens and we didn't really have any easy way to somehow add an event handler to the button. So I needed to have the ability to detect a click on the button and run some custom code anytime a click occurs.

So here is a timeline on how to accomplish this:

Screen Shot 2021-09-07 at 11 21 17 AM

This is a timeline describing everything that happens inside a render method and I added one additional step along the way.

So whenever I call the render method I call the template method and I get back that HTML string and at that point in time its just a string, to actually turn it into HTML, I insert that HTML string into a template element. That's when it actually gets turned into something useful.

Right after that is when I decided that it would be a good time to bind some event handlers to the HTML that is inside of that template element.

Right after that I continue to take that content of a template and insert it into the DOM.

Right above my template I added in an event handler that I want to run anytime I click on the button element like so:

export class UserForm {
  constructor(public parent: Element) {}

  onButtonClick() {
    console.log("Howdy");
  }

  template(): string {
    return `<div>
      <h1>User Form</h1>
      <input />
      <button>Click Me</button>
    </div>`;
  }

  render(): void {
    const templateElement = document.createElement("template");
    templateElement.innerHTML = this.template();
    console.log(this.parent);

    this.parent.append(templateElement.content);
  }
}

So I needed to tie that onButtonClick method to the button element. Keeping in mind that the HTML string is worthless, its only useful once it has been inserted into the templateElement.

So what I did was add in a method called eventsMap and everytime I call it, I return an object with keys and values that are strings contain an event name like click or hover or change and then after that a colon and selector to the element that I want to bind the element to.

export class UserForm {
  constructor(public parent: Element) {}

  eventsMap() {
    return {
      "click:button": this.onButtonClick,
    };
  }

  onButtonClick() {
    console.log("Howdy");
  }

  template(): string {
    return `<div>
      <h1>User Form</h1>
      <input />
      <button>Click Me</button>
    </div>`;
  }

  render(): void {
    const templateElement = document.createElement("template");
    templateElement.innerHTML = this.template();
    console.log(this.parent);

    this.parent.append(templateElement.content);
  }
}

So what I am trying to express here is that I want to somehow set up a click event handler to a button inside of my template and anytime that thing gets clicked I want to run this function here this.onButtonClick.

So essentially the key inside this object there is going to be a ton of meaning associated with them. So I need to parse that key 'click:button' and understand that I am trying to set up a click event handler on the button.

I need to somehow express to Typescript that I am going to be returning some keys where I don't really know what the keys are going to be ahead of time like so:

export class UserForm {
  constructor(public parent: Element) {}

  eventsMap(): { [key: string] } {
    return {
      "click:button": this.onButtonClick,
    };
  }

  onButtonClick() {
    console.log("Howdy");
  }

  template(): string {
    return `<div>
      <h1>User Form</h1>
      <input />
      <button>Click Me</button>
    </div>`;
  }

  render(): void {
    const templateElement = document.createElement("template");
    templateElement.innerHTML = this.template();
    console.log(this.parent);

    this.parent.append(templateElement.content);
  }
}

This tells TypeScript, I am going to return an object, but I don't know what the keys are going to be, but they are going to be strings.

Then I am going to say the value for everything inside the object is going to be a function that takes no arguments and returns nothing like so:

export class UserForm {
  constructor(public parent: Element) {}

  eventsMap(): { [key: string]: () => void } {
    return {
      "click:button": this.onButtonClick,
    };
  }

  onButtonClick() {
    console.log("Howdy");
  }

  template(): string {
    return `<div>
      <h1>User Form</h1>
      <input />
      <button>Click Me</button>
    </div>`;
  }

  render(): void {
    const templateElement = document.createElement("template");
    templateElement.innerHTML = this.template();
    console.log(this.parent);

    this.parent.append(templateElement.content);
  }
}

So this eventsMap is how I am going to relate the different events I am going to watch for inside my template to the different events I want to run.

⚠️ **GitHub.com Fallback** ⚠️