So I’ve got this project where I would like Radiant to render the
initial page, but do subsequent updates to various divs via AJAX. I
decided this because I don’t want a separate but identical layout files
within Radiant and the app/views/layout of my extension. I won’t be the
one to use this CMS once it is installed and if the user has to come
back to me for minor layout changes once things are in place, that won’t
be nice.
I created an extension with the idea that I would create a tag for each
div and define them to render the same partials that my the AJAX calls
return, just with some default parameters. I imagined I would use
render_component or maybe render_partial but quickly found there is no
controller to make these accessible from the tag definition context.
Without partials and without helpers like form_remote_tag and
link_to_remote, I found myself putting lots of raw html and javascript
(Prototype Ajax.request methods) in each tag block. Pretty nasty, so I
scrapped that.
Another solution would be the ability to render snippets from my
controller instead of the usual views. I’ve looked at Radiant on Rails
but support for processing Radiant tags while doing such output is still
a work in progress. Even if tag processing is finished soon, there is
still the issue of getting to Prototype/AJAX helper methods into those
snippets. I really like the idea of using snippets via my controller
though, if it were combined with an extension that exposes rails
Form/Prototype/Javascript helpers via Radiant Tags, that could maybe
work, without introducing that complexity into the core Radiant code.
For example, instead of my controller/view outputting an entire AJAX
sortable table, if I could define it as a snippet w/ the appropriate
AJAX helpers in place as Radiant tags, the end user could then tweak
considerably more, adding/dropping columns or changing class/id/css
information. I’ve already created radiant tags to access and iterate
over rows in my model so I think this would be a nice fit.
Which brings me back to my original approach, accessing rails
view/controller helpers from an extension tag block. I’ve read a few
posts where people were trying to do this, but they all have their
problems. Below are my experiences with various ways I’ve tried to get
around this.
Mental Tags - FormTagHelper?
http://www.ruby-forum.com/topic/104454#232664
This could allow tags to use rails helpers, thought it may be buggy. But
without the ability to render snippets from my controller, it is only a
half solution as my the AJAX responses from my controller will need to
output snippets, which RadiantOnRails does, but without processing
Radiant tags, so it would only work for the initial request served up by
Radiant. I could use a separate partial for the AJAX responses but I
don’t want my user making changes in the CMS and finding the AJAX
responses look different.
ActionView helpers in a behavior - I think I got it!
http://www.ruby-forum.com/topic/73437
Same issues as above, possible bugs, no controller access to output
snippets after tag expansion.
RJS/Tracking Controller through Radiant Extension?
http://www.ruby-forum.com/topic/107811#244757
Requires modifying a page’s process method to look for AJAX requests,
but I thought my extension’s controller can already receive requests
directed to it, so I don’t see the point.
[Radiant] Can you use controllers/views within a tag
http://lists.radiantcms.org/pipermail/radiant/2007-February/003407.html
Ahh, bring a controller instance into my Tag module/mixin. This may be
my best solution at this point. This would allow me to render entire
divs using single tags that output partials using my controller - the
same partials that my AJAX responses will continue to return. No need to
import rails view helpers into my tag module, and no need to render
snippets from the controller with tag processing. Not as as flexible as
I would like, but this approach doesn’t require me to duplicate the site
layout in app/views/layouts. The sample code in the above post doesn’t
work as is, it needed a few changes, which the the guys in #ruby-lang
help me come up with. Posted below, with asterisks by the added/changed
lines -
def activate
-
require ‘application’
-
SiteController.class_eval do
def show_uncached_page(url)
puts ‘!!!in show_uncached_page’
@page = find_page(url)
unless @page.nil?
@page.process_controller(self)
@cache.cache_response(url, response) if live? and @page.cache?
@performed_render = true
else
render :template => ‘site/not_found’, :status => 404
end
rescue Page::MissingRootPageError
redirect_to welcome_url
end
end
-
Page.class_eval do
attr_accessor :controllerdef process_controller(controller)
@controller = controller
process(controller.request, controller.response)
end
end
Page.send :include, MemberDirectoryTags
end
So after that, from your tag definition, you just call -
tag.globals.page.controller.render_component_as_string :controller =>
“member_directory”, :action => “sortable_table”
Right? Nope. render_component_as_string is protected.
So I tried something different -
From my controller (MemberDirectoryController) -
def sortable_table_as_string
render_component_as_string :controller => “member_directory”,
:action => “sortable_table”, :params => { :person => “david” }
end
From my tag definition -
tag ‘member_test_tag’ do |tag|
@controller = MemberDirectoryController.new
puts @controller
@controller.sortable_table_as_string
end
well when I use <r:member_test_tag /> this displays in my browser -
can’t dup NilClass
Through using some output statements I found this happens during
render_component_as_string in my controller. I’m guessing the controller
needs more initialization than just calling new on it.
And finally I tried…
(asterisks on lines added over prior example)
def activate
require 'application'
SiteController.class_eval do
def show_uncached_page(url)
puts ‘!!!in show_uncached_page’
@page = find_page(url)
unless @page.nil?
@page.process_controller(self)
@cache.cache_response(url, response) if live? and @page.cache?
@performed_render = true
else
render :template => ‘site/not_found’, :status => 404
end
rescue Page::MissingRootPageError
redirect_to welcome_url
end
-
def unprotected_render_component_as_string(options)
-
render_component_as_string options
-
end
end
Page.class_eval do
attr_accessor :controllerdef process_controller(controller)
puts ‘!!!in process_controller’
@controller = controller
process(controller.request, controller.response)
end
endPage.send :include, MemberDirectoryTags
end
Like my past attempts but I attempted to get around the protected
render_component_as_string and half-initialized controller by adding a
public instance method to SiteController called
unprotected_render_component_as_string(options) to expose the protected
method.
And in my tag definition file I put:
tag.globals.page.controller.unprotected_render_component_as_string
:controller => “member_directory”, :action => “sortable_table”
The <r:member_test_tag /> produced the error “interning empty string”
Below is the framework trace -
/usr/lib/ruby/gems/1.8/gems/radiant-0.6.1/lib/login_system.rb:16:in
intern' /usr/lib/ruby/gems/1.8/gems/radiant-0.6.1/lib/login_system.rb:16:in
authenticate’
Looks like an authentication issue? I don’t really know.
Well, that is it. I’ve outlined this quest to get things working with
AJAX so it will be available as a use case, and maybe help the devs
decide how to go forward with official changes to accommodate such apps.
I very much like Radiant for non-ajax use, I’ll be keeping an eye on it
for future projects as well.
If someone could point out how to access render_component_as_string from
my tag definition I’d be grateful. I’ve got to move on with this project
so I will be doubling up on my layouts for now, but if there is a way,
I’d like to know.