I've begun work on can.Component. can.Component is CanJS's answer to web components. It's goal is to simplify some often repeated processes in creating widgets. Namely:
Initializing them.
Creating a view model for them.
Rendering a template.
can.Component is going to be the union of can.Map (formerly can.Observe), can.Control, and a template.
Before I get to far, I'd like feedback on API / features. I'll be updating this thread with thoughts as I go along.
Lets start with an example:
A Tabs Widget
Lets say I wanted to create a tabs widget on my page that displays different foodTypes. What could be easier than rendering a template like:
- <tabs>
- {{#each foodTypes}}
- <panel title='{{title}}'>{{content}}</panel>
- {{/each}}
- </tabs>
With data like:
- new can.List([
- {title: "Fruits", content: "oranges, apples"},
- {title: "Breads", content: "pasta, cereal"},
- {title: "Sweets", content: "ice cream, candy"}
- ])
Using can.Component, we can create a tab and panel widgets like:
Tabs:
- can.Component.extend({
- tag: "tabs",
- template:
- "<ul>"+
- "{{#each panels}}"+
- "<li {{#isActive}}class='active'{{/isActive}} on-click='setActive'>{{title}}</li>"+
- "{{/each}}"+
- "</ul>"+
- "<content></content>",
- scope: {
- panels: [],
- addPanel: function(panel){
- this.attr("panels").push(panel)
- },
- removePanel: function(panel){
- var panels = this.attr("panels");
- panels.splice(panels.indexOf(panel),1)
- },
- setActive: function(scope){
- this.attr("active",scope);
- },
- isActive: function( title,options ) {
- if(this.attr('active.title') == title){
- return true;
- }
- }
- }
- });
Panel:
- can.Component.extend({
- tag:"panel",
- scope: {
- title: "@"
- },
- events: {
- inserted: function(){
- this.element.parent().scope().addPanel( this.scope );
- },
- removed: function(){
- this.element.parent().scope().removePanel( this.scope );
- }
- }
- })
Here's the basic parts of a can.Component:
tag - The custom tag this widget will be created on.
scope - the prototype properties and methods of a can.Map (formerly can.Observe).
template - A template to be rendered within the custom element. A template can have a "<content></content>" element where it will put the original content of the template within.The special title: "@" property on Panel links an element's attribute to a scope property.
events - Declarative event listeners very similar to a can.Control.
Other improvements:
Declarative bindings - Notice the on-click in the tabs template:
- <li {{#isActive}}class='active'{{/isActive}} on-click='setActive'>{{title}}</li>
This will call setActive on the control's scope.
Inserted and Removed events - We added support to know when jQuery inserts or removes an element. I'm hoping to add support to all libraries for this feature and to make it possible to listen for any DOM mutation event. Furthermore, I'd like to be able to live-bind to DOM values.
Two way bindings - We haven't decided on the API for this yet. It's possible we might make live-binding work with a can-value attribute like:
- <input can-value="person.name"/>
Thoughts?
P.S. I want to thank Andy Kant @andykant for helping me get the API to this point.