ExtJS and Rails With Netzke. Custom Widget Actions.

ExtJS and Rails With Netzke. Custom Widget Actions.Today I’m coming up with a light-weight tutorial, which will show how to extend a Netzke widget in order to add a custom Ext.Action into it, and then bind that to a new button in the bottom bar, as well as to a new context menu item. Then you’ll see how we can conditionally disable this action for more consistent end-user’s experience. If ExtJS actions are still something new to you, you’ll find this tutorial especially useful, as I’ll provide a brief introduction unto them. Otherwise you’ll see how easy it is to use Ext.Action with Netzke. As usual, there’s an online demo of this tutorial’s results, and the source code is on GitHub.

We’ll be extending Netzke::GridPanel, as it already provides both the bottom toolbar bar and the context menu, which will leave us little work adding our action into them. Let’s call our widget CustomActionGrid. Add a file named custom_action_grid.rb into the “lib/netzke” directory inside of your Netzke-enabled project (as always, you can use netzke-demo project as your playground), and start with defining our widget’s class:

module Netzke
  class CustomActionGrid < GridPanel
  end
end

What Ext.Action is

Let’s take a moment to understand what Ext.Action actually is. Here’s an excerpt from the ExtJS documentation:

An Action is a piece of reusable functionality that can be abstracted out of any particular component so that it can be usefully shared among multiple components. Actions let you share handlers, configuration options and UI updates across any components that support the Action interface (primarily Ext.Toolbar, Ext.Button and Ext.menu.Menu components).

It may be easier to understand the concept by an example. Imagine, your widget needs to do something on a request from the end-user. What are the ways for the user to trigger the action? She may click a button on the widget’s toolbar for example, or use the context menu, or even turn to the application-wide menu that links to your widget. Those possibilities are represented by different ExtJS components (hence the reference to “multiple components”): your widget’s toolbar, the context menu, or the application’s menu. If you were to configure all the 3 occurrences by hand, you would end up with not-so-DRY code, defining at least the same handler function everywhere. Now add that a button or a menu item can also have an icon and a state (enabled/disabled, hidden/shown), both of which can dynamically change according to some conditions, and you’ll realize that maintaining consistency among your widget’s actions would be a tedious task. That’s where Ext.Action comes to rescue. Create an action with an initial handler, icon, and state, and then link to it from any of those “components that support the Action interface”. Later, if you dynamically change any of those properties (in this tutorial we’ll be playing with the state), ExtJS will automatically update all controls that use this action.

Let’s add our custom action by overriding the Netzke::Base#actions method:

def actions
  super.merge({
    :show_details => {:text => "Show details"}
  })
end

This method (which every Netzke widget implements) should return a hash of actions that will be later instantiated inside of the JavaScript object. In this case the action will be called “showDetails”, and, by being triggered, it’ll call the widget’s method called onShowDetails by default (you can overwrite it by providing the :fn option like this: :fn => “myHandler”). Additionally, you can specify any extra configuration options that are accepted by Ext.Action.

Implementing the action

We may want our action to do something as complex or as simple as we wish. In this tutorial let’s open a little window which will display a formatted HTML text showing the summary of the currently selected record. So, let’s define the handler (as always, we do it inside the class method js_extend_properties):

def self.js_extend_properties
  {
    :on_show_details => <<-END_OF_JAVASCRIPT.l,
      function(){
        var tmpl = new Ext.Template("<b>{0}</b>: {1}<br/>"), html = "";
        Ext.iterate(this.getSelectionModel().getSelected().data, function(key, value){
          html += tmpl.apply([key.humanize(), value]);
        }, this);
        
        Ext.Msg.show({
          title: "Details",
          width: 300,
          msg: html
        });
      }
    END_OF_JAVASCRIPT
  }
end

Adding the action to the toolbar and context menu

After this we want to add the action both to the bottom toolbar and context menu (here I’m adding them, followed by a separator, before the other items):

def default_bbar
  ["show_details", "-", *super]
end

def default_context_menu
  ["show_details", "-", *super]
end

Here’s how the resulting bottom bar and context menu should look like: Action in context menu and bottom bar

And here’s what we should see after we click either the button, or the context menu item:

The result of the action

Dynamically enabling/disabling the action

Now let’s disable the action if the amount of selected rows in the grid is different from 1. In order to do it, override the Ext.Component#initComponent method like this:

:init_component => <<-END_OF_JAVASCRIPT.l,
  function(){
    #{js_full_class_name}.superclass.initComponent.call(this);

    this.getSelectionModel().on('selectionchange', function(selModel){
      this.actions.showDetails.setDisabled(selModel.getCount() != 1);
    }, this);
  }
END_OF_JAVASCRIPT

Note, that the generated by Netzke JavaScript class is provided with the actions property to access the actions that we define in the actions method in Ruby.

Now, if we select more than one row, our action gets disabled:

Action disabled

And here’s the last little touch. As the GridPanel initially loads without any rows selected, we want our action to be disabled by default. Update the actions method like this:

def actions
  super.merge({
    :show_details => {:text => "Show details", :disabled => true}
  })
end

Conclusion

This concludes the tutorial. Hopefully, you have learnt how to create and implement custom actions in a Netzke widget, and how to dynamically change the action’s state. Please, leave your remarks, questions or suggestions in the comments, or post them straight on the Netzke Google Groups.

blog comments powered by Disqus