ActiveScaffold Unobtrusive Javascript and TinyMCE

2 Comments

My last post covered the topic how ActiveScaffold and Unobtrusive Javascript may fit together. This time I would like to give you an example.
It s Activescaffold’s TinyMCE Bridge. It s a perfect example, cause it s a pure javascript feature.
Let’s take a look at the bridge file using obtrusive javascript:

Loading tinymce editor

Obtrusive Javascript

def active_scaffold_input_text_editor(column, options)
    options[:class] = "#{options[:class]} mceEditor #{column.options[:class]}".strip
    html = []
    html << send(override_input(:textarea), column, options)
    html << javascript_tag("tinyMCE.execCommand('mceAddControl', false, '#{options[:id]}');") if request.xhr?
    html.join "\n"
end

We use an activescaffold input override to add javascript code to our textareas.

Unobtrusive Javascript

$('form.as_form').live('as:form_loaded', function(event) {
  var as_form = $(this).closest("form");
  as_form.find('textarea.as_mceEditor').each(function(index, elem) {
    tinyMCE.execCommand('mceAddControl', false, $(elem).attr('id'));
  });
  return true;
});

A listener to activescaffold form_load event, finding all textareas and adding tinyMCE control.

Submitting tinymce editor

Obtrusive Javascript

def onsubmit
    if ActiveScaffold.js_framework == :jquery
       submit_js = 'tinyMCE.triggerSave();$(\'textarea.mceEditor\').each(function(index, elem) { tinyMCE.execCommand(\'mceRemoveControl\', false, $(elem).attr(\'id\')); });' if using_tiny_mce?
    else
       submit_js = 'tinyMCE.triggerSave();this.select(\'textarea.mceEditor\').each(function(elem) { tinyMCE.execCommand(\'mceRemoveControl\', false, elem.id); });' if using_tiny_mce?
    end
    [super, submit_js].compact.join ';'
end

We use form submit method override to add javascript code, which will call tinyMCE.triggerSave method.

Unobtrusive Javascript

$('form.as_form').live('as:form_submit', function(event) {
  var as_form = $(this).closest("form");
  if (as_form.has('textarea.as_mceEditor').length > 0) {
    tinyMCE.triggerSave();
  }
  return true;
});

A listener to activescaffold form_submit event, finding all textareas using tinyMCE and calling triggerSave.

Removing tinymce editor

Obtrusive Javascript

def active_scaffold_includes(*args)
        if ActiveScaffold.js_framework == :jquery
          tiny_mce_js = javascript_tag(%|
var action_link_close = ActiveScaffold.ActionLink.Abstract.prototype.close;
ActiveScaffold.ActionLink.Abstract.prototype.close = function() {
$(this.adapter).find('textarea.mceEditor').each(function(index, elem) {
tinyMCE.execCommand('mceRemoveControl', false, $(elem).attr('id'));
});
action_link_close.apply(this);
};
|) if using_tiny_mce?
        else
        ....
        end
        super(*args) + (include_tiny_mce_if_needed || '') + (tiny_mce_js || '')
      end

Ok, we add a javascript code snippet to activescaffold includes in case using_tiny_mce? returns true. Javascript code will link into action link.close action management and will remove our tiny_mce_editor. To be honest I do not like that solution at all. 🙂

Unobtrusive Version

$('form.as_form').live('as:form_unloaded', function(event) {
  var as_form = $(this).closest("form");
  as_form.find('textarea.as_mceEditor').each(function(index, elem) {
    tinyMCE.execCommand('mceRemoveControl', false, $(elem).attr('id'));
  });
  return true;
});

A listener to activescaffold form_unload event, finding all textareas and removing tinyMCE control.

Summary

Did we achieve our goals?
We ve separated javascript code from ruby code. which improves code quality and maintainability. You do not have to be an activescaffold expert anymore to add javascript functionality to forms, form_columns, list_columns. You just need to know javascript.

Hope that helps.

ActiveScaffold and Unobtrusive Javascript

2 Comments

Rails 3.0 started using unobtrusive javascript. However, what does that actually mean for activescaffold? Well, basically it means that there should nt be any javascript code in our partials. Looks easy at the first glance, however, if you start actually to implement it..
Big advances of unobtrusive javascript are:

  • application works without javascript
  • html code seperated from javascript code
  • html id attribute is nt needed anymore for many operations

I ve mentioned at the beginning that going unobtrusive is nt that easy as it seems. The big issues you have are:

  • How to cleanly separate javascript code?
  • How may application work if javascript is disabled?
  • It s easy to manage click events, unobtrusively, but how to manage load events for ajax calls?

In this post I will focus on the last bullet point. It took a while until I was able to manage that issue.
Unobtrusive javascript needs a trigger event to add the javascript functionality. For example when you click a button. However, if you would like to add javascript functionality on load you are in kind of trouble for ajax applications, because load events of browsers do only fire if the whole page is loaded and not if snippets of html code are changed or added during an ajax call.

I ve solved that issue at least for activescaffold as follows.

ActiveScaffold will trigger the following events in case of page load and ajax load:

  • as:list_row_loaded
  • as:form_loaded
  • as:form_element_loaded

ActiveScaffold will trigger the following events in case of page unload and ajax unload:

  • as:list_row_unloaded
  • as:form_unloaded
  • as:form_element_unloaded

A simple use case would be the following: list columns may be configured for inplace editing, which is a pure javascript feature including the need to add it to the specified columns during as:list_row_load event:

$('tr.record').live('as:list_row_loaded', function(event) {
  $(this).closest("tr").find('td > span.in_place_editor_field').each(function(index) {
    ActiveScaffold.create_inplace_editor($(this));
  });
  return true;
});

In my next post I will show you how I ve migrated the tiny_mce bridge to unobtrusive javascript.

Hope you will benefit from this new javascript events, cause it will make the step to unobtrusive javascript a lot easier using activescaffold.