canjs / can-component

Custom elements and widgets
https://canjs.com/doc/can-component.html
MIT License
8 stars 8 forks source link

inline partials replace slots and templates and are passed to the ViewModel #323

Open justinbmeyer opened 5 years ago

justinbmeyer commented 5 years ago

tldr Lets make something a little bit easier to use than partials.

Older ideas #322

Problem

1. Slots and partials use a lot of syntax that is unlike anything else in CanJS. The syntax is also noisy.

Here's how it looks to pass two templates:

    <my-email>
        <can-template name="subject">
            <h1>{{this.subject}}</h1>
        </can-template>
        <can-template name="body">
            <span>{{this.body}}</span>
        </can-template>
    </my-email>

And to call those templates:

view: `
        <can-slot name="subject" subject:from="subject" />
        <can-slot name="body" subject:from="body" />
`

2. Folks want access to these templates

Someone (I wrote it on behalf of someone else) wanted the ability to pass templates down: #147

like:

<sub-component>
  <can-template name="childName" from="view.templates.templateName"/>
</sub-component>

It's also odd that <can-template>s are not available to the ViewModel as these are essentially arguments to the components similar to from: would be.

Solution

Lets:

  1. Make templates available to the view
  2. Call these templates with call expressions like template()
  3. Reuse syntax already available to define them.

I propose something like the following:

    <my-email>
        {{<subjectView}}
            <h1>{{this.subject}}</h1>
        {{/subjectView}}
        {{<bodyView}}
            <span>{{this.body}}</span>
        {{/bodyView}}
    </my-email>
Component.extend({
  tag: "my-email",
  view: `
        {{ this.subjectView( subject = this.subject ) }}
        {{ this.bodyView( body = this.body ) }}
  `,
  ViewModel: {
    subjectView: "any",
    bodyView: "any"
  }
})

How it solves: Slots and partials use a lot of syntax that is unlike anything else in CanJS. The syntax is also noisy.

How it solves: Folks want access to these templates

My-email could pass along these partials like:

Component.extend({
  tag: "my-email",
  view: `
        <my-subject subjectView:from="this.subjectView" subject:from="this.subject"/>
        {{ this.bodyView( body = this.body ) }}
  `,
  ViewModel: {
    subjectView: "any",
    bodyView: "any"
  }
})

Other Considerations

Handling default content

Default content can be handled one of two ways:

// CONDITIONAL CHECKS
Component.extend({
  tag: "my-email",
  view: `
        {{# if(this.subjectView) }}
          {{ this.subjectView( subject = this.subject ) }}
        {{else}}
          DEFAULT CONTENT
        {{/if}}
        {{ this.bodyView( body = this.body ) }}
  `,
  ViewModel: {
    subjectView: "any",
    bodyView: "any"
  }
})

OR

// Default VM properties
Component.extend({
  tag: "my-email",
  view: `
        {{ this.subjectView( subject = this.subject ) }}
        {{ this.bodyView( body = this.body ) }}
  `,
  ViewModel: {
    subjectView: {
      type: stache,
      value: `DEFAULT CONTENT`
    },
    bodyView: "any"
  }
})
justinbmeyer commented 5 years ago
var fn = function(data, nodeList){
    var renderer = stache('<div>Hi</div>')
    return renderer( new Scope.LetContext({abc: data}), nodeList )
}
fn[can.isView] = true;
rjgotten commented 4 years ago

For what it's worth; I hate the inline partial syntax. The tag-based slot/template syntax is far more friendly on the eyes.

Objectively speaking, replacing a clear landmark name such as can-slot or can-template with a generic looking call expression and replacing the default inner content with a generic {{#if}} section helper is terrible for discoverability.