Liquid error: undefined method `source' for nil:NilClass Liquid error: undefined method `url' for nil:NilClass

Is this your first visit? You may want to subscribe to the feed.

Articles tagged with ajax

Observers for Ajax callbacks

In my Javascript-fu adventures over the past week or so, I’ve consistently run into the same problem. I want to do slight variations of an Ajax request on an individual basis. Here is a common method from my code: (Note: these examples use Event.addBehavior from Dan Webb’s excellent lowpro library for registering events.)

Event.addBehavior({
  // hijack any forms with the class "new" and submit them using Ajax
  'form.new:submit': function(event) {
    this.request({evalScripts: true,
      onLoading: function() { this.disable(); }.bind(this),
      onComlete: function() { this.enable(); }.bind(this)
    });
    Event.stop(event);
  }
});

But occasionally, I want a certain form to do something slightly different. For example, I have some forms that are hidden by default and I want to hide them again after they are submitted:

Event.addBehavior({
  'form.new.hidden:submit': function(event) {
    this.request({evalScripts: true,
      onLoading: function() { this.disable(); }.bind(this),
      onComlete: function() { this.enable(); }.bind(this),
      onSuccess: function() { new Effect.BlindUp(this); }.bind(this)
    });
    Event.stop(event);
  },
})

The problems with this are, 1) there is a lot of duplication, and 2) if this form has the “new” class name, it’s going to end up with 2 event handlers registered, both making an Ajax request.

So what I want is event observers on Form.request and Anchor.request for the Ajax request lifecycle, so I can call form.request.observe('loading', function() { … }), and that will be invoked any time an Ajax request is made for that form:

Event.addBehavior({
  // submit form requests using ajax
  'form.new:submit': function(event) {
    this.request({evalScripts: true});
    Event.stop(event);
  },

  // register observers to disable and enable the form
  'form.new': function() {
    this.request.observe({
      'loading': function() { this.disable(); }.bind(this),
      'complete': function() { this.enable(); }.bind(this)
    });
  },

  // register an observer to hide the form on success
  'form.new.hidden': function() {
    this.request.observe('success', function() {
      new Effect.BlindUp(this);
    }.bind(this));
  }
})

And this is where I need your help.

The first problem is that we need some type of event notification framework. I have that part solved by using a slightly modified version of Ryan Johnson’s Object.Event library, which basically allows you to add event observers to any object, and then call them by calling notify('eventname') within the object.

Next, I’ve made a modified version of Anchor.reqeust method from my post yesterday, which calls notify on each of the Ajax callbacks, and extends the request methods with the event notification methods:

if (!window.Anchor) var Anchor = new Object();

Anchor.Methods = {
  request: function(anchor, options) {
    anchor = $(anchor)
    callbacks = {}
    $A(['loading', 'complete', 'exception', 'failure', 'success']).each(function(event) {
      callbacks['on' + event.capitalize()] = function() {
        anchor.notify.apply(anchor, arguments.unshift(event));
      };
    });
    options = Object.extend(Object.extend({
      method: 'get'
    }, callbacks), options || {});
    return new Ajax.Request(anchor.readAttribute('href'), options);
  }
}
Object.Event.extend(Anchor.Methods.request);
Element.addMethods('a', Anchor.Methods);

This is where the second problem comes in. While the event notification methods are added to Anchor.Methods.request, they don’t get added to the anchor objects when the request method does. They’re getting lost in Prototype’s Element.extend method that adds the extensions to each element.

And as soon as I get that problem solved, I’m going to have another one: one instance of Anchor.request will be shared amongst all the anchor objects, so registering an observer on one will register it for every Ajax request. What I need then is for the event observer to keep track of the registered observers in each object, even though the methods are on the shared register function.

I could move the event registration methods to the anchor instead of on the request method, but that has problems of it’s own. Namely, each element already has an observe method for the browser’s events, and I can’t come up with a better name than observe. Besides, I think the methods belong on the request object; they are specific to the Ajax request.

So, does anyone have any good ideas? Is this a lame/unnecessary feature?

Code: ajax Jul 12, 2007 ● updated Jul 13, 2007 7 comments

How many introductions to Ajax do we need?

Really, how many introductions to Ajax do we really need?

