PullMonkey Blog


11 Aug

Dynamic Select Boxes – Ruby on Rails 3


Over 4 years ago, I wrote a tutorial for doing dynamic (cascading) select boxes.
Still getting comments and emails to this day. Mostly asking how to get this working with rails 3, which has moved from prototype to jquery.
So here's a tutorial for getting 3 select boxes to trigger updates for each other.

First set things up:

I just used the html5 haml twitter bootstrap, etc template. Really useful.

If you need data, here's what I used - put this in your db/seeds.rb file:

Next, setup your model associations:

Genres have many artists.
Artists have many songs.
Genres have many songs through artists.

I'm just using a home controller to setup variables for the index page as well as setup variables for use in the dynamic updating:

Now the view just has the 3 select boxes and the unobtrusive javascript (triggered onchange) to make the ajax calls for updating:

We need our rjs files for updating the select boxes, one for the songs (when artist changes) and one for the artists and songs (when genre changes):

Our routes are simple:

That's it.

UPDATE: Here's an erb alternative for index.html.

And the js.haml can be converted to js.erb by taking #{...} and converting to <%= ... %> :


06 Jan

POST OFC Graph as Image


I was asked recently (well sort of) to give an example of saving an image to the server. If you look at teethgrinder's example for this, you will see that he has made available an external interface to do just that - POST your graph as png raw data to your server for storage. This has many benefits such as saving the image for use in a PDF report or for printing, since we know at times it is a bit troublesome to print the embedded flash object.
I think the main problem people are having with this is the receiving of the image data post - see the upload_image method below. Also, teethgrinder's example never really says where to make the post_image() call. So I touch on both in the code below.
Here is an example of the png that is saved when I did this for the chart in the previous example:
OFC Saved Image

Well, let's just get right in to the code.
The controller contains the same code as my last post with only a few minor changes to the index method and the addition of the upload_image method.
In the controller, I have this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class TestItController < ApplicationController
  def index
    # note the user of open_flash_chart_object_from_hash instead of just open_flash_chart_object
    # this allows you to pass in the id of the div you want the the chart to be in
    # this is useful for when we need to findSWF by this id
    @graph = open_flash_chart_object_from_hash("/test_it/chart", :div_name => "my_chart")
  end

  # added to recieve the post data for the OFC png image of the OFC graph
  def upload_image
    name = "tmp_image.png" || params[:name]
    # the save_image method that is provided by the OFC swf file sends raw post data, so get to it like this
    data = request.raw_post
    File.open("#{RAILS_ROOT}/tmp/#{name}", "wb") { |f| f.write(data) } if data
    render :nothing => true
  end

  def chart
    # same code from here - http://pullmonkey.com/2010/01/05/open-flash-chart-ii-x-axis-date-and-time/ 
    ...
  end
end

So just note the use of open_flash_chart_object_from_hash() in the index method, this way we can pass in the id of the div.
In the view, I have this:

1
2
3
4
5
<%= javascript_include_tag 'swfobject.js' %>
<%= @graph %>
<%= save_as_image("http://localhost:3000/test_it/upload_image?name=tmp.png", :id => "my_chart") %>
<br/>
<%= button_to_function "Save Image", "post_image()" %>

Really the only difference from what we would normally have in our view is that I am using the save image setup method that was added to the open flash chart ruby on rails plugin in the last couple hours (as of this post). The save_image method takes some arguments, mainly the url to post the image data to and the id of the chart we setup in the controller.


23 Sep

Open Flash Chart II – Javascript (Part 3)


This article is a follow on to Part 1 and Part 2. In this article, I will discuss how we can change between various charts on the fly - meaning, changing the SWFObject without rerendering the page but this time we can do it without storing everything in javascript variables initially. We will use an Ajax request to grab our data off the server.
As promised there are still more topics to come on OFC and Javascript:

Here is the graph and interface we are after in this example:

Load Original Chart (Bar Graph)||Load Chart from server data (Line Graph)

More Open Flash Chart II examples.
And here is the code (the controller):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67

class TestItController < ApplicationController
  def index
    title = Title.new("MY TITLE - original")
    bar = BarGlass.new
    bar.set_values([1,2,3,4,5,6,7,8,9])
    @chart = OpenFlashChart.new
    @chart.set_title(title)
    @chart.add_element(bar)
  end

  def some_server_data
     title = Title.new("Multiple Lines")

    data1 = []
    data2 = []
    data3 = []

    10.times do |x|
      data1 << rand(5) + 1
      data2 << rand(6) + 7
      data3 << rand(5) + 14
    end

    line_dot = LineDot.new
    line_dot.text = "Line Dot"
    line_dot.width = 4
    line_dot.colour = '#DFC329'
    line_dot.dot_size = 5
    line_dot.values = data1

    line_hollow = LineHollow.new
    line_hollow.text = "Line Hollow"
    line_hollow.width = 1
    line_hollow.colour = '#6363AC'
    line_hollow.dot_size = 5
    line_hollow.values = data2

    line = Line.new
    line.text = "Line"
    line.width = 1
    line.colour = '#5E4725'
    line.dot_size = 5
    line.values = data3

    y = YAxis.new
    y.set_range(0,20,5)

    x_legend = XLegend.new("MY X Legend")
    x_legend.set_style('{font-size: 20px; color: #778877}')

    y_legend = YLegend.new("MY Y Legend")
    y_legend.set_style('{font-size: 20px; color: #770077}')

    chart =OpenFlashChart.new
    chart.set_title(title)
    chart.set_x_legend(x_legend)
    chart.set_y_legend(y_legend)
    chart.y_axis = y

    chart.add_element(line_dot)
    chart.add_element(line_hollow)
    chart.add_element(line)

    render :text => chart.to_s
  end
