htl-spec
htl-spec copied to clipboard
Support content projection in data-sly-template
In Angular and a multitude of other templating languages, it is possible to declare slots where templates can hold injected html, this is known as content projection, it is a method in which layout and content of layout are separated. This means that a template becomes extremely flexible and quick to refactor. I have found a very hacky way to do this currently (not practical in prod).
The workaround:
<sly data-sly-template.contentProjectionTest="${ @ FormInputSlot1, FormInputSlot2 }">
<form>
<div class='form-control-group-1'>
${FormInputSlot1 @context="html"}
</div>
<div class='form-control-group-2'>
${FormInputSlot2 @context="html"}
</div>
</form>
</sly>
<!--/* Hacky variables for html as a string */-->
<sly data-sly-test.injectedContentToSlot="${'
<label>creates a slot to inject content</label>
<input type=\'text\' value=\'Hello world\'>
'}"></sly>
<sly data-sly-test.injectedContentToSlot2="${'
<label>creates a slot to inject content</label>
<h1>varient</h1>
'}"></sly>
<!--/* render and inject */-->
<sly data-sly-call="${contentProjectionTest @
FormInputSlot1=injectedContentToSlot,
FormInputSlot2=injectedContentToSlot2
}"></sly>
This is less than ideal for several reasons • Escaped quotes • Markup as a string • Involved setup • No ability to have slot contents imported from files
I propose that the functionality could look like this:
<sly data-sly-template.contentProjectionTest>
<form>
<!--/*much more flexible template layout*/-->
<div class='form-control-group-1'>
<sly data-sly-slot="${contentProjectionTest @ FormInputSlot1}"/>
</div>
<div class='form-control-group-2'>
<sly data-sly-slot="${contentProjectionTest @ FormInputSlot2}"/>
</div>
</form>
</sly>
<sly data-sly-call="${contentProjectionTest}">
<sly data-sly-push="${contentProjectionTest @ FormInputSlot1}">
<label>creates a slot to inject content</label>
<input type=\'text\' value=\'Hello world\'>
</sly>
<sly data-sly-push"${contentProjectionTest @ FormInputSlot2}">
<label>creates a slot to inject content</label>
<h1>varient</h1>
</sly>
</sly>`
I'd love to hear your thoughts.
@acronamy, I have the impression that you can already achieve this, with two or more templates:
HTL script:
<template data-sly-template.inner>
<span>This is the inner template.</span>
</template>
<template data-sly-template.outer>
<span class="outer" data-sly-call="${inner}"></span>
</template>
<div class="wrapper" data-sly-call="${outer}"></div>
Output:
<div class="wrapper">
<span class="outer">
<span>This is the inner template.</span>
</span>
</div>
Does this example fit your use-case?
@raducotescu thanks for your reply, this is a better workaround than my own.
That sounds about right in the simplest use case, In the real world, I wouldn't have 2 templates in the same file as one would be /layouts/column.html and the other /content/select-list.html however so I would data-sly-use the content, so that means the inner template would have to be passed as a parameter. I use parameters in templates for attributes, text content and conditionals. I feel that this inner html insertion via params is a different idea from my current usage or params, which (is imo), not as clear using the method mentioned, a dedicated data-sly method would do more for clarity.
Some years ago I had the same idea but I have never created a proposal.
Maybe the syntax could be changed but the idea of @adam-cyclones is essential for modern modularisation.
The feature is supported by all modern frontend rendering engines like react
, angular
or vue
.
But also common rendering engines like handlebars
support it
Angular:
<dashboard-tile a="10" b="5" c="15">
Only believe in statistics you've faked yourself.
</dashboard-tile>
React:
<DashboarTile a="10" b="5" c="15">
Only believe in statistics you've faked yourself.
</DashboarTile>
Vue:
<dashboard-tile a="10" b="5" c="15">
Only believe in statistics you've faked yourself.
</dashboard-tile>
Handlebars:
{{#dashboard-tile a="10" b="5" c="15"}}
Only believe in statistics you've faked yourself.
{{/dashboard-tile}}
Why do I believe that the current htl
solution proposed by @raducotescu is not enough?
Lets say you wrote an abstraction for a container
, grid row
, a grid column
, a accordion
and a typography
:
<Container>
<Row vertical-alignment="top">
<Column size="4">
<Typography size="xl" responsive font="light">Hello World</Typography>
</Column>
<Column size="8">
<Accordion collapsed>
<Typography size="m" responsive font="medium">Some text about world</Typography>
</Accordion>
</Column>
</Row>
<Row vertical-alignment="top">
<Column size="8">
<Typography size="xl" responsive font="light">Hello Mars</Typography>
</Column>
<Column size="4">
<Accordion collapsed>
<Typography size="m" responsive font="medium">Some Text about <a href="moon">moon</a></Typography>
</Accordion>
</Column>
</Row>
</Container>
Maybe @raducotescu could correct me but I believe there is no good way to build reusable components similar to the example above in htl.
@jantimon, you're right, HTL's templates are not as advanced. I'll take this issue to our team and we'll try to figure out how it would work.
@raducotescu do you have any update for us?
I guess the most simple way would be the following:
<sly data-sly-template.headline="${ @ children }">
<h1>${children}</h1>
</sly>
<sly data-sly-call="${headline}">
Hello World
</sly>
It would be backwards compatible and very similar to react or handlebars
6 months ago I raised this. It's an important feature and I hope it gets added. I no longer work on AEM for one of your bigger clients so I have no vested interest. But it's interesting to see how it would be solved.