zerosum dirt(nap)

evolution through a series of accidents

zerosum dirt(nap)

JavaScript Tricks (RJS-R): Cleaning Up My Mess...

December 07, 2006 by nap · Comments

So a couple folks have pointed out that the last Rails+YUI example I posted doesn’t work in IE. Or Safari. Eek, that’s not so good.

Anyway, this post is kind of a hodgepodge documenting the process I went through to fix those issues and clean it up a bit. Maybe more of a ‘note to self’ than an actual blog entry, so not required reading by any means. Unless you’re having issues with IE and Minus MOR that is, in which case the magic word is content-type. I’m embarassed to say how much sleep I lost tracking that one down. Sigh.

class ExampleController < ApplicationController
  layout "standard", :except => :add

  def show
  end

  def add
    @response.headers['content-type'] = 'text/javascript';
    @thing = params[:thing]
  end
end

That’s our updated ExampleController. Notice that we’re setting the content type of the response in the headers now. The default content type appears to be html, instead of text/javascript. Not entirely sure why this is happening at the moment as Minus-R appears to set the content-type in it’s render method. But anyway, for whatever reason, Safari and Firefox both work fine, but IE doesn’t like it one bit. Of course, instead of warning us (or giving us an option to warn us, for that matter) it simply discards the asynchronous response. Hence, we never see an update. Nice, eh?

I also took the opportunity clean up the rest of our example a little bit. Here’s our new layout template:

<html>
<head>
  <title>YUI Tester: <%= controller.action_name %></title>
  <%= javascript_include_tag "yui/yahoo", "yui/event", "yui/dom", "yui/dragdrop", "yui/connection", "yui/container"%>
  <%= stylesheet_link_tag  'yui/container'%>

  <script language="javascript">
    YAHOO.namespace("yuitest.container");

    function init() {
      var handleCancel = function() { this.cancel(); };
      var handleSubmit = function() { this.submit(); };
      var handleFailure = function(o) { alert("failure: " + o.responseText); };
      var handleSuccess = function(o) { eval(o.responseText); };

      YAHOO.yuitest.container.myDialog = new YAHOO.widget.Dialog("myDialog", {
        width: "500px",
        modal: true, 
        visible: false,
        fixedcenter: true, 
        constraintoviewport: true, 
        draggable: true });

      var escKeyListener = new YAHOO.util.KeyListener(document, { keys : 27 }, 
        {fn:handleCancel,scope:YAHOO.yuitest.container.myDialog,correctScope:true} );

      YAHOO.util.Event.addListener( 'myDialogForm', 'submit', function(e) {
        YAHOO.util.Event.preventDefault(e);
        YAHOO.yuitest.container.myDialog.submit();
      });
        
      YAHOO.yuitest.container.myDialog.cfg.queueProperty("keylisteners", escKeyListener);
      YAHOO.yuitest.container.myDialog.cfg.queueProperty("buttons",
        [{ text:"Save", handler:handleSubmit, isDefault:true },{ text:"Cancel", handler:handleCancel } ]);

      YAHOO.yuitest.container.myDialog.callback = {
        success: handleSuccess,
        failure: handleFailure
      };

      YAHOO.yuitest.container.myDialog.render();
    }

    function addThing() {
      YAHOO.yuitest.container.myDialog.show();
    }

    YAHOO.util.Event.addListener(window, "load", init);
  </script>
</head>
<body>
  <div id="main">
    <% if flash[:notice] -%>
      <div id="notice"><%= flash[:notice] %></div>
    <% end -%>
    <%= @content_for_layout %>
  </div>
</body>
</html>

OK, a bunch of changes there. First, we’ve removed all the Prototype JS libs because we no longer need it — YUI’s connection manager can take care of this stuff for us, and since we’re not using the default behavior of RJS, there are no worries about dependence on Prototype. Next, we’ve added a couple key listeners on the popup dialog to handle enter (submit) and escape (cancel). Note that we have to use Event.preventDefault() in our enter key listener to suppress the default form submission action. Otherwise, we end up redirected to a new page that just contains our result string, and we don’t want that…

Finally, we’ve also eliminated the clumsy body of the success handler and replaced it with a single statement: eval(o.responseText). Yup, we can just evaluate the JavaScript returned from our Rails app. No need to append a new script tag to the body, yehck. Here’s the code that’s returned from our add.ejs template, as a reminder (it’s unchanged):

document.getElementById('hello_msg').innerHTML = '<%=@thing[:name]%>';

So yeah, it just replaces the inner HTML in the element named hello_msg. Easy enough. The next step here would be to figure out how to encapsulate the stuff in the layout using some sort of helper module or plugin. But that’s it for now.

(Progress on my current Rails project has been pretty slow lately, as we’re nearing completion on a big client project (a slick Java-based webstart app that’s been occupying the majority of my time for the past 6 or so months). It’s nice to finally see the light at the end of the tunnel! Hopefully once that wraps, we’ll have some significant time to pour into the RoR ideas and prototypes we’ve been playing around with…)

blog comments powered by Disqus