end

And in your view (index.html.erb):

1
2
3
4
5
6
7
8
9
10
11
12

<html>
  <head>
    <%= javascript_include_tag :defaults, 'swfobject' %>
  </head>
  <body>
    <%= @chart.js_open_flash_chart_object("my_chart_js_1", 550,300) %>
    <br/><br/>
    <%= @chart.link_to_ofc_load("Load Original Chart", "my_chart_js_1") %> ||
    <%= @chart.link_to_remote_ofc_load("Load Chart from server data", "my_chart_js_1", "/test_it/some_server_data") %>
  </body>
</html>

In this example, we make use of the link_to_remote_ofc_load method that basically creates a link_to_remote along with the function that we call to load the chart data into the swfobject chart from the server. It takes three arguments, the link text, the id of the div whose swf we will load new data into and the url from which to fetch the data.

For more information on the javascript callbacks that I setup here, you will want to view the page source and read about it further over at teethgrinder's tutorial.

Good Luck!


22 Sep

Open Flash Chart II – Javascript (Part 2)


This article is a follow on to Part 1. In this article, I will discuss how we can change between various charts on the fly - meaning, changing the SWFObject without sending a request or rerendering the page.
As promised there are still more topics to come on OFC and Javascript:

Here is the graph and interface we are after in this example:


Load Chart 1||Load Chart 2||Load Chart 3

More Open Flash Chart II examples.

And here is the code (the controller):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

class TestItController < ApplicationController
  def index
    title = Title.new("MY TITLE")
    bar = BarGlass.new
    bar.set_values([1,2,3,4,5,6,7,8,9])
    chart1 = OpenFlashChart.new
    chart1.set_title(title)
    chart1.add_element(bar)

    title = Title.new("MY TITLE 2")
    bar = BarGlass.new
    bar.set_values([1,2,3,4,5,6,7,8,9].reverse)
    chart2 = OpenFlashChart.new
    chart2.set_title(title)
    chart2.add_element(bar)

    title = Title.new("MY TITLE - some new data")
    bar = BarGlass.new
    bar.set_values([1,3,2,5,4,7,6,9,8])
    chart3 = OpenFlashChart.new
    chart3.set_title(title)
    chart3.add_element(bar)

    @charts = [chart1, chart2, chart3]
  end
end

And in your view (index.html.erb):

1
2
3
4
5
6
7
8
9
10
11
12
13
14

<html>
  <head>
    <%= javascript_include_tag :defaults, 'swfobject' %>
  </head>
  <body>
    <%= @charts.first.js_open_flash_chart_object("my_chart_js_2", 550,300) %>
    <br/><br/>
    <% @charts.each_with_index do |chart, i| %>
      <%= chart.link_to_ofc_load("Load Chart #{i + 1}", "my_chart_js_2") %>
      <%= " || " if i < @charts.size - 1 %>
    <% end %>
  </body>
</html>

In this example, we make use of the link_to_ofc_load method that basically creates a link_to_function along with the function that we call to load the chart data into the swfobject chart. It takes two arguments, the link text and the id of the div whose swf we will load new data into.

For more information on the javascript callbacks that I setup here, you will want to view the page source and read about it further over at teethgrinder's tutorial.

Good Luck!


21 Sep

Open Flash Chart II – Javascript (Part 1)


This article (and the work behind it -- meaning get the latest from github) is generously sponsored by Harry Seldon who wants to be able to pass data around using javascript. There are quite a few benefits to this, learn more from teethgrinder's tutorial on the same topic.

This example opens up a lot of possibilities and I thank Harry for pointing me to it. So more to come on OFC and Javascript. For a taste of what is to come, check these out:

Here is the graph we are after in this example:

More Open Flash Chart II examples.

And here is the code (the controller):

1
2
3
4
5
6
7
8
9
10
11

class TestItController < ApplicationController
  def index
    title = Title.new("MY TITLE")
    bar = BarGlass.new
    bar.set_values([1,2,3,4,5,6,7,8,9])
    @chart = OpenFlashChart.new
    @chart.set_title(title)
    @chart.add_element(bar)
  end
end

Notice that I do not render the chart object, however I turn it into an instance variable for use in our javascript rendering of our chart.

And in your view (index.html.erb):

1
2
3
4
5
6
7
8
9

<html>
  <head>
    <%= javascript_include_tag :defaults, "swfobject" %>
  </head>
  <body>
    <%= @chart.js_open_flash_chart_object("my_chart_js_1", 550,300) %>
  </body>
</html>

I do a lot behind the scenes but if you look, you will see a few new things here.

  1. The data comes from the @chart.js_open_flash_chart_object(...) call which sets up a few javascript callback methods to send the data to the SWF object. It takes 3 required arguments div_name (the id of the div that houses the chart), width and height and one optional argument base_url which defaults to "/".
  2. If you look at the HTML source, it is quite a bit different, we simply embed the SWF object. We do not need to point to the data method since there actually isn't one.
  3. One difference between this Rails example and php example (from teethgrinder) is that prototype which comes standard with rails, has a JSON implementation for javascript, so we do not need the json2.js file, but make sure to include prototype.js in your apps.

For more information on the javascript callbacks that I setup here, you will want to view the page source and read about it further over at teethgrinder's tutorial.

Good Luck! and Harry, I hope this helps, otherwise please drop me a line.