Since this commit landed, I’ve been focusing on how we can start composing shareable components and establishing a nice ecosystem of reusable components in the Ember community. There’s no real consensus on this from the core team, these are just some of my thoughts, but here’s a little writeup, would love your thoughts:
Ideas for components / composability
Ember now has the capability to implement the following patterns, but I’d like to see what people think of these patterns and whether there are use cases that have been left out, concerns, etc.
Calendar Widget
This has been Trek’s canonical example of composability. I don’t think I’m going totally in the same direction of what he had in mind; I’m probably focusing more on highlighting the configurability of default components rather than making use of an ecosystem of single-purpose, reusable low-level components, but they’re somewhat closely related.
Add the calendar to your app.
// Within some initializer...
// Not focusing too much on how EmberCalendar is loaded into your
// app, just assume it's global for now.
container.register('component:ember-calendar', EmberCalendarComponent);
// fwiw, though we're trying to get away from storing stuff on globals,
// the following will also work
App.EmberCalendarComponent = EmberCalendar;
This makes the calendar renderable in your templates via
{{ember-calendar}}
.
Render the default calendar
{{ember-calendar}}
(pardon the jQuery UI ripoff)
Override some defaults
Internally, the ember-calendar
defines lots of blocks of content that
can be overridden within the block you provided to ember-calendar
.
Here we override month-header
to remove the year and add some
exclamation points.
Note that this syntax is strange by today’s Handlebars conventions, but go with it for a second.
{{#ember-calendar}}
{{#month-header}}
{{month}}!!!!
{{/month-header}}
{{/ember-calendar}}
Override multiple defaults
To override more things, just provide multiple blocks…
{{#ember-calendar}}
{{#month-header}}
{{month}}!!!!
{{/month-header}}
{{#day-cell}}
{{day}}!
{{/day-cell}}
{{/ember-calendar}}
Handling events
This isn’t specific to new composability stuff, but you can combine the above with standard component event handling API.
{{#ember-calendar day-selected='daySelected'}}
{{#day-cell}}
{{day}}!
{{/day-cell}}
{{/ember-calendar}}
You could handle this event in some controller that closes the datepicker, saves it to a model, proceeeds to the next step in a wizard, etc.
super
I don’t know how I feel about this yet, but here goes; say we want to make the month header clickable, even though the component author never intended it to be.
App.CustomCalendar = CalendarComponent.extend({
actions: {
monthHeaderClicked: function() {
// Do some custom stuff, maybe call `this.sendAction()`
// to forward to some outside controller.
}
}
});
{{#ember-calendar}}
{{#month-header}}
<a href="#" {{action 'monthHeaderClicked'}}>
{{super}}
</a>
{{/month-header}}
{{/ember-calendar}}
We can easily wrap in an a
tag, make it fire an event, and display
the original content inside the a
tag.
On the authoring side of things
I haven’t written the code to support the above, yet, but it’s all very
doable. It’d largely be an extension of this JSBin,
which makes use of a very Railsy content-for
helper to accomplish
much of the above. It seems like the authoring solution will follow a
similar pattern of 1) define your default content blocks, 2) call yield
, 3)
invoke the top-most component/helper of your shareable component.
TODO
- Establish a pattern for sharing/importing components (it’s easy enough
to
defer / advanceReadiness
at the start of an app to make sure all your components are loaded ahead of time, among other things, but we should spend some time making sure whatever we settle on looks pretty, demoes well, etc.) - Adopt any awesome ideas yall have?