Re Rendering on Model Change - ldco2016/microurb_web_framework GitHub Wiki

Anytime my Model gets updated, I want to re-render this HTML, essentially reproduce the template and stick it back on the page.

To do so, in my Model Class I added in the events property. Additionally, anytime I change any properties on an instance of a Model, in this case the User Model, I would also trigger an event called change inside the setter:

import { AxiosPromise, AxiosResponse } from "axios";

interface ModelAttributes<T> {
  set(value: T): void;
  getAll(): T;
  get<K extends keyof T>(key: K): T[K];
}

interface Sync<T> {
  fetch(id: number): AxiosPromise;
  save(data: T): AxiosPromise;
}

interface Events {
  on(eventName: string, callback: () => void): void;
  trigger(eventName: string): void;
}

interface HasId {
  id?: number;
}

export class Model<T extends HasId> {
  constructor(
    private attributes: ModelAttributes<T>,
    private events: Events,
    private sync: Sync<T>
  ) {}

  on = this.events.on;
  trigger = this.events.trigger;
  get = this.attributes.get;

  set(update: T): void {
    this.attributes.set(update);
    this.events.trigger("change");
    // Object.assign(this.data, update);
  }

The change event is what tells other parts of my application that some data has updated.

When I create an instance of a view class I will listen for the Model that gets passed in, specifically for a change event. When that change event occurs I must have set some new data on my Model and I should attempt to re-render my View.

I will add in some code to my constructor() to ensure anytime I receive this model, right away I am going to start listening for the change event and when its triggered I am going to run that render().

import { User } from "../models/User";

export class UserForm {
  constructor(public parent: Element, public model: User) {
    this.model.on("change", () => {
      this.render();
    });
  }

One thing I might do to slightly improve this is maybe extract it to a helper method like so:

import { User } from "../models/User";

export class UserForm {
  constructor(public parent: Element, public model: User) {
    this.bindModel();
  }

  bindModel(): void {
    this.model.on("change", () => {
      this.render();
    });
  }

I like to keep the constructor() as simple as possible just to make it easier to understand exactly what happens when I create an instance of user form.

Screen Shot 2021-09-14 at 10 32 09 AM

It kind of works, but everytime I click on the button it duplicates the HTML, so how to fix that?

I will head back to my render() method and say the instant I start to render a second time I am going to look at the parent element that contains all the HTML, remove it, and replace with the newly rendered template.

To empty out the parent element I will do the following:

  render(): void {
    this.parent.innerHTML = "";

    const templateElement = document.createElement("template");
    templateElement.innerHTML = this.template();

    this.bindEvents(templateElement.content);

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

So I emptied out the parent element, re-render the template with templateElement, bind my events with this.bindEvents() and stick it into the parent element with this.parent.append().