Building Rails-ExtJS widgets with Netzke, part 1
This tutorial will show you how to create an AJAX-based Rails/ExtJS component using the netzke-core Rails plugin (the core part of the Netzke framework). It includes creating a demo Rails application from scratch. If you prefer to directly see the results (rather than follow the tutorial step-by-step), go to the last section below, and you’ll be up-and-running in just 3 simple steps.
Introduction
This tutorial has been updated on 2010-01-10 to be compatible with the latest netzke-core
We will create a Ext.tree.TreePanel-based widget from scratch and make it dynamically load the nodes from the server. A significant part of this tutorial is dealing with the basics of creating a Rails application, and it is there for those who are new to Rails. This tutorial requires Rails >= 2.3.0 and ExtJS >= 3.0.
Preparing a Rails/ExtJS application, and installing Netzke
Let’s start with creating a fresh rails application called “netzke-tutorial”:
rails netzke-tutorial && cd netzke-tutorial
Download ExtJS from http://extjs.com/products/extjs/download.php, and link it into the application’s public folder as extjs (make sure that, as the result, the folder “adapter” appears below the folder “public/extjs”), e.g.:
ln -s ~/code/extjs/ext-3.1.0 public/extjs
Install netzke-core as a Rails plugin (the latest and greatest version, as compared to the gem):
./script/plugin install git://github.com/skozlov/netzke-core.git
Declare the Netzke routes somewhere in routes.rb:
map.netzke
This declares the routes that Netzke will be using for client-server communication (among the rest).
Generate a “welcome” controller with an “index” action:
./script/generate controller welcome index
Delete the Rails' index file (public/index.html).
Create a simple application layout (in ‘app/views/layout/application.html.erb’):
<head> <title>My first Netzke widget</title> <%= netzke_init %> </head> <body> <%= yield %> </body>
Note the
netzke_inithelper in the “header” section: it provides for all the necessary JS and CSS includes, both for ExtJS and Netzke. Enabling Netzke in your views is as simple as that.
Enough with the preparations, let’s get to the fun part.
Creating Netzke widget
Create the folder “lib/netzke”, and inside it a file called “my_tree.rb” – here goes the code for our widget. Define the widget class in it:
class Netzke::MyTree < Netzke::Base end
This is enough for the simplest widget (by default, it results in a JS class inheriting from Ext.Panel). Let’s embed it into our index.html.erb:
<h1>My tree panel</h1> <div style='width: 300px;'> <%= netzke :my_tree %> </div>
As you see, embedding a Netzke widget into a view is done with the netzke helper. See the result in the browser. It’s very basic, but it works, right?
You can specify the majority of options of the Ext.Panel component in the :ext_config hash, and this is where, I hope, it starts getting interesting. Try the following, for instance:
<%= netzke :my_tree, :ext_config => { :html => 'Just a simple panel for now', :title => 'Here comes the tree', :body_style => "padding: 5px;", :border => true }%>
Ok, let’s get rid of :ext_config, and make the JS part of our widget inherit from Ext.tree.TreePanel (instead of the default Ext.Panel). We do it by defining the js_base_class class method in our MyTree class:
class Netzke::MyTree < Netzke::Base def self.js_base_class "Ext.tree.TreePanel" end end
Also, Ext.tree.TreePanel requires a root to be defined, we do it in js_extend_properties class method:
def self.js_extend_properties { :root => {:text => 'Root', :id => 'source'} } end
In
js_extend_propertiesdefine all the properties that the JS class will have by default, including its public functions (including the overrides), such asinitComponent, which we are going to define below.
Reload the page in the browser and see what’s changed: now we have a tree!
Add Rails model for tree data
In order to make the tree dynamically load its nodes from the server, we’ll need a Rails model with acts_as_tree functionality mixed in (let’s call it “Folder”):
./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
Also, acts_as_tree comes as a Rails plugin, so we’ll need to install it, too:
./script/plugin install git://github.com/rails/acts_as_tree.git
To seed some data into the tree, add the following into db/seeds.rb:
# Initial tree data root = Folder.create(:name => '/') root.children.create(:name => 'One') root.children.create(:name => 'Two').children.create(:name => 'Three')
Now, run the migrations and seed the data:
rake db:migrate && rake db:seed
Server part of the widget
Now, how do we implement the communication between the browser and the server? Netzke makes it very easy. We need to declare the interface between the client and the server sides of the widget. Use the api class method of Netzke:
class Netzke::MyTree < Netzke::Base api :get_children ... end
It will automatically provide for the following. The client side of the widget will get the method getChildren, which will initiate the AJAX communication with the server side. Calling this method will result in a remote call to the widget’s instance method get_children. This is explained in detail here, and covered in the third part of the tutorial. However, in this particular case, we will not need to call the server side explicitly, but rather provide Ext.tree.TreePanel with a URL from which it will be able to fetch the data (see Ext.tree.TreePanel documentation for details). We’ll do it by overriding the initComponent method (that any Ext.Component has):
# New and overriden properties of the resulting JS class def self.js_extend_properties { :root => {:text => 'Root', :id => 'source'}, :init_component => <<-END_OF_JAVASCRIPT.l, function(){ // dataUrl should be set to the API point which provides us with the nodes this.dataUrl = this.buildApiUrl("get_children"); // Call the superclass' initComponent #{js_full_class_name}.superclass.initComponent.call(this); } END_OF_JAVASCRIPT } end
In order to retrieve the URL for a specific API point, we use Netzke’s
buildApiUrlmethod. This abstracts away the fact that each JS instance of a Netzke widget finds a way to connect to its proper Ruby instance. This is what provides us with high reusability (see the next section).
Now we only need to implement the server side of the interface:
def get_children(params) klass = config[:model].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 the AJAX request. You see that this method uses the configuration parameter “model”, which we should provide to our widget so that it knows which Rails model should be used for the data. Add it to the netzke helper call in the view:
<h1>My tree</h1> <div style='width: 300px;'> <%= netzke :my_tree, :model => "Folder" %> </div>
That’s it! Reload your browser page and see the result.
Reusability of Netzke widgets
The best part of having a Netzke widget like this, is that it’s very easily reusable. To start with, you may have multiple MyTree’s in the same view (and Netzke is smart enough to not duplicate JS class definitions for each widget’s instance). In order to do that, simply name them differently, and provide individual configuration for each one. Netzke will take care of each client-side instance of the widget talking to its proper server-side instance:
<%= netzke :my_first_tree, :class_name => "MyTree", :model => "Folder", :ext_config => {:title => "Folders"} %> <%= netzke :my_second_tree, :class_name => "MyTree", :model => "Categories", :ext_config => {:title => "Categories"} %>
We didn’t have to specify
class_nameoption before, because, by convention, Netzke could infer it from the widget’s name.
Further, you may use any Netzke widget as a part of a compound widget, as well as load it dynamically on the fly. For these techniques, see the next parts of this tutorial’s series.
For busy folks
Instead of following the above instructions step-by-step, you can simply do the following:
Get the tutorial source code:
git clone git://github.com/skozlov/netzke-tutorial.git
Link your ExtJS library into public/extjs, e.g.:
ln -s ~/code/extjs/ext-3.1.0 public/extjs
Start the application:
cd netzke-tutorial && ruby script/server
That’s it. Point your browser to http://localhost:3000
You can find the source-code for this tutorial on GitHub.
News on Netzke:
- Twitter: http://twitter.com/skozlov
- Blog: http://blog.writelesscode.com
