I am starting to use Ractive and I would like to understand better how to make reusable components. It is quite easy to write a generic widget that can be used in different situations. My problem is that often the data in my applications does not come in the shape that the widget expects and I am not sure how to extend the widget to make it more application specific.
To make a concrete example, say I am writing a Ractive component to design a line chart. I would do something along the lines of
var Chart = Ractive.extend({
template: chart,
data: {
points_to_path: function(points) {
// whatever
}
}
});
with a template like
<svg>
{{# points_to_path(points) }}
<path d="{{ path }}" />
{{/ end of path }}
</svg>
This component could be used like
var chart = new Chart({
data: {
points: [[1, 2], [3, 4], [5, 6]]
}
});
Now assume I actually need to use such a component. Probably my data does not come in the shape of an array of two-components arrays, like points
above. Maybe I have something more like
[
{
date: 'aug 2011',
value: 12.51
},
{
date: 'sep 2011',
value: 12.38
},
...
]
So, what I would like to do is to make a more application specific Chart
that accepts data in the above form. I do not see an easy way to do this with Chart.extend
, because the reference to the points
variable is hardcoded in chart.html
.
In a framework like Backbone, views are nested, so I could just wrap the inner widget into a bigger view and in the external view define the logic for the data transformation. I am not quite sure how to go on from here in Ractive. Maybe I should have designed the widget in a different way from the start.
More generally, my problem is that I can write small components in Ractive, but I am not quite sure how to compose them into something bigger.
What is the idiomatic way in Ractive to specialize or extend components? Or combine many components into a bigger application?
There's a couple of possible solutions. I'm not sure what would qualify as idiomatic - generally, Ractive's philosophy is 'if it works, it's the right solution!' - I'm the author, but I'll leave it to the community to decide over time what is idiomatic.
1. Passing in functions at initialisation
If you were using d3 to create a scatterplot, typically you would do something like this:
In other words, for each
datumitem in the array, we've supplied a function that extracts the value we need to plot its x and y position.The Ractive equivalent would be to specify a function when initialising the component:
So if you had an array of points with
date
andvalue
properties, you'd just pass anormalise
function (ornormalize
if you prefer American spelling...) that returned{x: point.date, y: point.value}
or whatever.(Of course, you could overwrite the whole
points_to_path
function instead if that was easier.)2. Using partials
An alternative would be to use different partials in different situations:
This gives you a lot of freedom, but the line chart example is probably more cumbersome to adapt.
Inline components
The first method would also work with inline components - this example assumes you're using the development (0.3.8-pre) version, which will be released soon:
(Protip: you could specify a default
normalise
function withChart.data.normalise = someFunction
.)The second method is a little trickier - you would have to dynamically switch out the partial on initialisation:
Sorry for the epic length of this response, hope the answer you wanted was in here somewhere!