buxlabs/boxwood

component and partial props #685

Manborough posted onGitHub

This is a cool library, there seems to be quite a lack of server side component libraries for JS. I wanted to ask if there's any way currently to pass props between components and partials?

Given this

const view = await render('./app/views/one.html', {states: state })
// Render is just a wrapper around boxwood so I can pass in a template file and get back html.

it seems that in the component the state property seems to be global and can be accessed directly from layout. Perhaps it could be walled off instead and passed in directly like vue or svelte would.

something like this.

// template_file_1.html
<import layout from="./layout.html" />

<layout sub_state={state.events} >
     Slotted content
</layout>
//layout.html
<if sub_state.state>
 {sub_state.name}
<end>

<slot/>

@Manborough thanks for the kind words!

It is possible to pass props between components and partials, here's a simple example:

Explicit props

// pages/home.html
<import layout from="layouts/default.html"/>

<layout {title}>
  <h1>Hello, {name}!</h1>
</layout>
// layouts/default.html

<html>
  <head partial="./partials/head.html" {title} />
  <body>
    <slot/>
  </body>
</html>
// layouts/partials/head.html

<title>{title}</title>

The above shows the explicit shorthand syntax, you can explicitly pass values. Longer syntax looks like this:

<layout title="{title}">

should also work.

Implicit props

You can also pass values implicitly, using the globals keyword, e.g.

// layouts/partials.head.html

<title>{globals.title}</title>

In this case, you no longer need to pass the title. I prefer the explicit way, but this might help in some scenarios too.

Danger zone

The syntax you've shown:

<layout title={title}>
// or

<layout title={ title }>

is not recommended (lack of quotes). I decided to use a standard html parser under the hood instead of writing a new one. It allows you to reuse existing html tools (e.g. for color highlighting, formatting, linting).

I hope this answers your question, better docs are in progress and I'll try to include it there.

posted by emilos almost 4 years ago

I hope this answers your question, better docs are in progress and I'll try to include it there.

It does! Thank you :)

I've played around with a little bit already and ran into a related question. When I pass props through using this syntax, should the variable in the component be accessed using things? Currently it only works when you use states.

I was expecting that it would alias to the attribute name.

<button_1 things="{states}">
posted by Manborough almost 4 years ago

@Manborough I think it should work already, here's a spec that I've added to demonstrate:

https://github.com/buxlabs/boxwood/commit/3c9c78b478e2aeabd29a298ed4812d757546baa3

the lib is still in progress though, so it might have some rough edges. Can you show a full example? That would help a lot to repro

posted by emilos almost 4 years ago

Sure thing. I'm building a UI to assemble state machines, hence the state this and that.

// render_boxwood.mjs

import boxwood from 'boxwood'
const { compile, escape } = boxwood
import { readFile } from 'fs/promises'

export async function render(path, params) {
    let result = await readFile(path, 'utf8', (err, data) => err ? console.log(err) : data)

    const { template, errors } = await compile(result, {cache: false, paths: ['./app/views']})

    console.log(errors)

    if (errors.length > 0) {
        throw JSON.stringify(errors)
    }

    return template(params, escape)
}
// index.mjs

import { render } from './render_boxwood.mjs'

fastify.get('/', async (request, reply) => {
  const stateChart = await prisma.$queryRaw(chartcte, 'null')
  const state = await stateChart[0].state.sort((a, b) => a.order - b.order)

  const view = await render('./app/views/example.html', {states: state })
  reply.type('text/html').send(view)

})
// ./example.html
<import state from="./state.html" />
<div class="flex space-x-4 p-4">
    <state foo="{states}">
    </state>
</div>
// ./state.html

// If i change states in the for loop to foo I get an error.
//"Cannot read property 'length' of undefined"

<for state of states>
    <div class="border p-2 m-2">
        {state.value}
    </div>
</for>

The states object passed into the render function looks a bit like this.

[
  {
     id: '1',
     lvl: 0,
     name: 'Received',
     order: 1,
     value: 'received',
     events: { Whatever: null },
     states: null,
     initial: null,
     parent_id: null
   },
   {
     id: '2',
     lvl: 0,
     name: 'Design',
     order: 2,
     value: 'design',
     events: null,
     states: [{name: 'substates' }],
     initial: 'outsourced',
     parent_id: null
   }
 ]
posted by Manborough almost 4 years ago

I just tried out your example too, it works fine, so there's something up with the way I'm doing it currently. Could be the for loop, or perhaps the repetition in my naming. (Or a silly mistake of course)

posted by Manborough almost 4 years ago

@Manborough thanks for the great example. I've pushed a fix and released it as v0.53.4. I'm reworking the compiler right now, which will hopefully simplify things and avoid this type of errors.

posted by emilos almost 4 years ago

Sounds great. I'll close off this issue.

posted by Manborough almost 4 years ago

Fund this Issue

$0.00
Funded

Pull requests