Given a template that displays a count passed into it:
<div>
The current count is: ${data.count}
</div>
You could turn this into a stateful widget by simply adding a method and a click handler:
<div on('click', incrementCount)>
The current count is: ${data.count}
</div>
method incrementCount() {
this.set('count', data.count+1)
}
When clicked, the div
will call the incrementCount
method and
it will set the widget's state which causes the template to rerender
with the new state passed in as data. In order to simply the thinking,
only data
exists in this new paradigm, there's no mention of state
.
This approach makes it so that adding client-side widget is a simple as adding a method right in the template. It's very intuitive and doesn't need a whole lot of additional code/boilerplate.
The downside is tooling support.We can get syntax highlighting working, no problem, but linting, setting breakpoints, and such is not going to work. The user may be able to do some of this against the compiled output, which should be very readable, but that's not ideal.
This is actually quite easily accomplished by aliasing the
setState
and setStateDirty
methods.
Widget.prototype.set = Widget.prototype.setState;
Widget.prototype.forceRerender = Widget.prototype.setStateDirty;
The data
variable inside these methods actually refers to this.state
as seen in the compile code:
function() {
var data = this.state; // this line added
this.set('count', data.count+1)
}
The actual implementation of this is somewhat generic.
The method
keyword is actually a custom tag that would be handled at compile-time by the
marko-widgets
taglib. The incrementCount()
signature is an attribute argument and the body of the
method/function is a new construct that requires an update to the htmljs-parser
: the block
.
A block
is simple curly braces ({
, }
) with javascript statements inside.
Before addding widget:
var __marko = require('marko'),
__markoHelpers = __marko.h,
marko_escapeXml = __markoHelpers.x;
function renderer(data, out) {
out.w("<div class=\"counter\">" +
marko_escapeXml(data.count) +
"</div>");
}
module.exports = __marko.t(__filename, renderer);
After adding widget:
var __marko = require('marko'),
__markoHelpers = __marko.h,
marko_escapeXml = __markoHelpers.x;
function renderer(data, out) {
out.w("<div id=\"w0\" class=\"counter\" data-on-click=\"incrementCount|w0\">" +
marko_escapeXml(data.count) +
"</div>");
}
module.exports = __marko.w(__filename, {
renderer: renderer,
incrementCount: function() {
var data = this.state;
this.set('count', data.count+1)
}
});