dustjs
dustjs copied to clipboard
Would it be possible to pass a sub-tree as parameter to a partial?
Apologies if this has been previously discussed or is simply unpractical to implement, but I wish there as a way to pass a sub-tree of DOM elements to a partial as a parameter.
Example:
<!-- Calling the partial -->
{>"components/my-component1" parameter1="foobar"}
<div class="component__inner">
<p>Hello world</p>
</div>
{<}
<!-- Inside the partial -->
<div class="component component--{parameter1}">
{_content}
</div>
Result:
<div class="component component--foobar">
<div class="component__inner">
<p>Hello world</p>
</div>
</div>
This would allow developers to create self-contained and reusable components as Dust.js partials. You can argue that this is technically possible now, but one would have to pass any markup required by the component as a normal parameter, which can get messy very easily:
{>"components/my-component1" parameter1="foobar" content="<div class='component__inner'><p>Hello world</p></div>"}
A similar approached is used by Sass in their mixins, as developers can define normal parameters as well a special code block defined as @content
.
@mixin component1($arg1, $arg) {
.component1 {
width: $arg1;
height: $arg2;
@content;
}
}
React components also make this possible through this.props.children
.
I'd love to hear your thoughts about this. Thanks in advance!
I think this is possible now using string interpolation in partial names:
Content partial (named "innerComponent"):
<div class="component__inner">
<p>Hello world</p>
</div>
Main partial (named "component"):
<div class="component component--{parameter1}">
{>"{contentPartial}"/}
</div>
Usage:
{>component parameter1="foobar" contentPartial="innerComponent"/}
That's true, but it requires the "inner component" to be a partial and therefore not arbitrary dynamic content, which almost defeats the purpose. Being able to pass any block of markup to a partial would allow me to do something like this:
Component 1: form.dust
Creates a <form>
element wrapped in a container, applies the correct classes based on type
, includes various meta information in the form of hidden elements and adds a submit button.
Accepts
-
action
-
method
— defaulting toPOST
-
type
— let's imagine we created 2 variations of a form,normal
andcollapsed
-
content
— the content of the form
Component 2: checkbox.dust
Creates an accessible checkbox input field with customised styling.
Accepts
-
checked
— defaulting tofalse
-
label
Usage
Creating a form with 3 elements: two regular text inputs and an instance of checkbox
.
{> "form.dust" method="GET" action="myuri.com" type="collapsed" }
<input type="text" placeholder="Name">
<input type="email" placeholder="Email">
{> "checkbox.dust" label="Subscribe to newsletter" }
{<}
Forcing the elements passed to form.dust
to be in a partial would mean creating a file for every possible combination of elements, which is unpractical when the elements need to be generated dynamically (e.g. reading from a JSON schema).
Unless I'm missing something here :sweat_smile:
Alright, what you want is a custom Dust helper (see http://www.dustjs.com/guides/dust-helpers/ and http://www.dustjs.com/docs/helper-api/ for more info on writing custom helpers).
For the form.dust example above, here's more or less what it would look like:
Helper
dust.helpers['form.dust'] = function(chunk, context, bodies, params) {
var action = params.action;
var method = params.method || 'POST';
var type = params.type;
// Write the container
chunk.write('<div class="dust-form-container">')
.write(`<form method="${method}" action="${action}" class="${type}">`)
// Render the body, using the current context
.render(bodies.block, context)
// Close the container
.write('</form>')
.write('</div>');
};
Usage
{@form.dust method="GET" action="myuri.com" type="collapsed"}
<input type="text" placeholder="Name">
<input type="email" placeholder="Email">
{@checkbox.dust label="Subscribe to newsletter"/}
{/form.dust}
Output
<div class="dust-form-container">
<form method="GET" action="myuri.com" class="collapsed">
<input type="text" placeholder="Name">
<input type="email" placeholder="Email">
<!-- Whatever you defined in your @checkbox.dust helper -->
</form>
</div>
Hmm, that's kind of icky to have to put HTML into your helper though. I wonder if we could use chunk.partial
in a creative way to do this.
The reason we haven't supported this is mostly around the magic {content}
variable or similar that you would need. There's another issue on it I'll dig up.
Alright, what you want is a custom Dust helper (...)
I don't think helpers are the answer. With that pattern, I would end up no partials at all and all my components would be JavaScript files with HTML inside.
The reason we haven't supported this is mostly around the magic {content} variable or similar that you would need.
I understand the reluctancy of introducing a magic variable, but in my opinion the benefits would surpass any disadvantages. Couldn't it be something like $content
to be inline with things like $idx
?
Any updates on this? Thanks!
For anyone interested, this is possible by creating a very simple helper used to call a partial and inject its content block as a parameter:
(function (dust) {
/**
* @partial helper
* Calls a partial and injects the body block as a `$content` parameter
*
* Example:
* {@partial $name="partials/form"}
* <input name="foo" type="text">
* {/partial}
*
* By @eduardoboucas
* https://eduardoboucas.com/blog/2016/04/15/creating-modular-ui-components-with-dustjs.html
*/
dust.helpers.partial = function (chunk, context, bodies, params) {
var newContext = {
$content: bodies.block
};
return chunk.partial(params.$name, context.push(newContext), params);
};
})(typeof exports !== 'undefined' ? module.exports = require('dustjs-linkedin') : dust);
A more detailed explanation can be found here.
Nice write-up. We would want to do this as a grammar extension instead of in the runtime, so we'll see where that goes.
On Fri, Apr 15, 2016, 3:30 AM Eduardo Bouças [email protected] wrote:
For anyone interested, this is possible by creating a very simple helper used to call a partial and inject its content block as a parameter:
(function (dust) { /** * @partial helper * Calls a partial and injects the body block as a
$content
parameter * * Example: * {@partial $name="partials/form"} * * {/partial} * * By @eduardoboucas * https://eduardoboucas.com/blog/2016/04/15/creating-modular-ui-components-with-dustjs.html */ dust.helpers.partial = function (chunk, context, bodies, params) { var newContext = { $content: bodies.block };return chunk.partial(params.$name, context.push(newContext), params);
}; })(typeof exports !== 'undefined' ? module.exports = require('dustjs-linkedin') : dust);
A more detailed explanation can be found here https://eduardoboucas.com/blog/2016/04/15/creating-modular-ui-components-with-dustjs.html .
— You are receiving this because you commented. Reply to this email directly or view it on GitHub https://github.com/linkedin/dustjs/issues/715#issuecomment-210407610
That'd be great. Thank you!