Saturday, May 31, 2008

Ext.apply and Ext.applyIf

In case you missed it, last weekend Jay Garcia released his third screencast in which he explains Ext.apply.

Here is the video, a short 7 and something minutes long, which is a fresh alternative of reading plain blog posts. :-)





Jay actually mentions a firebug bug which I wasn't aware of but which I encountered just this week after learning about it in his screencast. The bug is that the properties of an object you are inspecting (after clicking on the your object which is printed to your console via console.log(object) or console.info(object)) might not be the actual current ones. The work-around is to print directly the property you want to inspect via console.log(object.property).

If you're a little confused about the defaults (third) parameter of Ext.apply, then just think of it as you would write the the following

Ext.apply(receiver, defaults);
Ext.apply(receiver, source);

// is the same as
Ext.apply(receiver, source, defaults);


Also note that there is an Ext.applyIf method (without a third parameter) which only assigns properties/attributes to the receiver object if they aren't existing in the receiver object yet.

And as a small extra here's a real-world example of using Ext.apply which I implemented this week:

onBeforeLoad: function(store, options) {
options.params = options.params || {};
this.cleanParams(options.params);
var params = this.buildQuery(this.getFilterData());
if (store.lastOptions && store.lastOptions.params) {
options.params = Ext.apply(store.lastOptions.params, options.params);
}
Ext.apply(options.params, params);
}

I'm overwriting here the onBeforeLoad method from the excellent Ext.ux.GridFilter extension which is executed every time before the store is loading.
So if you load the store the first time via store.load({params: {start: 0, limit: 50}}), it will resend these parameters when the store is reloaded after you changed your filter values or after you manually executed store.load() after you saved changes to your EditorGridPanel for example. Moreover, the lastOptions params will be overwritten when you switch to the next page via your PagingToolbar, which calls something like store.load({params: {start: 50, limit: 50}}).

Monday, May 26, 2008

More Rails 2.1 changes that target JSON and therefore Ext JS

Two days ago I posted about Rails 2.1 changes that make living with Ext JS easier. Today I discovered (via Jörg Battermann) a tutorial about using Rails 2.1 (see Part 1 and Part 2).
The tutorial shows two new config options that target JSON, especially the to_json method: ActiveRecord::Base.include_root_in_json and ActiveSupport.use_standard_json_time_format

Here are the respective changesets and some more information on these changes:

Changeset 9202: Tweak ActiveRecord::Base#to_json to include a root value in the returned hash: {post: {title: ...}}

This is almost a nice change. I mean almost because it might help you when feeding your Ext JS forms via a Ext.data.JsonReader, but it'll break your Ext JS grids, when using the Ext.data.JsonReader.

Currently, you do something like the following in your javascript code:

orders_ds = new Ext.data.Store({
reader: new Ext.data.JsonReader({root: 'orders', id: 'id' }, orders_items),
proxy: new Ext.data.HttpProxy({url: orders_path_json})
});

and the following in your controller action:

render :json => { :orders => @orders }

which returns a JSON data in the following format

{"orders": [{"id": 1, ...}, {"id": 1, ...}]}

Well, after you turn on the ActiveRecord::Base.include_root_in_json option, it'll return:

{"orders": [{"order": {"id": 1, ...}}, {"order": {"id": 2, ...}}]}

which cannot be understood by your Ext.data.JsonReader by default.

It would be smarter if the include_root_in_json would differ between a single active record object and a collection of active record objects.
Therefore, if you use render :json => @orders or @orders.to_json, it would return the data in an array without a root for each object, but with the plural name of the model class name as root for the array. Just as I showed in the first JSON data example.
Well, maybe I'll write a patch for Rails for that.

Changeset 9203: Add config.active_support.use_standard_json_time_format setting so that Times and Dates export to ISO 8601 dates.

If you turn on this config option, it will probably also break your Ext JS grids. But you can easily fix that...

Right now you probably have this code in your record layout part when dealing with dates:

{ name: 'release_date', type: 'date', dateFormat: 'Y/m/d' }

Well, your JsonReader assumes that the date format is Y/m/d. But after you turned the use_standard_json_time_format option on, it will return the date in the default JSON date format.
It means you have to change the date format to the following:

{ name: 'release_date', type: 'date', dateFormat: 'Y-m-d' }


