Hacker for Hire

Rails With Calendar Date Select

Wyatt • • Technology

Today, I spent 2-hours trying to figure out the correct way to implement an AJAX based call with the wonderful [Calendar Date Select][1] plugin. Now I’m sure it’s totally my fault for being a Rails noob, but I couldn’t come up with squat on Google for how to use this plugin with an server side AJAX call. So here’s my write up on how to do it using remote_function.

According to the documentation, if I want to use an AJAX style call, I should use the onchange attribute, which executes some JavaScript for you. That’s cool, I can do that. But that’s not really that helpful for trying to update the server unless you use the Ajax.Updater() like [these guys did][2]. Alrighty, so now I’ve got to figure out my controller URL, build that in and then pass in some parameters to my JavaScript I’m writing inside my Rails code.

<font size=3>WRONG!</font>

The first time I saw that I had to be writing JavaScript, I should have know I was trolling the wrong websites. I started to think and remembered that I used an AJAX call somewhere else in my code to sort a list. No dice. The implements sortable hides everything from me and again wasn’t even close to what I was looking for. More searching and I finally … FINALLY … found some bastion of sanity that said you are supposed to use [remote_function][3] to make AJAX based calls. I knew this was exactly what I was looking for when I saw the name and when I read that you don’t have to write JavaScript to make it work … it writes it for you.

Only there was a snag. [Calendar Date Select][1]‘s onchange gives you the function $F(this) to retrieve the date. But it’s a JavaScript function, not a Rails function … so you have to some how get the JavaScript variable to be a Rails variable … going the other way is cake:

"/* javascript in rails */ var t = #{@myObject.date};"

So now, I started search for how to make a JavaScript variable part of a Rail’s POST request. It’s the :with parameter. So now my call looks like:

<span class="emphasize">Scheduled for <span id=‘current_show_date’>< %= date_no_time_format(@show.date) %></span>
        < %= calendar_date_select_tag "show[date]",
          date_no_time_format(@show.date).to_s,
          :time => false,
          :popup => "force", :hidden => true,
          :onchange => remote_function(:update => "current_show_date",
               :url => {:action => "update_show_date", :id => @show.id},
               :with => "’date=’+$F(this)" )
        %>
      </span>

After 2-hours of trying to find this, I find the “F(this)” so true :-). Well, now another problem … the date is coming across as “Monday, January 1 2009.” Which I’m sure I can do all sorts of fun conversions on inside of the ruby code … but I don’t want to. It’s going in the database as a DateTime, it should be passed as an ISO date. But of course, there is no ISO date formate for [Calendar Date Select][1]. Fortunately, you can extend it and someone took the time to write an ISO formate for it … but they left it all busted to hell in the comments section. So I fixed it. Then I fixed all the [jslint][4] problems with it so it would actually work in IE. You can download it [here][5], or copy it from here:

Date.prototype.toFormattedString = function(include_time) {
        var hour;
    var str = this.getFullYear() + "-" + Date.padded2(this.getMonth() + 1) + "-" +Date.padded2(this.getDate());
    if (include_time) {
        hour = this.getHours();
        str += " " + this.getHours() + ":" + this.getPaddedMinutes();
    }
    return str;
};

Date.parseFormattedString = function (string) {

    var regexp = "([0-9]{4})(-([0-9]{2})(-([0-9]{2})" +
        "( ([0-9]{1,2}):([0-9]{2})?" +
        "?)?)?)?";

    var d = string.match(new RegExp(regexp, "i"));
    if (d === null) {
        return Date.parse(string); // at least give javascript a crack at it.
    }
    var offset = ;
    var date = new Date(d[1], , 1);
    if (d[3]) {
        date.setMonth(d[3] 1);
    }
    if (d[5]) {
        date.setDate(d[5]);
    }
    if (d[7]) {
        date.setHours(d[7]);
    }
    if (d[8]) {
        date.setMinutes(d[8]);
    }
    if (d[]) {
        date.setSeconds(d[]);
    }
    if (d[2]) {
        date.setMilliseconds(Number("0." + d[2]));
    }
    if (d[4]) {
        offset = (Number(d[6])) + Number(d[8]);
        offset = ((d[5] == ‘-‘) ? 1 : 1);
    }
    return date;
};

Drop in your as [project]/public/javascript/calendar_date_select/format_iso_date.js. Then add the following lines to your environment.rb:

# Custom Date/Time formating for CalendarDateSelect
  CalendarDateSelect::FORMATS[:iso_date] = {
    # Here’s the code to pass to Date#strftime
    :date => "%Y-%m-%d",
    :time => " %I:%M %p",  # notice the space before time.  If you want date and time to be seperated with a space, put the leading space here.
    :javascript_include => "format_iso_date"
  }
  CalendarDateSelect.format = :iso_date

Lesson learned … if the path looks complex, it’s probably not the wrong way … but it’s more than likely not the right way.

[1]: http://code.google.com/p/calendardateselect/ [2]: http://www.integrallis.com/ourblogs/articles/2008/01/15/building-tempo-with-rails-part-vi [3]: http://api.rubyonrails.org/classes/ActionView/Helpers/PrototypeHelper.html [4]: http://jslint.org [5]: /wp-content/uploads/2008/03/format_iso_date.js "ISO Formatter for Calendar Date Select"

comments powered by Disqus