Open kristianmandrup opened 8 years ago
Hi @kristianmandrup, that is kind of supported, but the problem is that the defineComponent(def)
and the defineRenderer(def)
functions add some additional _static_ methods that are important to rendering:
render(input) : RenderResult
⟵ This function can be used to render the UI componentrenderer(input, out)
⟵ This function is used as the Marko tag renderer for the UI component's custom tagThose static methods won't get added if you just export a class. Instead, you would need to do the following:
export default class Widget {
constructor(widgetConfig) {
}
}
var renderer = require('marko-widgets').defineRenderer({
template: require('./template.marko'),
getTemplateData: function(state, input) {
// ...
}
});
Widget.renderer = Widget.prototype.renderer = renderer;
Widget.render = renderer.render;
While it would be nice to just use the class
syntax, I don't think there is a good way to avoid the boilerplate if you want to attach the static rendering functions to the Widget
class.
The elegant solutions: ES7 decorators ;)
@Widget({template: '../cool/template.marko'})
export default class MyCoolWidget extends CoolWidget {
constructor(widgetConfig) {
super(widgetConfig);
}
}
https://github.com/wycats/javascript-decorators
It is also possible to decorate the class itself. In this case, the decorator takes the target constructor. Since decorators are expressions, decorators can take additional arguments and act like a factory.
http://www.2ality.com/2015/01/es6-destructuring.html http://www.2ality.com/2011/11/keyword-parameters.html
Destructuring can be used to effectively "simulate" optional function arguments
// Widget decorator: adds static renderer function to class
function Widget({template = './template.marko', templateData = undefined} = {}) {
return function decorator(constructor)
var templateData = templateData || constructor.prototype.getTemplateData;
var renderer = require('marko-widgets').defineRenderer({
template: require(template), // "configuration then convention " ;)
getTemplateData: function(state, input) {
templateData(state, input);
}
});
constructor.renderer = constructor.prototype.renderer = renderer;
constructor.render = renderer.render;
}
}
Pretty cool I should say :)
Hey, I'm trying a small experimental project using ES7 with marko templates.
https://github.com/kristianmandrup/marko-es7
However when I try your proposed pattern using
template: require('./template.marko')
I get an error:
SyntaxError: /Users/kristianmandrup/repos/test123/marko-es7/dist/template.marko: Unexpected token (1:6)
> 1 | Hello $data.name!
| ^
at Parser.pp.raise (/Users/kristianmandrup/repos/test123/marko-es7/node_modules/babel-core/node_modules/babylon/lib/parser/location.js:24:13)
Looks like the babel parser is trying to parse it. Should just be "required" by the regular node require
and transformed into valid javascript. I think you've added a special require hook for loading/transforming .marko
files? How do I ensure that hook is being used without babel interference?
Thanks ;)
I kinda have it working now, except I'm a bit lost on how to test this infrastructure properly :P Any assistance would be greatly appreciated!! :) Please see my project.
Test https://github.com/kristianmandrup/marko-es7/blob/master/test/widget_test.js#L49
template config https://github.com/kristianmandrup/marko-es7/blob/master/lib/widget.js#L19
use of template https://github.com/kristianmandrup/marko-es7/blob/master/lib/index.js#L19
How do I access and use the template correctly here?
Unless I am missing something, the user should not be implementing the render(input, out)
method for a UI component. Nor should the user be accessing the template directly. The user should just be implementing the getTemplateData(state, input)
method.
Might be premature to use ES7, but interesting none the less. Personally, I am wary of decorators since I think they were abused in languages like Java (i.e. Java annotations).
Hey @kristianmandrup, were you able to come up with a good solution based on ES7 decorators?
Since I don't know the internals of defineWidget
it was rather hard to debug given the limited time I was willing to throw at it. Mainly I just setup the babel infrastructure. I had problems requiring the .marko
template correctly and not sure about my test infrastructure either. Would require a joint effort for ~30 mins I would think...
Now that Node.js 4.0 is out and most other modern frameworks leverage the concept of Classes in Javascript, I think it would be very appropriate if Marko Widgets (and Marko Tags in general) supported class definitions as a (modern) alternative. You could even leverage decorators, for those using Babel.
Could be defined as a class, like this.
Using class properties
or pure ES2015 getter
Looks pretty cool and concise to me :) The nice thing is that it would be easy to do widget inheritance as well. Decorators can be used to provide mixin like behavior if needed (like in React components).