Building Rails-ExtJS widgets with Netzke, part 1
UPDATE (2009-09-07): This tutorial is outdated with the release of netzke-basepack v0.5.0. This tutorial will show you how to create an AJAX-based Rails/ExtJS component using the netzke-core gem, the core part of the Netzke framework. If you prefer to directly see the result rather than follow the tutorial step-by-step, see the last paragraph below for the instructions that will get you up-and-running in just 3 simple steps.
Introduction
We will create a Ext.tree.TreePanel-based widget from scratch and make it dynamically load the tree-nodes from the server - you will learn how easy it is to setup a client-server communication with Netzke. In the following parts you'll learn how to add more functionality to this widget, how to nest it in other widgets (such creating compound widgets), and how to write tests for it.
A big part of it is dealing with the basics of creating a Rails application, and it is there for those who are new to Rails. The next tutorial parts will build upon this one. This tutorial requires Rails-2.2.2 and Ext-2.2.
Preparing a Rails application
Let's start with creating a fresh rails application called "netzke_tutorial":
rails netzke-tutorial && cd netzke-tutorial
Generate welcome controller with an index action:
./script/generate controller welcome index
uncomment this line in routes.rb:
map.root :controller => "welcome"
and delete the Rails' index file (public/index.html).
Now, configure the Passenger to serve your application, and after pointing to http://netzke-tutorial.local you must see the default Rails text. (Of course, you can also use WEBrick, Mongrel or whatever you prefer, but I believe that you use Passenger at least for development - because it's very, very handy).
Download extjs from http://extjs.com/products/extjs/download.php, rename it into extjs and put it into the public folder (you can also create a symlink), so that the folder 'adapter' is right below the folder public/extjs.
Enough preparations, let's get to the fun part.
Installing Netzke and creating the client part of the widget
Install the netzke-core gem:
gem install netzke-core
Include the gem in the environment.rb:
config.gem "netzke-core"
Declare the routes in routes.rb:
map.netzke
Run the plugin's generator, which will create a couple of files that'll help us get starting:
./script/generate netzke_core
Create the folder lib/netzke, and inside of it a file called 'tree_panel.rb' - this will be our widget.
Define the class in it:
class Netzke::TreePanel < Netzke::Base
end
This is enough for a functioning simplest widget (by default, it is based on "Ext.Panel"). Let's make it known to the controller:
class WelcomeController < ApplicationController
netzke :tree_panel
end
By that, you'll get a couple of helper methods to integrate the widget into your HTML page (we'll look at that later), but also a useful controller action tree_panel_test, which will generate a simple html page for us with the widget baked right in. Point you browser to http://netzke_tutorial.local/welcome/tree_panel_test
It's very basic, but it works, right? Btw, you can specify the majority of options of Ext.Panel component in the :ext_config hash. Try this for instance:
class WelcomeController < ApplicationController
netzke :tree_panel, :ext_config => {
:bbar => [{:text => 'Button', :menu => {
:items => [{:text => 'Not implemented'}]
}}],
:html => 'Simply a panel',
:border => true}
end
Isn't it kind of programming Ext JS in Ruby? :)
Ok, let's get rid of this :ext_config, and make our panel a Ext.tree.TreePanel. We do it by defining js_base_class class method in our Netzke::TreePanel class:
class Netzke::TreePanel < Netzke::Base
def self.js_base_class
"Ext.tree.TreePanel"
end
end
Also, an Ext tree needs a root to be defined, we do it in js_default_config method:
def self.js_default_config
super.merge({
:root => {:text => '/', :id => 'source'}
})
end
Reload you browser page and see what's changed - we have a tree!
Creating the server part of the widget
Now let's make this tree to dynamically load data from the server. For this we'll need a Rails model with acts_as_tree functionality mixed in, let's call it "Folder". Generate the model:
./script/generate model Folder name:string parent_id:integer
Add acts_as_tree line into app/models/folder.rb, so that it looks like this:
class Folder < ActiveRecord::Base
acts_as_tree
end
In the migration file generated for you by Rails, add these lines before the end of self.up method:
root = Folder.create(:name => '/')
root.children.create(:name => 'One')
root.children.create(:name => 'Two').children.create(:name => 'Three')`
This will provide us with some initial data that will later be loaded into the tree.
Also, acts_as_tree is an Rails plugin, so we'll need to install it, too:
./script/plugin install git://github.com/rails/acts_as_tree.git
Now, run the migrations:
rake db:migrate
Now, how do we implement the communication between the server and the browser? Netzke makes it very easy. We need to declare the interface between the client and the server sides of the widget:
class Netzke::TreePanel < Netzke::Base
interface :get_children
...
end
It will automatically provide the following: 1) the client side with the url for the AJAX request, which will be stored as a property of the config parameter for the constructor - config.interface.getChildren, 2) the server side with the call to get_children method (which we need to implement) in our widget class when the client side does the AJAX request.
Ext.tree.TreePanel loads its nodes by means of Ext.tree.TreeLoader which we will introduce like this:
def self.js_default_config
super.merge({
:root => {:text => '/', :id => 'source'},
:loader => {:data_url => "config.interface.getChildren".l}
})
end
(the l-method which Netzke defines for strings makes sure that the string's content will be literally translated into JS-code, without being included into quotes)
Now we only need to implement the server side of the interface:
def get_children(params)
klass = config[:data_class_name].constantize
node = params[:node] == 'source' ? klass.find_by_parent_id(nil) : klass.find(params[:node].to_i)
node.children.map{|n| {:text => n.name, :id => n.id}}
end
The method returns an array of tree nodes, which Netzke will translate into JSON format and pass to the client as a response to an AJAX request. You see that this method uses the configuration parameter "data_class_name", which we should provide to our widget so that it knows which Rails model should provide us the data (in welcome_controller.rb):
class WelcomeController < ApplicationController
netzke :tree_panel, :data_class_name => 'Folder'
end
That's it! Reload your browser page and see the result.
For busy folks
Instead of following the above instructions step-by-step, you can simply do the following:
sudo gem install netzke-core
git clone git://github.com/skozlov/netzke-tutorial.git
cd netzke-tutorial && ruby script/server
That's it. Point your browser to http://localhost:3000/welcome/tree_panel_test
You can find the source-code for this tutorial on GitHub: http://github.com/skozlov/netzke-tutorial
If you want to see some samples of pre-created Netzke widgets and interaction between them, have a look at the live demo that was built as a prototype for the upcoming Netzke framework: http://ext-widget-poc.writelesscode.com