Well, these Rails changes are all JSON related changes that come in the soon to be releases Rails 2.1, but which won't really make your life easier with Ext JS. Sorry to disappoint you on that. But Rails is certainly going into the right direction.

Saturday, May 24, 2008

Rails 2.1 changes that make living with Ext JS easier

I just like to point out a few changes of the soon to be released Rails 2.1 which I caught via Chu Yeow's Living on the edge blog series.

Automatically parse posted JSON content for Mime::JSON requests
The most important change is probably the automatically parsing of JSON requests into the params hash.
Here's the example from Chu Yeows's post:

# http request
POST /posts
{"post": {"title": "Breaking News"}}

# controller action code
def create
@post = Post.create params[:post]
end

Similar functionality is also provided by the json_request plugin which I use currently with Rails 2.0.2.

json_escape for escaping HTML entities in JSON strings
In the same post, Chu Yeow is mentioning the addition of json_escape:

json_escape("is a > 0 & a < 10?")
# => is a \u003E 0 \u0026 a \u003C 10?

# or you can use the alias j in your erb/haml templates:
<%=j @person.to_json %>

Right know I can't think of another use as Chu Yeow's example for documenting a JSON api.

Custom JavaScript and stylesheet symbols
You all know javascript_include_tag :defaults, which includes your application.js and prototype files.
Now you can define your own symbols, such as :extjs.

See here for details of this change.
This addition is similar to my little helper I use in my projects which I described in my ext-all.js or ext-all-debug.js post.

Haml 2.0
By the way, because I mentioned haml, today Haml 2.0 was released, which includes a javascript filter. Check out the blog post for more info.

Thursday, May 22, 2008

Rails helpers in Ext JS

Today I needed to truncate a set of strings in Ext JS. Rails has the truncate helper method built in, Ext JS not, which isn't bad since it's pretty easy to write your own little helper in Ext JS.
Update: Diego pointed out that there is actually a truncate method in Ext JS, called ellipsis which can be found in the Ext.util.Format class. I hope this post still makes a little sense, since it's about writing your own helpers in Javascript/Ext JS.

Here's the Ruby code from the Rails core:

def truncate(text, length = 30, truncate_string = "...")
if text.nil? then return end
l = length - truncate_string.chars.length
(text.chars.length > length ? text.chars[0...l] + truncate_string : text).to_s
end

Here's my little Javascript helper:

Ext.apply(String.prototype, {

truncate_length: 30,

truncate_string: "...",

truncate : function(truncate_length, truncate_string) {
truncate_length = truncate_length || this.truncate_length;
truncate_string = truncate_string || this.truncate_string;
var length = truncate_length - truncate_string.length;
return this.length > truncate_length ? this.substr(0, length) + truncate_string : String(this);
}

});

It has the same behavior as the Rails helper. Note that you don't really have to use Ext.apply(), but it helps you to DRY your code, since you can pass an object with multiple methods and attributes to the prototype attribute, instead of writing String.prototype = function() { .. } for each attribute/method you want to add/overwrite.

Creating an extra attribute for truncate_length and truncate_string allows you to easily define your default values for your app. So if you like to append only two dots by default, just add String.prototype.truncate_string = '..' to your code.

While writing this post I actually remember that Prototype JS has the truncate and many other helper methods already built in. But well, writing your own helpers (with the help of Ext JS) can be fun, too. X-D

Here is the Prototype code, just for the sake of comparison:

truncate: function(length, truncation) {
length = length || 30;
truncation = truncation === undefined ? '...' : truncation;
return this.length > length ?
this.slice(0, length - truncation.length) + truncation : String(this);
}

Wednesday, May 14, 2008

Ext JS Events, Observable and DomHelper slides

Want to learn more advanced stuff about how to use Ext JS?
Check out these slides from an Ext JS core developer named Aaron Conran. You might know him from the Ext JS forum.



First a very good and quick introduction to Javascript, where you learn more about strictly (not) equals such as === and !== which you encounter often in the Ext JS code and also some nice to know information about the difference between null and undefined.



The next presentation teaches you about Ext JS Event Handling:




Next a presentation about Ext JS Observable Class which includes a nice example of how to catch all fired events of an object and print them to your firebug console.



Ext JS's Dom helpers should also be checked out!






Thanks Aaron for putting together these very helpful slides!