tko icon indicating copy to clipboard operation
tko copied to clipboard

Add support for "slots"

Open brianmhunt opened this issue 6 years ago • 14 comments

I like the idea of slots for components, from Vue. Thoughts?

@knockout/core @knockout/extended

brianmhunt avatar Jun 15 '18 18:06 brianmhunt

I would be impressed if done right.

I thought it was what web component people call "transclusion". Not sure any more after Googling.

In Angular the slot name is substituted with a sort of css selector applied on the component inside content.

https://scotch.io/tutorials/angular-2-transclusion-using-ng-content

miellaby avatar Jun 16 '18 20:06 miellaby

+1 over here.

Seems like it wouldn't be too tough unless I'm missing something about this. Basically just allowing components to define a template within their inner HTML that binds to the $data. which then is injected into the template wherever the slot occurs. Honestly, we've built lots of components this way in knockout with a simple

<!-- ko template {name:'component-body-template',data:$data} -->
<!-- /ko -->

in the template for the component. This would allow the template to be dynamic (as the body of the custom element) and not have to be defined and named separately. We do this all over our code currently, and it works great. Most of those templates are only used once, which creates lots of structure and mess that could be simplified in this approach.

I imagine the slot implementation could be as simple as adding $innertemplate or something to context whenever component binding is invoked. Then anywhere within the component there could be a

<ko-slot></ko-slot>

which just binds up the $data to $innertemplate. That could allow some interesting and useful scenarios in a very web-component-ish manner.

rposener avatar Jun 18 '18 16:06 rposener

There is similar functionality in Durandal called Widgets if you want to review more variations of the same theme. Please, keep this functionality optional, so that it is possible to exclude this module if not needed (or any other impelmentation is desired).

maskmaster avatar Jun 18 '18 18:06 maskmaster

This is already supported in Knockout to some degree: http://knockoutjs.com/documentation/component-custom-elements.html#passing-markup-into-components

mbest avatar Jun 18 '18 18:06 mbest

If I understand well, it might be trivial to write a new binding à la <!-- ko slot: selector --><!-- /ko --> which would work like a <!-- ko template: { nodes: $($componentTemplateNodes).find(selector).get() } --><!-- /ko -->

miellaby avatar Jun 18 '18 20:06 miellaby

To me the one specific thing that vue does is which is really powerful is use the slot attribute of a template - presumably keeping the templates in a manner where component templates cannot contaminate each other. At present, we use lots of <template> in our SPA application, and have occasionally found uniqueness issues across the team. That would be the benefit to the slot approach over the currently available approach @mbest mentions.

Also, wondering how hard it'd be to enable named slot defaults in the registered component as well? I'm thinking done in such a manner that a default "slot template" is provided, but each slot could be overridden per-component instance.

I think those 2 benefits:

  1. Encapsulation of set of names per component
  2. Provide default and override capability per slot

would be a great add to the story of using tko components as a web-component framework.

rposener avatar Jun 18 '18 21:06 rposener

@rposener What I linked to doesn't require <template> tags. It's the same functionality as a basic <slot> (without names).

mbest avatar Jun 18 '18 22:06 mbest

I've implemented slots, and they look like this:

When creating a component named <custom-component> with the following template:

<template id='custom-component-template'>
  One
  <slot name='abc'></slot>
  Two
</template>

We can use the custom component:

<custom-component>
   <template slot='abc'>Slot!</template>
</custom-component>

Which will output:

<custom-component>
  One
  Slot!
  Two
</custom-component>

There are some lingering questions, but this is the first run at it, and the specifics should improve/hone with time.

brianmhunt avatar Jun 20 '18 14:06 brianmhunt

I'd say the ideal approach here is to just use components as parameters, which is the react model. That might be better than creating a new concept to learn.

daedalus28 avatar Jun 21 '18 21:06 daedalus28

Is there a clear example of that somewhere?

brianmhunt avatar Jun 21 '18 22:06 brianmhunt

Sure, here's an example article I found - https://jaketrent.com/post/ways-to-compose-react-components/#components-as-props

daedalus28 avatar Jun 22 '18 19:06 daedalus28

@daedalus28 Thanks for the link and input.

I've reviewed the two front-runner options, namely React and Vue, and the Vue model for slots seems more in-line with the KO model. Replicating what React does would require a relatively substantial investment by comparison, and even then I anticipate some kinks, so — notwithstanding input that illuminates any ignorances I have on this — I'm going to stick with Vue's <slot> model.

So what we end up with for transclusion will be familiar to people who use Vue, and perhaps less familiar to those with a React background.

brianmhunt avatar Jun 24 '18 16:06 brianmhunt

Did this ever get implemented? cannot find any reference to slots in documentation anywhere.

grofit avatar Jun 25 '19 10:06 grofit

@grofit It is implemented, but not documented. The unit tests verifying the behaviour are here:

https://github.com/knockout/tko/blob/master/packages/binding.component/spec/componentBindingBehaviors.js#L1019-L1430

brianmhunt avatar Jul 02 '19 13:07 brianmhunt