Ractive.js
ractivejs.org
ractivejs.org
I mean, the syntax is different, but unlike other templating systems they try to be "smart" about what needs rerendering by tracking dependencies.
When you choose a technology with a bad website, you have to actually look at that website for hours and hours reading the documentation. This can be incredibly frustrating for people who are sensitive to such things.
But more importantly, there's a subtle biasing. People usually do not make rational decisions about technology. The design of the website is a more significant factor than you might think, at least in my experience of watching people make such decisions. If it wasn't, then why would anybody bother to make a good website?
- it builds on the "Good Parts" of javascript, in that KO observables are just closures. You can pass them around or attach them to different objects however you feel like. For instance, we built a standard UI widget for entering currency values. Anywhere we need to accept currency, we instantiate one of these widgets and pass it the observable that holds the value. Clean, easy, encapsulated.
- Some people have a distaste for the "()" syntax: obj.prop() rather than obj.prop. There's some truth there, but the "()" syntax has one big benefit that overrides the slight ugliness in my experience: it catches naming errors immediately right at their source. If you've ever had to refactor a fairly big piece of JS, you know that one difficulty is correcting all the changed references. And if you miss one, the bug may be subtle, as the old reference doesn't except but quietly returns undefined. With knockout, the old reference blows up immediately, as undefined isn't a function. This is a big win.
We had some growing pains with knockout. Lessons learned:
- don't build anything but a small web app the way the knockout examples demonstrate. The reason is that while knockout claims to be MVVM, their examples are really just VVM -- there're no distinct model classes. They store data directly in the viewmodel classes. Bad things follow as your app grows: since the data is in the VM, any view that needs the data (i.e., all of them) needs to be bound to that same VM, meaning that your one VM ends up needing to feed several views. You find yourself annotating your viewmodel into sections -- "// these properties are used by view X ... // these properties are used by view Y" etc. Yech. Much better to have true model classes, then build a VM class on top of the model for each of your views.
- we now make heavy use of named templates to decompose large views into more manageable pieces. We wrote a simple custom binding to make this easy: since each VM is designed to work with a single view (see prior note), we put the #id of the view's named template into a .Template property of the VM class. The custom binding inspects this property and binds the VM class to its named template.
I've been using knockout for the last 3 years, mostly for small projects but the last one has been a big one. Growing pains but its working out great.
Knockout needs more love.
Have you any code samples written like this you can share? I'm just about to start a large (for Knockout) project and it'd be great to start without making the same mistakes.
> dependency injection minification headaches
Meh, that's hand-wavy at best. The only thing you have to do to pass minification is to include the injected dependencies in an array like so:
['$stateProvider', '$urlRouterProvider',
function ($stateProvider, $urlRouterProvider) {
// Stuff
}]
You get used to it in seconds and then it's not really an issue anymore.Also I don't like having to organize unrelated code into factories and services just because that's how Angular wants to view the world.
There's also a grunt plugin. https://github.com/btford/grunt-ngmin
It's pretty much only useful if you want to write a to-do app.
Hard to explain concisely but it allows fairly complex updates to be expressed very efficiently - see this animated chart for example. The path is created using an expression, which is reevaluated during animation and whenever the dimensions of the chart change: http://www.ractivejs.org/examples/animated-chart/
Say you want to write a compiler whose input is "bound" to a source file; when the input changes, you don't want to recompile the entire file all over again. Instead, you memoize at AST node boundaries, trace dependencies between AST nodes, and then "clean" nodes when they become dirty, detecting changes that affect other nodes to clean them also.
To be honest, I find these data binding frameworks to be very boring, since we've been doing this forever (I did my dissertation on one myself). We really should be looking at the next level that supports incremental processing of more complicated updates.
There is a lot of research in this area, concurrent revisions at MSR or self-adjusting computations at CMU to name a couple. Also, if you've seen Bret Victor's videos and wonder how any of those could be implemented for real, then this is where we need to go. It will be a fun trip though.
The thing I have that I can't see here is a selecting/switching node: essentially a page or a tab that holds multiple states but only presents one at a time.
Thus makes me very happy and very sad: happy as I don't have to write all things things now; sad becasue I don't get to write all these things now.
This looks promising. Can you comment on similarities/differences between Ractive and facebook's React.js in terms of style and performance?
Also, looks like the github minified version is 72k, is that correct?
As far as the style goes, I'm obviously biased, and I'd encourage people to try both to see what suits them best. For me, mustache syntax is very readable, even by non-devs (which makes collaboration and rapid prototyping very easy), and the Ractive API aims to be as easy as possible.
I find React's JSX to be a bit of a barrier (I'm aware that it's not essential to use JSX with React), but as I say it's a matter of personal preference.
Yes, it's currently about that size (27k gzipped) - one of my goals for future versions is to try and wrangle that down. It's not that big a library for the amount of functionality it contains, but smaller is always better.
Syntax differences aside, seems to me that React is geared more towards interactive UI components and Ractive is oriented more towards reactive documents, kind of like Tangle[1]. Is this accurate?
The similarity seems to be in the philosophy of the implementation (manipulating a fast representation and rendering to the DOM). It's pretty cool to see that we both discovered the "secret" though :) Seems like we're validating each others' projects!
If you ever want to nerd out we hang out in #reactjs on freenode.
Yes, you could draw the Tangle comparison, though I think there's probably a fair amount of overlap - a couple of the Ractive examples are recreations of React/Angular demonstrations.
Looking forward to spending some more time with the React source code to try and learn your secrets!
Still working on a solution that mix Backbone for core models + Polymer for view lifecycle and custom elements + Ractive for awesomeness updating.
The only concern I have is that some people do not like to use logicless.. I think React is powerful for letting you declare full javascript evaluations and expressions on your template code and giving you more flexible/productive way of writing.
The JavaScript expression is parsed into an AST, and references to parts of the data model are extracted so they can be dependency tracked and evaluated against the right context. Quite hard to explain concisely but I think it does what you're after.
Ractive's approach appears to be much more suitable for use cases like a journalist interested in only learning barely enough javascript to get by.
Explain why in comparison to angular?
The main issue is that to do anything complicated in angular you need to start writing tricky[1] "link" functions; the React approach completely sidesteps this complexity. There are some tradeoffs you have to accept, like JSX, but they are easily worth it.
[1] tricky is of course relative; angular is easier to code than something like backbone+jquery or backbone+knockout.
I really like the classic "Hello, {name}" example- how would I go about supplementing that so not only is the model constantly updated on the client JS side, but also on the server's database? Is there some place where I put a .ajax() call and Ractive.js handles the rest? (So the functionality would then be to display the name in the database, but whenever modified by the user, the database's value is also modified)
Backbone et al are better at handling that side of things. The Ractive approach is to use an 'adaptor' - so you could create a Backbone Model or Backbone Collection adaptor that maps to a particular 'keypath' (such as 'user' or 'items'), and whenever the model changes, Ractive updates. With two-way binding, user interactions can also change the model.
This part of the library is underdeveloped and experimental at the moment, but that's the approach we'll most likely be taking.
You can use Ractive on the server however - you can use the same data on the server as on the client and call ractive.renderHTML(), which is useful for progressive enhancement.
I haven't looked into Rivet.js, component/reactive or other equivalent library. I'm just planning to research which library is the best fit for me. I don't really like full-stack framework like Angular. It was nice to know yet another candidate.
Does anyone know any other reactive template engine?
I like Knockout.js
On the other hand, Rivets is much smaller, so if you only want the data-binding then it's a fine choice. Personally I really enjoy the freedom of writing mustaches straight into my template, rather than using data-bind attributes everywhere, but it's horses for courses.
Is there a particular niche for this framework would you say e.g. small, fast to market apps like news stories - or is is suitable for larger projects where you might normally reach for more of a "framework"?
It was initially designed to scratch my own itch - I'm a newsroom developer at the Guardian, where we turn around projects with fairly tight deadlines, so I guess you could say it's optimised for that. (Blog post here: http://www.guardian.co.uk/info/developer-blog/2013/jul/24/ra...) In particular I wanted an API that wouldn't be completely mysterious to journalists who are starting to dabble with code.
There's actually a separate discussion on Ractive at https://news.ycombinator.com/item?id=6096545 - I'm not sure what the HN etiquette is, can anyone enlighten me? (I appreciate both submissions though!)
You can certainly use D3 (or any other library) alongside Ractive, though things could go awry if two libraries were trying to manipulate the same bit of the page at the same time - e.g. if an element has a style attribute like style='left: {{left}}px;' and D3 modifies the element's style, D3's changes will only last until the value of {{left}} is updated.
Ractive doesn't have this issue because updates always happen explicitly with ractive.set( 'key', value ) (or with array mutator methods). It's not quite as magical as Angular's frictionless updates, but it gives you slightly more control (e.g. you can do ractive.animate( 'key', value ) and so on), without needing to inherit from custom observable classes.
div.color = 'blue';
instead of: div.set({ color: 'blue' }); or div.set('color', 'blue');
Or would this cause other problems that I'm not thinking of?Edit: my ruby cap is on a little tight today. Javascript programmers might not expect extra logic to be run just by updating a property. I still think it would be cool though :)
No one should use __defineGetter__ and __defineSetter__ because they are non-standard and deprecated[1].
[0] http://kangax.github.io/es5-compat-table/#define-property-ie...
[1] https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe...
Edit: left this tab open and I see this point has since been made, but I will leave the comment anyways.
I'm curious if the IE8 limitation could be worked around, using something like:
if (ie8)
RactiveBaseModel.prototype = HTMLAnchorElement.prototype;I mean, why would
model.set('someProp', 'someVal');
x = model.get('someProp');
be better than model.someProp('someVal');
x = model.someProp();
And the second one is likely to perform better, allows better tooling support (nice to have), is shorter, and allows representing a property as a concept in itself `model.someProp`. You can still access a property with a variable name in the (unusual) case you need to.If you really insist on explicit set/get, then you can still have...
model.someProp.set('someVal');
x = model.someProp.get();
Which still has no downside I can think of, and since this is JS, if you have a variable prop name, you can fall back to...Pretty please, will the unnecessary strings die?
1. It's explicit, and therefore more predictable. I get itchy when libraries take control out of my hands. Though I understand not everyone feels that way!
2. Performance. Getters and setters have a penalty (or did the last time I looked into it in any depth).
3. It only works for existing properties. With Ractive you don't have to declare the 'shape' of your entire model up-front - you can start with a completely empty model and set properties as and when it's convenient. (With ES6 and Object.observe maybe we can sidestep that issue one day.)
4. Performance (again). If you set several properties simultaneously (e.g. ractive.set({ opacity: 0.5, left: 10 }), or whatever), then updates won't happen until all the new data has been taken account of.
5. That irritating thing that happens when you try to do foo.bar.baz = 'bob' and you get an error because foo.bar is undefined.
6. There are some cases where you want to do something like ractive.set( keypath + '.complete', true ) - you can only do that with string-based keypaths.
As for emn13's suggestion of Knockout-style observables, my own experience is that having to inherit from custom observable classes gets cumbersome quite quickly. But different strokes for different folks!
Firefox Aurora 24.0a2 (2013-07-19) and 2013-07-20
TypeError: templateBlock.innerText is undefined @ http://www.ractivejs.org/:217
But yes, you're right, the problem is in the inline code, not in the library.
Array modifications aren't generally a problem, because you'd reference the index from within the template - Ractive keeps the references up to date. Also, proxy event handlers are aware of the current keypath context - see this JSFiddle to see what I mean http://jsfiddle.net/KJt2Y/2/
At the moment i prefer Backbone in my other projects, but it is to "heavy" for a small single page (especially for users that did not know backbone and want to modify the templates).
You should probably explain how ractive.js differentiates itself from knockout. It's the direct competitor out there, not angularJS which is doing things very differently.
I glanced at the source and (correct me if I'm wrong) it seems array templating is done by simply comparing the length of the current and previous arrays? That's naive and buggy?
Good luck
I'll add a comparison table of some sort to my (lengthening!) todo list.
Could you elaborate at all on 'naive and buggy'? I think I probably know what you're getting at, but I'd be interested to hear your thoughts.
Array sections do have a concept of 'smart updates' - if you do e.g. list.push( newItem ) or list.splice( index, 1 ) then it will take a more surgical approach to updates. You can see this in action at http://www.ractivejs.org/examples/todos/, explanation of array modification at https://github.com/Rich-Harris/Ractive/wiki/Array-modificati...
There's also a concept of 'adaptors' which are ideal for Backbone.Model <-> Ractive binding, though it's a bit experimental and incomplete at the moment. You can bind to a model manually like
model = new MyBackboneModel( attrs ); ractive = new Ractive({ el: whatever, template: tpl, data: model.attributes });
model.on( 'change', function ( model ) { ractive.set( model.changed ); });
1. Great library, thanks for contributing!
2. What was your motivation to add transitions and animations, isn't that a bit out of scope?
3. I has no dependencies, why did you choose not to?
2. Not at all - animation is something I need in my day job all the time, and having .animate() saves me so much time. I wasn't sure if I'd get much use out of transitions (I was jealous of ng-animate and wanted to see if I could implement something similar!) but I've found them very useful. Transitions probably need a bit more work though.
3. It just doesn't need any. The amount of code I could save by using a helper library like Underscore wouldn't be worth the potential extra hassle of version headaches etc. Personally I much prefer using 'fire and forget' libraries with no dependencies.
The difference is the difference between declarative and imperative programming. With jQuery you have to describe the steps that the browser has to follow in order to do something (e.g. $button.toggleClass( 'selected' ), whereas with Ractive you're much closer to simply declaring your intentions (e.g. ractive.toggle( 'selected' ), assuming your template references that variable). It's an inherently more scalable approach (which isn't to knock jQuery at all - they're attacking different if overlapping problems).
That's because almost all other templating engines are string -> string (i.e. you add data to a string template, and get HTML) whereas Ractive is string -> DOM. Microtemplates, EJS etc would have to be reimplemented as string -> DOM engines separately if they were to work with Ractive.
HTML is an amazing language for creating static documents, but it was never designed for interactive web apps
https://en.wikipedia.org/wiki/Cambrian_explosion
This is how most major advancements in human civilization occur, not in bio/techno/etc monocultures.
Don't forget to thank Stallman and early FOSS advocates.
Though it is also reactive, in the 'reactive programming' sense.
https://news.ycombinator.com/item?id=6096660
I haven't dug into it yet, but from the description it reminds me a bit of Facebook/Instagram's React js framework [1].
React creates a lightweight model of the DOM in memory, and when any part is changed, it sends a diff to update the browser DOM. No bindings necessary:
"When your component is first initialized, the render method is called, generating a lightweight representation of your view. From that representation, a string of markup is produced, and injected into the document. When your data changes, the render method is called again. In order to perform updates as efficiently as possible, we diff the return value from the previous call to render with the new one, and generate a minimal set of changes to be applied to the DOM."
Your description is similar, but it's not clear how exactly the update is made:
"In this example, Ractive.js constructs a parallel DOM representation which is aware of its dependencies on the values of user and messages.unread. When those values change, it knows exactly which parts of the real DOM need to be updated.
This kind of surgical DOM manipulation is more efficient than constantly trashing views only to re-render them, and it scales far more elegantly than manually updating elements."
How does the DOM update mechanism work?
[1]: http://facebook.github.io/react/blog/2013/06/05/why-react.ht...
I'm not intimately familiar with how React updates happen so I can't comment on how similarly we're doing things, but it looks like we have the same kind of approach.