Skip to content

Add API for marking manually a Directive as a "template directive" #1931

Closed

Description

@jelbourn

Currently, for a directive to be able to inject its own ProtoViewRef, if has to be used like this:

<div *some-directive></div>
<!-- OR -->
<template some-directive></template>

This works, but doesn't provide for the nicest API when you want to build a component wants to get a hold of the ProtoViews to do something special. I ran into this when trying to build a Tabs component.

The API for the tabs should look like this:

<tab-group>
  <!-- Tab label can be set via attribute with the content directly inside of <tab> -->
  <tab label="Vegetables"> Carrots, Beets </tab>
  <!-- Tab label can also be a full element -->
  <tab>
    <tab-label> Fruits </tab-label>
    <tab-body> Apples, Cherries </tab-body>    
  </tab>
</tab-group>

As far as the user is concerned, this is a straightforward component. Under the hood the TabGroup needs to be able to collect the tab labels and tab bodies and render them in the appropriate place inside of its own template. In order to to do that, it needs to create ViewContainers for the contents of tab-label, tab-body, and tab. To create the ViewContainers, it needs the ProtoViewRef for the directives. The ProtoView is only available, however, when the directive is used as a "template directive", as I mentioned above. This means that the API would have to be written like this:

<tab-group>
  <!-- Tab label can be set via attribute with the content directly inside of <tab> -->
  <template tab label="Vegetables"> Carrots, Beets </template>
  <!-- Tab label can also be a full element -->
  <div tab>
    <template tab-label> Fruits </template>
    <template tab-body> Apples, Cherries </template>    
  </div>
</tab-group>

All of the nice semantic tag names are gone, and it's surfacing implementation details that the user has no need to understand.

For dealing with this, I propose adding a flag to @Directive that set the template behavior to be always on:

@Directive({
  selector: 'tab',
  isTemplated: true
})

Activity

  1. tbosch commented on May 15, 2015

    @tbosch
    Contributor

    I think it is important for a user to know whether something is a template or not. So hiding this behind a flag on the directive seems wrong to me. However, there might be different opinions here...

  2. matanlurey commented on May 17, 2015

    @matanlurey
    Contributor

    Is there ever a case where you'd want a Directive to both work in template and non-template mode, or is that even possible? If not, I tend to agree with Jeremy.

  3. mhevery commented on May 31, 2015

    @mhevery
    Contributor

    My current thinking is that this works as intended. And if you want to do the transclusion you need the * it has a lot of implication about variable visibility and tooling.

  4. jelbourn commented on Jun 1, 2015

    @jelbourn
    ContributorAuthor

    @mhevery for the use case I have in mind I don't quite see how this affects tooling. Did you have an example in mind?

    We should figure out if there's another way to achieve the same component feature and API without making this change.

  5. tbosch commented on Jun 1, 2015

    @tbosch
    Contributor

    @jelbourn your use case is pretty close to content reprojection, although not quite as you need to project into another component.

    @jelbourn @mhevery
    How about this proposal: Every component can ask for the ProtoView that represents its light dom? With this, it gets a lot easier to implement your own content reprojection.

  6. tbosch commented on Jun 1, 2015

    @tbosch
    Contributor

    Because of native content reprojection, users are already used to the thought that things that they put into the light dom of a component can show up somewhere else. So this would not be surprising or a new concept to the user...

    Also, if we assume this, we could have a new directive ng-content which would just get the ProtoView from the parent component and stamp it out. I.e. we would have a simple but extensible form of content reprojection that is based on views rather than direct DOM manipulation...

  7. mhevery commented on Jun 1, 2015

    @mhevery
    Contributor

    We discussed this with @vsavkin and @tbosch and we decided that this is a real use case which needs a design doc to explore this more.

  8. 7 remaining items

  9. modified the milestones: alpha-30, alpha-29 on Jul 1, 2015
  10. modified the milestone: alpha-31 on Jul 1, 2015
  11. modified the milestones: alpha-31, alpha-32 on Jul 11, 2015
  12. tbosch commented on Jul 16, 2015

    @tbosch
    Contributor

    This is now possible via this construct:

    @Component({
      selector: 'tab',
    })
    @View({
      template: `<template capture-template><ng-content></ng-content></template>`,
      directives: CaptureTemplate
    })
    class Tab {}
    
    @Directive({
      selector: '[capture-template]'
    })
    class CaptureTemplate {
      constructor(public elementRef:ElementRef, pubic protoViewRef:ProtoViewRef) {}
    }
    
  13. angular-automatic-lock-bot commented on Sep 6, 2019

    @angular-automatic-lock-bot

    This issue has been automatically locked due to inactivity.
    Please file a new issue if you are encountering a similar or related problem.

    Read more about our automatic conversation locking policy.

    This action has been performed automatically by a bot.

  14. locked and limited conversation to collaborators on Sep 6, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions