I've been using knockout heavily for a project recently and have come to really like it. This is a small to mid size website, about 6 engineer months of effort and somewhere in the mid 10klocs of custom javascript. Some strengths of knockout:
- 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 got a library called Falcon.js (https://github.com/stoodder/falconjs) that essentially provides a Model, Collection, View framework on top of Knockout that I've been using for over a year now on internal and client projects. It works really well for single page and mobile web hybrid apps, it's completely unit tested, and I'm currently in the works of getting the documentation finished up. It's only dependency is jQuery and it comes bundled with Knockout 2.2.1 built in. Feel free to take a look at what I have so far, I'd love your feedback. Again, I apologize for the incomplete state of documentation at the moment but I promise everything'll be wrapped up sooner than later!
I'm currently using this style in a RequireJS/Angular project. Everything works great, but it's pretty ugly. We should strive to not write ugly shit, but good clean expressive code. This is so non-standard it hurts. There are plenty of other ways the Angular team could have gone; CommonJS or AMD would have been better choices IMO.
Or if you're already in the process of minification you can also fix these injections automatically, with a simple regex or https://github.com/btford/ngmin.
personally, I already have a massive library of AMD modules, using a second module system, even if it adds a feature is confusing for people who join the project and makes for ugly code.
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.
I actually find that website almost entirely useless. A to-do list means nothing and demonstrates nothing useful. It has no associations between models and nothing particularly complex. I have tried so many of these frameworks and found crippling flaws in all of them, but on the surface they all look peachy when compared via a to-do app.
It's pretty much only useful if you want to write a to-do app.
Declarative data binding is intrinsically limited. If you want something that supports more complex updates, you need to start looking into things like incremental algorithms, self-adjusting computations, damage and repair, logged reversible effects, iterative processing methods, and so on.
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.
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/
This is fascinating: I've been writing a very (and I mean very) similar library that also uses the concept of a Parallel DOM (dependents, views updating on a 'data' property, nodes named by standard ids, all child nodes with IDs scanned for and bound to a property), I had just decided to create the concept of 'Keypaths' as my events were clashing on ids.
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.
Thanks. Performance-wise, I haven't tested Ractive and React against each other, but that would be a test worth doing. Ractive is definitely built with performance in mind - it uses dependency tracking to ensure that the DOM is only updated when necessary.
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.
Its naming is a bit conflicting to component/reactive. The name "reactive" is so presumable that other people will choose in the future.
https://github.com/component/reactive
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?
Data-binding is the most obvious feature of Ractive but there are others - easier event handling, transitions, animations, JavaScript-ish expressions within templates, server-side parsing and HTML rendering, and so on.
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.
Nice - looks very simple and lightweight! I especially like the effort put into the interactive tutorial documentation that these frameworks have. Angular and knockout both had stand out docs too!
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"?
Library author here - thanks! The interactive tutorials borrow very heavily from Knockout (in fact Knockout was a big source of inspiration all round).
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!)
Can someone comment on where to find performance information on this framework? AngularJS has some very smart ideas that allow it to be magical and performant. This looks very cool, and I would like to make sure it is not just polling for changes. I'm suspicious because there is no $apply required. :)
Definitely no polling! Model changes are done explicitly with ractive.set() (or ractive.animate(), or array mutator methods). It uses a dependency tracking mechanism to update the view with the fewest calculations and DOM touches necessary.
I have a question, since this has been bugging me with other js MV* frameworks lately... Is the set() method necessary for updating single attribute? All browsers have supported getter/setter APIs (__defineGetter__ and Object.defineProperty), why not take advantage of them? Being able to say this would be cool:
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 :)
Many of these frameworks want to support IE 8, which is the default browser of windows 7. IE 8 only supports Object.defineProperty with DOM objects[0], which can be infuriating.
No one should use __defineGetter__ and __defineSetter__ because they are non-standard and deprecated[1].
It's definitely something I've thought about. There are a few reasons I still prefer ractive.set( keypath, value ), which I'll try and articulate:
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!
If getters/setters aren't available, I still vastly prefer plain functions (e.g. knockout) to the stringized setter.
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...
I like a lot of this, but one part I'm not so sure of is that, if I follow the docs, all get/set operations need to be from the root of the model. They take a "keypath" that starts at root, like "obj.get('subObj1.subArray[3].prop')". It seems to me like that would make it difficult to decompose a large application into component pieces. Your components, to do any get/set operations, would need to know their path from root. And what if that path changes, like an array that's modified? Seems like it could become a considerable barrier in mid-to-large applications.
You're right, all keypaths start from the root. One approach for large apps is to split the UI up into several Ractive instances (which might all be bound to a single global model, but 'start' at different keypaths).
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/
Nice little library. I think i will have a close look and implement it into apiDoc (http://apidocjs.com) Templates - for now i use crude jQuery events (quick & dirty coded).
With Ractive i think i can make the code cleaner and easier to modify.
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?
Thanks! Yes, a lot of the feedback I've had through various channels has involved 'how does this compare to x', and I'm pleased to see that a lot of people have picked up on the similarities to Knockout. On the project homepage I specifically refer to Angular because my sense is that it will be more familiar to more people, and despite the many architectural differences they're attacking the same problem space (though Angular deals with xhr/routing etc whereas Ractive is focused on DOM stuff).
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.
Can anyone comment on using Ractive as a replacement to Backbone.View? I'd think that making Ractive the view layer in a Backbone app would go a long way in augmenting a lot of the things that Backbone refuses to do in views (binding, smart DOM management/manipulation, actual templating support)
I really like this library.. it's been a while I've been looking for something similar. Angular was a bit "too big" for some of my projects, and backbone was just too verbose. Something like this will let me keep the code clean while still not getting involved in a big framework.
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.
[+] [-] andrewvc|12 years ago|reply
[+] [-] curveship|12 years ago|reply
- 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.
[+] [-] stoodder|12 years ago|reply
[+] [-] scott_karana|12 years ago|reply
[+] [-] dustingetz|12 years ago|reply
[+] [-] scrabble|12 years ago|reply
[+] [-] ww520|12 years ago|reply
[+] [-] msutherl|12 years ago|reply
[+] [-] skore|12 years ago|reply
> 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:
You get used to it in seconds and then it's not really an issue anymore.[+] [-] tbranyen|12 years ago|reply
[+] [-] rolandboon|12 years ago|reply
[+] [-] nailer|12 years ago|reply
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.
[+] [-] codereflection|12 years ago|reply
[+] [-] robotmay|12 years ago|reply
It's pretty much only useful if you want to write a to-do app.
[+] [-] nathansobo|12 years ago|reply
[+] [-] seanmcdirmid|12 years ago|reply
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.
[+] [-] rich_harris|12 years ago|reply
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/
[+] [-] sambeau|12 years ago|reply
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.
[+] [-] jonahx|12 years ago|reply
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?
[+] [-] rich_harris|12 years ago|reply
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.
[+] [-] smagch|12 years ago|reply
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?
[+] [-] rubiquity|12 years ago|reply
[+] [-] rich_harris|12 years ago|reply
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.
[+] [-] boothead|12 years ago|reply
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"?
[+] [-] rich_harris|12 years ago|reply
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!)
[+] [-] wiremine|12 years ago|reply
[+] [-] xrd|12 years ago|reply
[+] [-] rich_harris|12 years ago|reply
[+] [-] mannix|12 years ago|reply
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 :)
[+] [-] mnemonik|12 years ago|reply
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.
[+] [-] rich_harris|12 years ago|reply
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!
[+] [-] emn13|12 years ago|reply
I mean, why would
be better than 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...
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?
[+] [-] plorkyeran|12 years ago|reply
[+] [-] nailer|12 years ago|reply
[+] [-] Dekku|12 years ago|reply
[+] [-] rich_harris|12 years ago|reply
[+] [-] curveship|12 years ago|reply
[+] [-] rich_harris|12 years ago|reply
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/
[+] [-] scottmessinger|12 years ago|reply
[+] [-] apidoc|12 years ago|reply
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).
[+] [-] boubiyeah|12 years ago|reply
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
[+] [-] rich_harris|12 years ago|reply
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...
[+] [-] Narretz|12 years ago|reply
[+] [-] nkerkin|12 years ago|reply
[+] [-] modarts|12 years ago|reply
[+] [-] d0m|12 years ago|reply
[+] [-] usethis|12 years ago|reply
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?
[+] [-] rich_harris|12 years ago|reply
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.