twine
Twine is a minimalistic two-way binding system.
Features:
- It's just JS - no new syntax to learn for bindings
- Small and easy to understand codebase
Non-features:
- No creation of new nodes (e.g. no iteration)
- No special declaration of bindable data (i.e. bind to any JS data)
Installation
Twine is available on bower via bower install twine
if that is your preference.
Twine comes as dist/twine.js
and dist/twine.min.js
in this repo and in the bower package.
Twine is also available as a gem. In your Gemfile, add gem 'twine-rails'
and include it in your application.js
manifest via //= require twine
Usage
Twine can be initialized simply with the following:
<script type="text/javascript">
var context = {};
$(function() {
Twine.reset(context).bind().refresh();
});
</script>
Above, context
will be considered the context root, and this will work until you navigate to a new page. On a simple app, this may be all you need to do.
With rails/turbolinks
Turbolinks requires a bit more consideration, as the executing JS context will remain the same -- you have the same window
object throughout operation. When the page changes and new nodes come in, they need to be re-bound manually. Twine leaves this to you, rather than attempting to guess.
Here's a sample snippet that you might use:
context = {}
document.addEventListener 'page:change', ->
Twine.reset(context).bind().refresh()
return
If you're using the jquery.turbolinks gem, then you can use:
context = {}
$ ->
Twine.reset(context).bind().refresh()
return
With Shopify/turbograft
With TurboGraft, you may have cases where you want to keep parts of the page around, and thus, their bindings should continue to live.
The following snippet may help:
context = {}
reset = (nodes) ->
if nodes
Twine.bind(node) for node in nodes
else
Twine.reset(context).bind()
Twine.refreshImmediately()
return
document.addEventListener 'DOMContentLoaded', -> reset()
document.addEventListener 'page:load', (event) ->
reset(event.data)
return
document.addEventListener 'page:before-partial-replace', (event) ->
nodes = event.data
Twine.unbind(node) for node in nodes
return
$(document).ajaxComplete ->
Twine.refresh()
Twine.afterBound
Registers a function to be called when the currently binding node and its children have finished binding.
Example:
class Foo
constructor: ->
Twine.afterBound ->
console.log("done")
# other methods needed in the context
# ...
<div context='bar' define='{bar: new Foo}'></div>
Twine.shouldDiscardEvent
Lets you register a function to ignore certain events in order to improve performance. If the function you set returns true, then the event processing chain will be halted
Example:
Twine.shouldDiscardEvent.click = (event) ->
$target = $(event.target)
$target.hasClass('disabled')
Dev Console
To get the current context in the dev console, inspect an element then type:
Twine.context($0)
Where context expects a node and $0
is shorthand for the current node in the dev console.
Contributing
- Clone the repo:
git clone [email protected]:Shopify/twine
cd twine
npm install
npm install -g testem coffee-script
- Run the tests using
testem
, ortestem ci
- Submit a PR
Releasing
- Update version number in
package.json
,bower.json
, andlib/twine-rails/version.rb
- Run
bundle install
to updateGemfile.lock
- Run make .all && make .uglify to update JS
- Push the new tag to GitHub and the new version to rubygems with
bundle exec rake release