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:
- 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) - 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
? - Does this just confuse people more now that a component can have an
outlet
and ayield
? 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!