Boban Jovanoski

Standalone Widgets BobiYo Jan 14

Comments are closed

Tags: widget   apotomo   javascript   jsonp   rails   ruby  

Introduction

For a not so much recent project I needed to create a standalone widget that can be displayed on any page with less effort. You can see the result here (don't forget to view source). And if it seems interesting to you, keep on reading :) As far as I know this can be done with iframe or javascript(jsonp), if you know any other way pls let me know.

alt text

The Idea is to create a standalone widget for your application that can be used easily on any other site. Think of it as minifying your app where cool stuff are happening :) So I did some research and I've decided to use pure javascript and Apotomo, because:

  • Fun

  • Apotomo - which separates the stuff that you need and you get a much cleaner code, hence the speed. Apotomo comes with many othercool stuff, I recommend that You check out the tutorials http://apotomo.de. I'll explain later why I've decided to use Apotomo :)

  • Javascript - I got a bit nostalgic for writing pure js....and using a framework may have conflicts with the clients js, and widgets need to be independent. The js that is used is inspired from the twitter widget.


Implementation

Create new rails 3.0.3 app - there is an issue with json serialization in previous rails versions, the to_json method does not return the required structure.

  1. Add haml+compass in your Gemfile :)
  2. Create new apotomo widget with

    rails generate apotomo:widget apotomo_widget display_form --haml

  3. Create/Use the models from your application so you can get the data that you want to show in the widget. I've created a simple PostLink model that has title and url. There is also a rake task that gets the titles and urls from apotomo's feed and stores them in db.

  4. Set up the widget display action - see source in haml.

After the display action for the widget is defined we can add some styling, but we also want the client to be able to set a theme for our widget. At first, I've defined different css for every theme that is used when the widget is initialized, but I've depricated that code and I've decided to put all the styling for different themes in one file. My css skills are .... :) anyway you can check the styling for some themes that I've set here


It's js time

Ok, we've set the display action for the widget, now it's time to create a widgets controller and write some js, so:

  • Create a widgets controller that responds to the index action with js
  • set: match "widgets", :to => "widgets#index" in routes.rb
  • Create index.js.erb in app/views/widgets directory.

class WidgetsController < ApplicationController
  include Apotomo::Rails::ControllerMethods
  has_widgets do |root|
     root << widget(:apotomo_widget, 'apotomo_widget', :display)
  end

  def index
     respond_to do |format|
      format.js
     end
   end
end

You can check the end result here. At first I've defined some helper methods that are used through the code. JsonP is used for getting data from the server..css and data that we want to display. Every widget has it's own istance which is known by the widget_number, and it's own refresh_interval. For this example I've added options for refresh_interval and theme to be set by the client.

After the widget is initialized the autoRefresh() function is called which updates the widget data. So how do we get the data from the server?

This is the why apotomo part kicks in....despite the number of widgets that we can create using Apotomo, all apotomos widgets will respond with the action that we've requested: so for example if we have lets say 3 apotomo widgets (first_widget, second_widget, third_widget) they'll all respond to /widgets/render_event_response action where as arguments we specify on which widget(source), which action(type) we want to call.

In this example: we request for: &source=apotomo_widget&type=update This will call the update action in apotomo_widget...which we use to execute our callback.

def update
  text = params[:callback] + '(' + PostLink.all.to_json + ')'
  render :text => text, :content_type => 'application/json'
end

Unfortunately Apotomo does not accept the :callback parameter :( ...usually the rails way we simply need to call:

render :json => PostLink.all, :callback => params[:callback]

The callback parameter in this example looks like this APOTOMO.Widget.updatePosts_1 for the first widget instance, APOTOMO.Widget.updatePosts_2 for the second widget instance and so on. So this methods are executed in javascript which updates the widget data for the widget that requested


At the end

Now all that we need to do is to include our js(path to our widgets/index action) on any page and create the widget/widgets instances. This can be done on any web page, all that needs to be entered is:

<script type="text/javascript" src="http://widgets.jovanoski.net/widgets"></script>
And where we want the widgets to show we simply put
<script type="text/javascript">new APOTOMO.Widget({refresh_interval: 800000, theme: 'default' });</script>
<script type="text/javascript">new APOTOMO.Widget({refresh_interval: 800000, theme: 'blue' });</script>