Transponder is a opinionated library for assisting in working with front end heavy rails app.
8kb uncompressed / 2kb minified & compressed (gzip) (client side library)
Try opening the Kontax app in multiple browser and update something, and see how transponder's abstraction make this sort of thing very clean and easy for a rails app.
Add this line to your application's Gemfile:
gem 'transponder'
And then execute:
$ bundle
Or install it yourself as:
$ gem install transponder
A transponder module provides some basic structure for your javascript code
rails g transponder:install application
This will generate a transponder 'module' in your app/assets/javascripts
folder by the name of 'application', you can change the application
to something else, but we recommend sticking with defaults until you understand more about transponder.
Presenters is perhaps one of the most important thing about Transponder, it allows you to use your server side templates in your client side code, cleanly and allows better reuseability of code.
rails g transponder:presenter contacts
Running this command will generate a presenter in your Transponder module application/presenters
with the name contacts_presenter.coffee
Typically with Rails UJS you would create a view with something like this
Lets say you have a basic contacts_controller.rb
class ContactsController < ApplicationController
respond_to :json, :html, :js
def index
@contacts = Contact.all
respond_with @contacts
end
end
In your index.js.erb
you would then have something like this
$('#contacts').html("<%= j render @contacts %>");
This Javascript code is evaled in the browser and content of the node with the id #contacts
gets replaced with server side template that came from <%= j render @contacts %>
This is fine however it has a few problems. First of all if you use coffeescript it has to be compiled in real time as its responding which adds to your response time. Secondly if you want to do more complex things in your response things can get very messy. Code reuse isn't that great either.
With Transponder you have a consistent way of working with your server side template. Lets take a look at the difference
In your index.js.erb
transponder version would look something like this.
["#contacts", "<%= xms_event %>", "<%= j render @contacts %>"]
Your server side response code using transponder will mostly likely look something like this. There is consistency to it. The first element is the DOM node you want to manipulate, the second element is what will allow the client side Transponder code to know which Presenter is responsible for this response and lastly we have the server side generated content.
Well in our presenter we would do something like this
class Application.Presenters.ContactsPresenter extends Transponder.Presenter
presenterName: 'contacts'
module: 'application'
index: ->
$(@element).html(@response)
# ... do more stuff ...
The first 3 lines of code are generated by the presenter generator you were just using before the only line of code you should pay attention to here is the last 2. Basically the @element
is the dom element you specified in index.js.erb
and the @response
is the content that was rendered by the server.
In the presenter you can do pretty much anything you want to your response before it gets output to the DOM. This gives a nice structure and consistency to the whole pattern. It allows you to mix server side templates with full client side programmability.
Testing is also much easier as now you've shifted the responsibility of the client side behavior to the client. We have more documentation coming on how to test your presenters.
Services are meant to be an easy way to manage functionality of a widget on a page. What does this mean?
Generally we will have elements on the page that do more than just display information, they have to listen to some even like a mouse click or a tap and then act on that. They may have 1 simple functionality or multiple functions Services are a way to manage that.
Services are designed to be idempotent. You can run a service on a page repeatedly and it will not apply to the widgets that already have the same service applied. This can be very useful when working with pages that use pjax / turbolinks / single page apps etc...
To generate a service simply type
rails g transponder:service contacts_search
In our initializers/manifest.coffee
file we have something like this
Application.services_manifest = ->
$('body').trigger 'application:services:contacts_search'
Basically we want to trigger this service on the page. Since we're using 'body' as the element the service will run on every page. If we apply a class to the html <body>
tag to something like this in our application layout file <body class="<%= controller_name %> <%= action_name %>"
we can trigger specific services on specific pages.
For example if we're on the controller index page and we want to trigger the contacts search only when we're on that page we can do something like this
Application.services_manifest = ->
$('body.contacts.index').trigger 'application:services:contacts_search'
This gives us very fine grained control as to which services should run on which pages.
We have this in our erb some where on our page
<%= form_tag contacts_path, class: 'navbar-form navbar-left contacts_search', remote: true do %>
<div class='form-group'>
<%= text_field_tag :query, nil,class: 'form-control', placeholder: "Search for someone", id: 'search-field' %>
</div>
<% end %>
which renders down to
<form accept-charset="UTF-8" action="/contacts" class="navbar-form navbar-left contacts_search contacts_search_active" data-remote="true" method="post"><div style="margin:0;padding:0;display:inline"><input name="utf8" type="hidden" value="✓"></div>
<div class="form-group">
<input class="form-control" id="search-field" name="query" placeholder="Search for someone" type="text">
</div>
</form>
The service will detect the element on the page with the matching class to the serviceName:
and apply the behavior to those elements.
class Application.Services.ContactsSearch extends Transponder.Service
serviceName: 'contacts_search'
module: 'application'
init: ->
@element.on 'keyup', "#search-field", @submitSearch
search: _.debounce ( (e) ->
field = @element.find('#search-field')
$.ajax
url: @element.prop('action')
dataType: 'script'
data:
query: field.val()
), 600
submitSearch: (field) =>
@search(field)
serve: ->
@init()
- Add Documentation
- Video Screencasts
- Add more features to Kontax
- More documentation on Services
- Fork it
- Create your feature branch (
git checkout -b my-new-feature
) - Commit your changes (
git commit -am 'Add some feature'
) - Push to the branch (
git push origin my-new-feature
) - Create new Pull Request
Gem developed by Zack Siri of Artellectual