I’ve been a long time subscriber to Linux Journal but have been disappointed by the content in the past year or so. After Nicholas Petreley signed on as editor, I was encouraged to see their visual changes to the magazine (finally making it look like a magazine that could be understood by someone that doesn’t know how to write a kernel module), and I was delighted when they finally discovered Ruby this past summer. When the latest issue arrived in my mailbox, I was excited to see Tim Bray on the cover.

That excitement quickly turned into the fuel for this rant when I started paging through the issue and I came across the first article, “Beginning Ajax”. Not only do we not need another introduction to Ajax, why are we still writing introductions that use the low level XmlHttpRequest object and explaining to our readers how you have to use ActiveX on IE?

I use Ajax in some form on most of the apps that I write, but beyond the examples from the first introduction to Ajax article that I read, I have NEVER used the low level JavaScript calls for doing Ajax. Prototype eliminates the need to use the low level handling of Ajax:

new Ajax.Updater('div_to_update', '/dosomething', {
    asynchronous:true,
    onComplete: doSomething(request),
    onLoading: showSpinner();
    }
);

That’s a heck of a lot cleaner than the 25 line example in the Linux Journal article. xhr.readyState == 4, who cares?

What’s even more interesting, using Rails, I rarely even write code using Prototype. More commonly, I write:

<%= link_to_remote('edit',
  :update => 'div_to_update',
  :url => {:action => 'edit', :id => object})
%>

90% of the time, this is more than sufficient, and for that other 10%, I am intimately familiar with Prototype and can accomplish the same thing in a fraction of the time that it takes to write the raw JavaScript.

So, again, I ask, why are we still writing introduction to Ajax articles that use low level JavaScript? I agree that it is important to understand how Ajax works, but there are plenty of articles that do that. No one (in their right mind) would actually use this code in their production apps.

Code: ajax Oct 09, 2006 ● updated Oct 24, 2006 0 comments

Using RedBox for modal dialogs

When I first saw Lightbox, my first thought (ok, second thought; my first was “This is awesome!”) was: “This would be a great technique for confirmation pages!” I never got around to using lightbox due to it’s emphasis on images, but eventually other solutions started showing up, like GreyBox and ThickBox, that allowed you to use other content.

A couple weeks ago, I came across Redbox, a rails plugin based on ThickBox. It is a great little plugin that simply consists of a few Rails helpers and some Javascript.

As I experimented with it, I decided that I really like the feel of using modal dialog boxes for confirmation messages. It is more consistent with the desktop experience, but not as constrained as popups on the desktop.

Redbox Delete

As I was using Redbox, I developed a pattern that works really well. Instead of limiting :destroy requests to POST, I modified them to return a delete confirmation page when called with a GET, then perform the modification when called with a POST.

def destroy
  @payment = Payment.find(params[:id])
  if request.get?
    respond_to do |wants|
      wants.html
      wants.js { render :layout => 'modal' }
    end
  else
    @payment.destroy
    flash[:notice] = "Payment for $#{@payment.amount} has been deleted."
    redirect_to :action => 'index'
  end
end

This creates a little extra code, but it also makes the modal windows degradable. If the client doesn’t support JavaScript, or it is disabled, then when they click a destroy link, they will be taken to a separate confirmation page. I’ve also created a ‘modal’ layout that can have some formating, or maybe a close link.

The code to create the redbox link looks like:

<%= link_to_remote_redbox 'delete',
    {:url => {:action => 'destroy', :id => payment}, :method => :get},
    :href => url_for(:action => 'destroy', :id => payment) %>

link_to_remote_redbox takes the same parameters as link_to_remote, minus the :update parameter that tells where to put the new content. Note the :method =&gt; :get. By default, Ajax requests use POST, but I don’t want to POST to the destroy action because that will perform the modification. I’m also setting :href, which makes this link degradable.

As I used Redbox more and more for confirmation pages, it occurred to me that this method would also work really well for simple forms. So, with barely any modification—adding respond_to in the action and replacing link_to with link_to_remote_redbox—I was able to change these small forms into modal dialogs. Currently, the forms submit a regular request, but I could refactor it a little more and use Ajax to submit the forms and update the page.

Redbox Enroll

Overall, I really like the feel of these modal dialogs. They make the app feel snappier and less daunting. Users don’t have to jump all over the app for simple modifications. They spend the majority of their time on one page, the most important page.

Code: ajax Sep 21, 2006 ● updated Jun 28, 2007 11 comments

Subscribe

Browse by Tag