Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make the jsx parser pass child elements as parameters #181

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

deefco
Copy link

@deefco deefco commented Sep 9, 2022

Hi, this fix makes createElement automatically bind the child elements of a custom component as parameters.children in said component. This enables the creation of "layout components", where you can render {parameters.children} and display whatever components you passed into it.

Quick example:

class TheTime extends tko.Component {
  get template() {
    const date = new Date().toISOString();
    return <div>{date}</div>;
  }
}

class SideBar extends tko.Component {
  constructor(attributes) {
    super();
    this.children = attributes.children;
  }

  get template() {
    return (
      <div>
        <h2>Sidebar header</h2>
        {this.children}
      </div>
    );
  }
}

class App extends tko.Component {
  constructor(attributes) {
    super();
  }

  get template() {
    return (
      <sidebar>
        <the-time />
      </sidebar>
    );
  }
}

The time component will now get rendered inside the sidebar, without the sidebar component having to know anything about it.

For more information see: https://reactjs.org/docs/composition-vs-inheritance.html

@brianmhunt
Copy link
Member

@deefco You can already do this in TKO with a parameter e.g.

class TheTime extends tko.Component {
  get template() {
    const date = new Date().toISOString()
    return <div>{date}</div>
  }
}

class SideBar extends tko.Component {
  constructor(params) {
    super()
    this.inner = params.insideTheBar
  }

  get template() {
    return (
      <div>
        <h2>Sidebar header</h2>
        {this.inner}
      </div>
    );
  }
}

SideBar.register()
TheTime.register()
tko.applyBindings()

// there's no concept of an "App" in TKO.

HTML

<side-bar insideTheBar={<the-time />} /> // JSX
// or 
<side-bar insideTheBar={<the-time></the-time}></side-bar> // proper HTML

Parameterization preserves the separation of responsibilities. We don't want components to have access to the inner data structures, since they may change.

TKO also has a concept of <template> and <slot> slots (similar to Vue) but it's rather verbose to achieve nearly the same outcome. It exists for legacy apps that cannot use T/JSX.

Do these happen to solve your use case?

@deefco
Copy link
Author

deefco commented Sep 10, 2022

<side-bar insideTheBar={<the-time />} /> // JSX
// or 
<side-bar insideTheBar={<the-time></the-time}></side-bar> // proper HTML

My code is nothing but syntactic sugar for

<side-bar><the-time /></side-bar>
<side-bar children={<the-time />} /> 

It just ensures you can write nested jsx the way you would write html.

I'm not sure I understand your argument "Parameterization preserves the separation of responsibilities. We don't want components to have access to the inner data structures, since they may change." Could you explain that further? It seems to me that when you manually pass child components the parent also has access to the inner data structure of the element?

Nested components are also mentioned in the very first paragraph of the jsx standard. If tko works differently then perhaps there should be a notice in the documentation somewhere that tko doesn't support this?

@brianmhunt
Copy link
Member

@deefco I was thinking about this a bit and I think I understand the ask. Tell me if this makes sense for a desirable outcome:

The default get template for a tko.ViewComponent could/should be the inner HTML passed to that template.

class SideBar extends tko.ViewComponent {
}

SideBar.register()

// Given
<sub-menu><b>hello</b></sub-menu>
// Would render as
<sub-menu><b>hello</b></sub-menu>

This also enables some mediation since a ViewComponent could use super.template to get the children, and possibly do things with them e.g.

class SideBar extends tko.ViewComponent {
  get template () {
     return <div class='abc'>{super.template}</div>
  }
}

SideBar.register()

// Now
<sub-menu><b>hello</b></sub-menu>
/// will render as
<sub-menu><div class='abc'><b>hello</b></div></sub-menu>

This seems to make sense to me. Do you feel like this is the desired outcome, or is there a use case that I haven't considered here?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants