Transponder is a opinionated library for assisting in working with front end heavy rails app.
8kb uncompressed / 2kb minified & compressed (gzip) (client side library)
Example App Using Transponder
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:
And then execute:
Or install it yourself as:
$ gem install transponder
Generate a basic Module
rails g transponder:install application
This will generate a transponder 'module' in your
application to something else, but we recommend sticking with defaults until you understand more about transponder.
Generate a Presenter
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
How is this better than Rails UJS?
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
index.js.erb you would then have something like this
$('#contacts').html("<%= j render @contacts %>");
#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
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.
So what happens once this response gets to the client
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
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()
TODO - Whats Coming
- 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