пятница, сентября 15, 2006

RJS tricks

Just have read presentation slides from Euro RailsConf 2006 talk "Sharing RJS: Reuse at the app level" and I think it is VERY helpful.

среда, сентября 13, 2006

Rails javascript generation and conditions

I've recently faced the problem: I need to generate javascript that would insert some HTML block unless it is already there. So, the code should be something like this:

if( !($('block_id')) ) {
// insert block
}

All that should go to RJS template.

First revision:

page << "if( !($('block_id')) ) {"
page.insert_html :before, 'container_id', 'my html'
page << "}"

Next, it would be nice to make use of JavaScriptProxy objects (that are spawn, e.g., by page#[] method). The problem is that HTML is emited when those proxy created or evaluated, thus it's not possible to wrap that javascript code into e.g. "if( ... )"..
Solution, first try:

class << page
def if(expr)
self << "if( #{expr} ) {"
yield if block_given?
self << "}"
end
end

page.if "$('#{element_id}').visible()" do
page[element_id].hide
end

This is better then the first one, but too ugly though. A better solution would include some black magick:

module AcitiveView
module Helpers
class JavaScriptProxy
def respond_to?(name)
return true if name.to_sym == :to_script
super
end

def to_script
@generator.instance_variable_get('@lines').pop.chomp(';')
end
end
end
end

Then:

class << page
def if(expr)
self << "if( #{expr.respond_to?(:to_script) ? expr.to_script : expr} ) {"
yield if block_given?
self << "}"
end
end

page.if(page[element_id].visible) do
...
end

And finally, the more peaceful variant:

module ApplicationHelper
def if_exists(page, element_id)
page << "if( !($('#{element_id}')) ) {"
yield if block_given?
page << "}"
end
end

if_exists page, 'block_id' do
page.insert_html .....
end

воскресенье, сентября 10, 2006

Site map

How do you divide site into areas ? How do you highlight current link ?

Definition

You've got site with several navigation bars, e.g. top menu and sidebar. You want current location highlighted on both top menu and sidebar.

Analysis

Rails has link_to_unless_current method to deal with such things, but it will not be enough: it handles link to only one page, but you need the bunch of pages be linked to. Link to can become selected only on one of the page, but on others it will not be selected (because they have different urls).

Solution

My solution was to develop some site structure markup that could associate each page with some "area" or "section" or kind of..

I wrote site_map plugin that handles that:

SiteMap.draw do |map|
map.area 'member' do
map.section 'profile', :controller => 'member' do
['profile', 'profile_update',
'company_profile', 'company_profile_update'
].each do |action|
map.location :action => action
end
end
end

map.area 'admin' do
map.section 'users', :controller => 'users'
map.section 'products', :controller => 'products'
end
end

In controller and in view there is a current_location method available that returns a location descriptor - object, that has area, section, etc. values populated with names that correspond to current page. You can use it to find out what link should be "selected":

<%= link_to_if current_location.section == 'users',
'User management', :controller => 'users' %>