Concepts in CanJS that don’t map to JSX
two-way binding vs 1 way binding
There really is no concept of binding in JSX right, you are just assigning dynamic attributes
return <MyComponent someAttr={ somethingFromThisFunctionsScope } />
Would we default to 1-way parent to child? Or 2-way binding?
return <MyComponent numRemaining={ viewModel.count } />
Event binding
Related to the last point, do we embrace the idea of passing callbacks into components for observations (not really the can-js way), or do we introduce some sort of event binding? Do we listen for bubbled events? What about capture phase events?
return ([
<MyComponent onBubbledEvent={ vm.handleBubbled } />
<MyComponent onEventCapture={ vm.handleCaptured } />
])
Binding to sibling components attrs
<drivers-licenses {^selected}="*selectedDriver"/>
<edit-driver {driver}="*selectedDriver"/>
What would this look like with JSX?
Child to parent binding
<my-component childProp:to="value"/>
What does that look like? Event binding? Callbacks?
Other things to think about
Higher Order Components
Do our JSX components create custom-elements? Or are they like React components that are just functions that may or may not return DOM-generating vdom?
connectViewModel(ViewModel)(props => (
<div className={ props.type }>{ props.message }</div>
))
If we choose the latter, we would be able to embrace the interesting idea of HoCs, but if not, do we only do “dashed” components?
return ([
<my-component onClick={ vm.toggleActive } />
<something-else onInput={ ev => vm.setTitle(ev.target.value) } />
])
View-Models
I really like the idea of the view model not knowing anything about DOM. It would be nice if all the messy DOM and events stuff was limited to a “view”.
class MyComponent extends can.JSXComponent {
updateText(ev) {
this.viewModel.updateText(ev.target.value);
}
handleKeydown(ev) {
if (event.which == 13 || event.keyCode == 13) {
ev.preventDefault();
this.viewModel.editing = false;
}
}
toggleEdit() {
this.isEditing = !this.isEditing;
}
render() {
if (this.isEditing) {
return <input onInput={ this.updateText }
onKeydown={ this.handleKeyDown }
value={ this.viewModel.text } />
}
return <span onDoubleClick={ this.toggleEdit }>{this.viewModel.text}</span>
}
}
So should our “views” be some sort of class with it’s own DOM state, and lifecycle, and the ability to hold some form of state? At that point are we just recreating React?
Or should we just use JSX in our view/template function? If then, are we losing some of the benefits of JSX?
const ViewModel = DefineMap(/*...*/);
class ThingyModal extends can.JSXComponent {
tag = 'thingy-modal';
leakScope = false;
events = {
'body click'(ev) {
if (this.viewModel.isOpen && !ev.target.contains(this.element)) {
this.viewModel.isOpen = false
}
}
};
ViewModel() {
return new ViewModel();
}
view() {
return (
<bit-modal showModal={this.viewModel.isOpen}>
<h1>Thingy</h1>
<img src="./thingy" />
<p>Some words about the thingy</p>
</bit-modal>
);
}
}
…to me there is absolutely nothing compelling about using JSX in this example or Kevins super simple examples. We’d be better off sticking with stash.
Are we talking specifically about Spec the https://facebook.github.io/jsx/ ?
…or how it’s used in React?
What is the real appeal of JSX?
On thing for me is the idea that I don’t have to do anything template specific for conditionals, and if/else and comparisons.
If I can make it work in JavaScript I “know” it will work in JSX.
For example this simple and easy to understand bit of JSX
{ a === b && c === d ? (
<div>Compatible</div>
) : (
<div class="warn">Incompatible</div>
)}
Becomes more complicated in stache
{{#is a b }}
{{#is c d }}
<div>Compatible</div>
{{else}}
<div class="warn">Incompatible</div>
{{/is}}
{{else}}
<div class="warn">Incompatible</div>
{{/is}}
…and pretty much encourages you to make a helper or a function expression from your scope to handle the logic, or you end up repeating yourself.
This is a simple example, but as complexity grows, as do the troubles with the template abstraction.
Personally, I find this easier to understand:
const pending = tasks.filter(t => t.status === 'pending');
const closed = tasks.filter(t => t.status === 'closed');
const Task = ({ task }) => (
<div className={`task ${ task.status }`}>
{ task.description }
<footer>{task.deadline}</footer>
</div>
);
return <div className="tasks">
<h3>Pending</h3>
{ pending.map(t => <Task task={t} />) }
<h3>Closed</h3>
{ closed.map(t => <Task task={t} />) }
</div>
…than this…
{{<Task}}
<div class="task {{this.status}}"}>
{{ this.description }}
<footer>{{ this.deadline }}</footer>
</div>
{{/Task}}
<div class="tasks">
<h3>Pending</h3>
{{#tasks}}
{{#eq status 'pending'}}
{{>Task}}
{{/eq}}
{{/tasks}}
<h3>Closed</h3>
{{#tasks}}
{{#eq status 'closed'}}
{{>Task}}
{{/eq}}
{{/tasks}}
</div>
…because it looks like JavaScript (and is) and not some special templating language I need to know the secret sauce for.
So… yeah…
I didn’t answer anything, just slogging some ideas around to think about.