Obviously this mini-tutorial was inspired by recent events on the list.
# How to build a Hobo-jQuery plugin from a jQuery plugin
It isn't difficult to use a jQuery widget from within a Hobo
application using standard HTML and Javascript techniques. For our
example, we'll use https://github.com/recurser/jquery-simple-color.
Here's an example using just HTML-style DRYML and Javascript:
<edit-page>
<custom-scripts:>
<javascript name="jquery.simple-color"/>
<script type="text/javascript">
jQuery(document).ready(function($) {
$('.simple_color').simpleColor();
});
</script>
</custom-scripts:>
<form:>
<field-list:>
<color-view:>
<input class='simple_color'/>
</color-view:>
</field-list:>
</form:>
</edit-page>
That's fine, and if you're only using it once and in one place, it's
OK to stop there. However, we can make it better and turn this into a
Hobo plugin so that the color picker is used automatically without any
intervention required. Even better, other users can take advantage,
too.
Let's start with the bare minimum hobo-jquery plugin. It consists of
two parts. Here's the tag definition:
<def tag="simple-color">
<input data-rapid="#{data_rapid('simple-color')}" merge/>
</def>
And here's the javascript:
$.fn.hjq_simple_color = function(annotations) {
this.simpleColor(annotations);
};
There's code in hjq.js that notices the data-rapid attribute, and then
tries to call a jQuery plugin with the same name with `hjq_`
prepended.
The next step is to allow users to customize the color picker. Let's
add support for the displayColorCode option:
<def tag="simple-color" attrs="displayColorCode" >
<input data-rapid="#{data_rapid('simple-color',
:displayColorCode => displayColorCode)}" merge/>
</def>
However, simple-color has 14 available options, so it could get
cumbersome to support them all that way. There's an easier way:
<def tag="simple-color">
<% options, attrs = attributes.partition_hash(['defaultColor',
'border', 'cellWidth', 'cellHeight', 'cellMargin', 'boxWidth',
'boxHeight', 'columns', 'insert', 'buttonClass', 'colors',
'displayColorCode', 'colorCodeAlign', 'colorCodeColor']) %>
<input data-rapid="#{data_rapid('simple-color', options)}"
merge-attrs="&attrs"/>
</def>
That's good enough for simple-color, because all of its options are
data types. However, many jQuery plugins support passing javascript
functions for option values. Obviously that's a little bit more work.
Here's an example of that:
<def tag="my-plugin">
<% options, attrs = attributes.partition_hash(['foo', 'bar'])
events, html_attrs = attrs.partition_hash(['myMethod']) %>
<div data-rapid="#{data_rapid('my-plugin', :options => options,
:events => events)}" merge-attrs="&html_attrs"/>
</def>
$.fn.hjq_my_plugin = function(annotations) {
this.myPlugin(this.hjq('getOptions', annotations)));
};
We now have a working simple-color tag. However, it still has to be
called explicitly every time we need it. We can make it even more
awesome by taking advantage of Hobo's rich types.
Create app/rich_types/color.rb:
class Color < String
COLUMN_TYPE = :string
HoboFields.register_type(:color, self)
end
Now in your fields definition, use your new rich type rather than `:string`:
fields do
color Color, :default => "#000000"
end
Now we can define an input for our new type:
<def tag="input" for="Color">
<simple-color merge/>
</def>
But that blows up into an infinite loop. The input tag references the
simple-color tag which references the input tag which references the
simple-color tag which...
The solution here is to bypass the input tag by using Rails' text_field_tag:
<def tag="input" for="Color" attrs="name">
<%=
options, attrs = attributes.partition_hash(['defaultColor',
'border', 'cellWidth', 'cellHeight', 'cellMargin', 'boxWidth',
'boxHeight', 'columns', 'insert', 'buttonClass', 'colors',
'displayColorCode', 'colorCodeAlign', 'colorCodeColor'])
add_data_rapid!(attrs, 'simple-color', options)
text_field_tag(name, this, deunderscore_attributes(attrs))
%>
</def>
For completion, let's define a view for our new rich type:
<def tag="view" for="Color">
<span style="background-color: #{html_escape(this)};"><%= this %></span>
</def>
Now we need to just package things up and distribute them to the
world. But that's TBD because Rails 3.1 and Hobo 1.4 make that easier
than in the past.