Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Template inheritance #11167

Closed
RobbieTheWagner opened this issue May 14, 2015 · 18 comments
Closed

Template inheritance #11167

RobbieTheWagner opened this issue May 14, 2015 · 18 comments

Comments

@RobbieTheWagner
Copy link
Member

I think something like this should be supported, especially with the push towards components.

I want to have a parent component with a template like:

<div>Foo</div>
<div>Bar</div>
{{yield}}

And then I would expect for child components, that extend that parent component to just be able to define a template of:

<div>Baz</div>

And have it automatically put that in the yield, so whenever I used the child component, it would show up as:

<div>Foo</div>
<div>Bar</div>
<div>Baz</div>
@selvagsz
Copy link

+1 for this. I ran into a similar use case. I'm not sure whether it is semantically correct to inherit the template. I worked around it by specifying the layout name for the child component and rendering it as a block.

@mixonic
Copy link
Sponsor Member

mixonic commented May 15, 2015

@rwwagner90 This seems pretty unlikely to be adopted- Overriding how and where a component {{yield}}s is very much something a child component (via inheritance) would want to do. IMO you want a way to call {{super}} from a template to accomplish what you want here.

But templates play absolutely no role in the inheritance flow right now, and making them aware of it is a rough sell. Hm.

@RobbieTheWagner
Copy link
Member Author

@mixonic it just seems counter intuitive to me to not be able to do some sort of inheritance. If I have a parent component that has a lot of common functionality and DOM elements, I obviously want to have my child component extend it for the JS functionality, but then there is no provision for pulling in the common DOM from the parent template, unless you use partials, which blows away the point of the component inheritance or actually use component blocks, which forces you to duplicate code.

@jmurphyau
Copy link
Contributor

I've managed to get the behaviour you're describing working.. I'm not sure I would recommend it and I'm likely going to move away from it in favour of tagless components.

I achieved this by setting the components layout property to the parent component template, and the template property to the child (current) component.

If you're using Ember CLI with pod structure there is a particular way to get this to work. Basically when a component is resolved Ember will try find the template.hbs file - once that's found it uses that template as the layout property - completely ignoring if you've explicitly set a layout property on your component.js - you need to trick Ember by calling the child component template something other than template.hbs

Anyway I'm not sure I've explained it well enough - but if I haven't then I have an example that might help. I've previously logged this as an issue with Ember CLI (ember-cli/ember-cli#3496) - in this issue there is a link to a repo (https://github.com/jmurphyau/component-template-issue) which shows how I got the inheritance to work.

@mixonic
Copy link
Sponsor Member

mixonic commented May 15, 2015

The layout represents a components shadow dom. The template is the "light" dom, of the contents of yield that gets rendered in the outer scope. I definitely don't think that is a viable long-term solution, and may have some weird edge cases.

@RobbieTheWagner
Copy link
Member Author

So let's assume for a minute that I have an inheritance chain like parent -> child -> child -> child. What would be the recommended way to use all the common elements from each level in the final child component? It would make sense to me to have some way to have the templates work together.

@mixonic
Copy link
Sponsor Member

mixonic commented May 15, 2015

@rwwagner90 a compiled template is just an object stored on a property. Like any other property on an instance, there is no way to arbitrarily yank parts of an object you've replaced from a superclass at the same property.

I think I suggested a fairly viable idea with {{super but the implementation and ramifications across apps are unclear (like in init, do you always call super just in case you have a parent? Seems onerous).

I hope you agree there isn't a trivial solution and the problem will require a lot of thought and api design work.

@RobbieTheWagner
Copy link
Member Author

Yeah, I definitely don't think there is a trivial solution. There are some "easier" solutions, but they could have bad implications. I'm not sure how to get this right, but I think it would be very useful to have. Changing everything over to components, but then still requiring you to use partials because this isn't supported just feels incomplete to me. I realize this may not be implemented anytime soon, but do you agree there is a need there @mixonic?

@jmurphyau
Copy link
Contributor

Could you do something like this:

child-component/template.hbs

{{#parent-component}}
  This is the child component
{{/parent-component}}

parent-component/template.hbs

{{#grandparent-component}}
  This is the parent component
  {{yield}}
{{/grandparent-component}}

grandparent-component/template.hbs

This is the grandparent component
{{yield}}

@jmurphyau
Copy link
Contributor

That outputs the HTML below when calling {{child-component}}

<div id="ember434" class="ember-view grandparent parent child">
  <div id="ember441" class="ember-view grandparent parent">
    <div id="ember446" class="ember-view grandparent">
      This is the grandparent component
      This is the parent component
      This is the child component
    </div>
  </div>
</div>

This was/is a problem for me and this is where tagless components are have an advantage - if the {{grandparent-component}} and {{parent-component}} both have tagName: '' you only end up with one div:

<div id="ember434" class="ember-view grandparent parent child">
  This is the grandparent component
  This is the parent component
  This is the child component
</div>

@RobbieTheWagner
Copy link
Member Author

@jmurphyau we did consider an option along those lines, but won't wrapping each of those like that cause us to have to pass variables back up the chain to the grandparent component?

@jmurphyau
Copy link
Contributor

I would expect so - yeah.

@RobbieTheWagner
Copy link
Member Author

I'd like to avoid having to do that. There may not be a way around it though. Just would really like to see this implemented. Would be very slick.

@stefanpenner
Copy link
Member

They may be good to explore in an RFC or RFC issue, as this appears as a feature request not a bug report.

@RobbieTheWagner
Copy link
Member Author

@stefanpenner what should I do to start the discussion in a more appropriate place?

@mmun
Copy link
Member

mmun commented Jul 12, 2015

@rwwagner90 Make an issue (not a PR) on the RFCs repo emberjs/rfcs and gather feedback. I'm interested in seeing actual use cases rather than foo and bar. We should have this discussion ASAP so the feedback can be taken into account for the work on angle bracket components.

@RobbieTheWagner
Copy link
Member Author

@mmun I made an issue in rfcs. emberjs/rfcs#77

It's been awhile since I had an actual use case for this, but I'll try to come up with a better example than foo and bar.

@hoIIer
Copy link

hoIIer commented Jul 26, 2015

hello, I am running into an issue where I have a parent route and I want the template to have some default content, but render over that with a child route.

Example:

import Ember from 'ember'
import config from './config/environment'

Router = Ember.Router.extend
  location: config.locationType

Router.map ->

  # Channels
  @route 'channels', path: '/', ->
    @route 'channel', path: ':slug', ->

      # Content
      @route 'content', path: ':slug'

export default Router


# templates/channels/index.hbs

<main class="main channels container-fluid">
  <div class="row">
    <h1>The brown fox jumped over the...</h1>
    {{#outlet}}
      <p>Here I am, the content for my cool component that displays within the parent index view.</p>
    {{/outlet}}
  </div>
</main>


# templates/channels/channel.hbs

<p>Here I am, the content for the child route/template</p>

Is this possible? it seems to touch in template inheritance which is how I found this issue... Is there any way to achieve this currently?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

7 participants