Proposal: Component + Sub-Components


#1

Problem

There have been a number of posts that have been asking a similar question and it seems that there’s a problem that I don’t think has been properly solved in Ember yet.

In short, Ember components should have the ability to create, append, and destroy sub-components without having to explicitly declare them in the component’s template.

Here’s some examples of those posts:

In addition to these, The Ember UI library attempts to do this proposal by adding a show method to a component’s class…and the component is appended to the DOM root of the ember application. You can see this implementation here.

Use Case

Some of the use cases here are outlined in the posts above. But, here’s another use case that might resonate with some folks:

Suppose you have an D3-based chart. You want the users to be able to control the type of graph that they’re looking at, even though the data underneath the graph does not change (think Excel’s graph types: Bar Chart, Scatter Graph, Pie Chart, etc).

Right now, there’s not really an ideal way to manage this state change of this graph. Here’s a few ways you could:

Option 1

Don’t do this with Components. Use Ember.ContainerView to manually manage view state. Each of the graph types are also plain old Ember.View. Ember decidedly moved away from views toward components, so this feels like taking a few steps backwards.

Option 2

Create a turducken-chart component which is all the chart options in one. A chartType is passed in and the component renders just that type, perhaps by there being a number of mixins or something that define all the different types of charts. This is workable, but still feels clumsy.

Option 3

Create components that are made aware of wether they should render; then, internally, the component will optionally render itself or not.

I think that there’s a few problems with this solution. First, this introduces some added complexity to a component to manage its state and needs to be instantiated/rendered even if it has nothing to do. That’s annoying overhead.

Second, you end up with something like this…which is not really scalable (and your controller has to have calculated properties to grow with it):

{{#svg-wrapper width=800 height=500}}
  {{bar-chart data=data shouldRender=shouldRenderBarChart}}
  {{line-chart data=data shouldRender=shouldRenderLineChart}}
  {{scatter-chart data=data shouldRender=shouldRenderScatterChart}}
  {{pie-chart data=data shouldRender=shouldRenderPieChart}}
{{/svg-wrapper}}

Ideal Solution

Ideally, I could write this:

{{dynamic-chart width=800 height=500 data=data type=chartType}}

The template for that component might look something like this:

<svg {{bind-attr width=width height=height}}>
  {{outlet}}
</svg>

Then, in my component, I could do something like this to instantiate a sub-component when my type changes.

export default Ember.Component.extend({
  type: 'line-chart',
  data: null,
  width: null,
  height: null,
  watchType: function() {
    this.renderComponentIntoOutlet({
      component: this.get('type'),
      context: ['data=data']
    });
  }.observes('type').on('didInsertElement')
});

There’s a few things to consider with this:

  1. Since the “sub-component” cannot be passed context variables from the controller, they have to be passed from the parent component. What’s the best way to do this? (context: ['data=data'] isn’t it; but it’s something)
  2. How do actions work in this scenario? Should the actions in the sub-component bubble to the parent component if true is returned? Or, should that only happen with sendAction?
  3. Does this just confuse people more now that a component can have an outlet and a yield? Is there a better way to think about this?

I would really like to see this functionality; I think there are others who might agree and I don’t think I’ve seen this functionality formally requested or implemented.

I look forward to the thoughts of others!


#2

@Spencer_Price Seems useful. Not sure about the scoping issues play out. There may be something about the intended isolation of components that would be make this tricky. But certainly chaining together related components seems like a useful pattern. Maybe some of this can be done with a bus pattern and data binding. And perhaps you have a separate component watching the the other components or shared properties. But then I start to ask what’s the difference from a regular controller? Or multiple controllers.

Are you familiar with the Ember RFC process?

You might want to consider drafting a proposal rfc document around this. That is one way to clarify the technical issues involved.


#3

I didn’t realize ember could do sub-components, I’ve had trouble with nested components, so great to know that’s an option.


#4

This discussion may be related to RFC#3 and this issue


#5

@eccegordo — yea, I’m familiar with the process. Thought I’d float this here before doing an RFC. And, I understand your point about how components are isolated that makes this tricky, certainly.

@ppcano — I think you’re right: those issues probably need to be resolved determining how to implement this.