- Solution description
- TodoMVC example
- Installation
- Usage from command line
- Usage from code
- Additional features
- Gulp plugin
- Grunt plugin
First problem of react is HTML inside our JS files and there is no way to make them separate.
So I want to take out HTML from this class to separate file
var MenuExample = React.createClass({
getInitialState: function() {
return {focused: 0};
},
clicked: function(index) {
this.setState({focused: index});
},
render: function() {
var self = this;
return (
<div>
<ul>
{this.props.items.map(function(m, index) {
var style = '';
if (self.state.focused == index) {
style = 'focused';
}
return (<li className={style}>{m}</li>);
})}
</ul>
<p>Selected: {this.props.items[this.state.focused]}</p>
</div>
);
}
});
But if we just take out everything after return
it's not what I want. I need absolutely clear HTML so I can write
my CSS and see results without running whole app. I want to se template like this
<div>
<ul>
<li class={style}>{m}</li>
</ul>
<p>Selected: {this.props.items[this.state.focused]}</p>
</div>
Okay, first of all we need to put this.props.items.map(...)
in ul
. I thought, annotation is good enough to make link
to some function like this
<div>
<ul>
<!-- @render list -->
</ul>
<p>Selected: {this.props.items[this.state.focused]}</p>
</div>
But where should be that list
function? What if it will be right after our return
, where was this template?
var MenuExample = React.createClass({
//...
render: function() {
var self = this;
return {
list: function () {
this.props.items.map(function(m, index) {
var style = '';
if (self.state.focused == index) {
style = 'focused';
}
return <li className={style}>{m}</li>;
})
}
};
}
});
But wait, not that again, HTML in code, maybe we can take it out, but it should be in same template file. Maybe we will make some annotation which will be replaced with HTML (with some unique id) from our template?
var MenuExample = React.createClass({
//...
render: function() {
var self = this;
return {
list: function () {
this.props.items.map(function(m, index) {
var style = '';
if (self.state.focused == index) {
style = 'focused';
}
return "@jsx-tpl item";
})
}
};
}
});
Annotation not in comment because it is a value, not just some meta information. Lets call attribute with this unique id just like annotation.
<div>
<ul>
<!-- @render list -->
<li jsx-tpl="item" class={style}>{m}</li>
</ul>
<p>Selected: {this.props.items[this.state.focused]}</p>
</div>
Lets say that all tags with attribute jsx-tpl
will be detached from template and can be used only in classes to replace
@jsx-tpl
annotations. So basically they can be anywhere in template.
Last thing, what if we want many templates in one html file? Lets add jsx-class
attribute with displayName value of
react class to element in template to join them
<div jsx-class="Menu">
<ul>
<!-- @render list -->
<li jsx-tpl="item" class={style}>{m}</li>
</ul>
<p>Selected: {this.props.items[this.state.focused]}</p>
</div>
<div jsx-class="SomeOtherComponent">
...
</div>
So we should add to our classes filed displayName:
var MenuExample = React.createClass({
displayName: 'Menu',
//...
render: function () {
//...
}
});
var x = React.createClass({
displayName: 'SomeOtherComponent',
//...
render: function () {
//...
}
});
If you define jsx-class
in another jsx-class
or jsx-tpl
then this definition will be replaced to the end of body, but what if you need instance of this class right in this place? Just add jsx-instance="true"
to it. All attributes with curly brackets will be with instance.
Example
<div>
<ul jsx-class="List">
<li jsx-class="Item" jsx-instance="true" user="{user}" class="row" data-id="1">
<h3>{user.name}</h3>
</li>
</ul>
</div>
Will be converted to
<div>
<ul jsx-class="List">
<Item jsx-tpl="item" user="{user}" />
</ul>
</div>
<li jsx-class="Item" class="row" data-id="1">
<h3>{user.name}</h3>
</li>
Here original implementation of TodoMVC project on React and here same project but with separated html examples/todomvc. You can see that each component has it template. But wait, checkout one combined template components/index.html for all components. You can use it to compile all components. You can just open it and see how your app looks like without running js code. How cool is that?
npm install react-st
Just run react-st -j test/js/menu.js
. It will take file test/js/menu.js, join with test/js/menu.html
and save to test/js/menu.jsx
To see all options run react-st -h
Example
var convert = require('react-st');
convert(jsString, htmlString, function (err, jsxString) {
// check err
// save jsxString to file or whatever you want
});
- All
class
attributes will renamed toclassName
- All
jsx-spread="someVar"
attributes will be converted to Spread Attribute{...someVar}
- I added syntax like this
class="item {style}"
and it will be converted toclassName={"item " + style}
. Helpful for styling html without running js code
All attributes with curly brackets will be converted to react jsx version. If you will write something like this
data-bind="attr: {active: isActive}"
it will be converted to data-bind={'attr: ' + active: isActive}
, so be careful.
See https://github.com/redexp/gulp-react-st
See https://github.com/TheWolfNL/grunt-react-separate-templates
I will be glad if you have any suggestions, just create an issue.