[Help] How to render or replace a partial page having <script> tag?

My problem is quite complicated.

In my project, I have to use static <script> tag to generate dynamic graph with links inside it. for example in a erb.html file, I wrote.

   1. ### this will generate a tree view in my page.    2. <script>    3. create_tree(){###};    4. </script>    5. <div id='tree'></div>

However, the <script> section above is not static, I have to write a helper to dynamically generate it. so it becomes to

   1. <%= generate_tree_view(:id=>1) ## this will generate the <script> section and insert them in the page    2. <div id='tree'></div>

But, right now, my problem is I have to call this tree view page by ajax like this

   1. ## view file show.erb.html    2. ...    3. <%= link_to_remote 'see_tree', :url=>{:action=>'view_tree', :id=>1} >    4. <div id='view_tree'></div>    5. ...

   1. ## controller file tree_controller.rb    2. ...    3. def view_tree    4. ### get some parameters, say 'tree_parameter' array    5. render :update do |page|    6. page.replace_html 'view_tree', :partial=>'view_tree', :object=>tree_parameter    7. end    8. end    9. ...

   1. ## partial page _view_tree.erb.html    2. <%= generate_tree_view(:tree_parameter=>view_tree)    3. <div id='tree'></div>

I thought this should work because it actually worked if I directly use the generate_tree_view help in the show.erb.html. But however, when the partial page is called by ajax, the <script> section which is generated by generate_tree_view didn't show up in the page. The final page only shows this:

   1. ## view file show.erb.html, saw by firebug 'cause it is the only way I can see a rendered page.    2. ...    3. <div id='view_tree'>    4. <div id='tree'></div>    5. </div>    6. ...

It seems they got executed by the javascript call instead of just display it.

So, what should I do if I just want to inject a <script> section in a rendered page.

I'm not sure whether you have already got my idea, but I really appreciate it if you help me solve this problem. Thank you.

I don't use RJS although I have used rails to generate javascript dynamically. I tend to prefer to see the javascript that's being executed so that when things go wrong I know exactly why. Anyway, someone else reading this thread might have a quick answer whereas I don't, but ...

My problem is quite complicated.

In my project, I have to use static <script> tag to generate dynamic graph with links inside it. for example in a erb.html file, I wrote.

   1. ### this will generate a tree view in my page.    2. <script>    3. create_tree(){###};    4. </script>    5. <div id='tree'></div>

However, the <script> section above is not static, I have to write a helper to dynamically generate it. so it becomes to

   1. <%= generate_tree_view(:id=>1) ## this will generate the <script> section and insert them in the page    2. <div id='tree'></div>

But, right now, my problem is I have to call this tree view page by ajax like this

   1. ## view file show.erb.html    2. ...    3. <%= link_to_remote 'see_tree', :url=>{:action=>'view_tree', :id=>1} >    4. <div id='view_tree'></div>    5. ...

   1. ## controller file tree_controller.rb    2. ...    3. def view_tree    4. ### get some parameters, say 'tree_parameter' array    5. render :update do |page|    6. page.replace_html 'view_tree', :partial=>'view_tree', :object=>tree_parameter    7. end    8. end    9. ...

   1. ## partial page _view_tree.erb.html    2. <%= generate_tree_view(:tree_parameter=>view_tree)    3. <div id='tree'></div>

There are no quotes around 'view_tree' above. Is that right?

Check the javascript console (ctrl+shift+J in firefox) for javascript errors that your function may be making.

Instead of RJS you could just try rendering the partial (that's just my preference - it might be easier to see what's going wrong).   render :partial => 'view_tree' ,     :locals => { :your_var => 'your val' ... } (_view_tree.html.erb) And in your link_to_remote, I think you'll need to include an :update option with the id of the element whose inner html you are updating. See the docs http://api.rubyonrails.org/classes/ActionView/Helpers/PrototypeHelper.html

Any script tag in the partial will probably be eval'd by the javascript interpreter. (The link_to_remote should generate an Ajax.Updater with the evalScripts:true option which you can check for just to make sure).

I thought this should work because it actually worked if I directly use the generate_tree_view help in the show.erb.html. But however, when the partial page is called by ajax, the <script> section which is generated by generate_tree_view didn't show up in the page. The final page only shows this:

   1. ## view file show.erb.html, saw by firebug 'cause it is the only way I can see a rendered page.    2. ...    3. <div id='view_tree'>    4. <div id='tree'></div>    5. </div>    6. ...

It seems they got executed by the javascript call instead of just display it.

So, what should I do if I just want to inject a <script> section in a rendered page.

Prototype will remove script tags whether it is set to process them or not so you will likely not see them in firebug after the update (which is probably a good thing). (I'm referring specifically to prototype's Ajax.Updater)

Also check your output in your functional tests for this controller:

  # Check the partial _element3_1.html.erb.   def test_1a     xml_http_request :post,'view_tree',params     assert_tag :tag => 'script' , :content => /alert/   end

page.replace_html maps onto prototype's Element.update function which strips script tags from what it's inserting (and due to the way it does the update I doubt it would make much difference if the scripts weren't removed).

Two ways out: a) Use link_to_remote 'Click me', :update => 'view_tree', :url => {...} and then in your controller instead of using render :update just render the partial

b) call that javascript from the render :update block ie

render :update do |page|   page.replace_html 'view_tree', :partial=>'view_tree', :object=>tree_parameter   page << "alert('hello world') end

whether you do this before or after the replace_html somewhat depends on whether the JS depends on the page having being updated or not

Fred

@Fred Thank you for reply. I just tried the two ways you suggested.

1. use link_to_remote :update in view and render :partial in controller instead of calling render :update This really does not make any differences. They gave the same output which all my staff in <script> tag got stripped.

2. directly insert the javascript in the controller. Actually, this is not allowed in my case. Because I generated a bunch of quite complicated javascript in the 'generate_tree_view' helper. And inside this helper, I also called 'remote_function' to give a javascript call to some link which will be rendered as a tree's node by the library I am using. As the remote_function can not be called in the controller, I can not generate the script inside controller. And the only way I can generate it is put 'generate_tree_view' helper in the view.

@Daniel Thank you for reply. Though I barely understand what your point is. As far as I understood, if you want to make a ajax call in rails by using link_to_remote, you either use 'link_to_remote :update' in view and use 'render :partial' in controller or use 'link_to_remote'(without update field) in view and use 'render page.replace' in controller.

But you said 'Prototype will remove script tags whether it is set to process them or not'. Does that mean I actually can not insert a dynamic <script> tag inside the rendered page by using prototype javascript call?

@Fred Thank you for reply. I just tried the two ways you suggested.

1. use link_to_remote :update in view and render :partial in controller instead of calling render :update This really does not make any differences. They gave the same output which all my staff in <script> tag got stripped.

It may strip it, but the javascript definitely runs (at least in my simple test app).

2. directly insert the javascript in the controller. Actually, this is not allowed in my case. Because I generated a bunch of quite complicated javascript in the 'generate_tree_view' helper. And inside this helper, I also called 'remote_function' to give a javascript call to some link which will be rendered as a tree's node by the library I am using. As the remote_function can not be called in the controller, I can not generate the script inside controller. And the only way I can generate it is put 'generate_tree_view' helper in the view.

the stuff inside the render :update block is not evaluated within the context of the controller. You can call helper functions in there such as remote_fuction or generate_tree_view.

Fred

@Fred

I re-checked the script code I generated and found some problem there. After fixing that problem, everything goes beautifully by using you method. Thank you very much for your suggestion.

And I really feel this is the most helpful place when I have some questions with rails. Cheers!

@Fred

@Daniel Thank you for reply. Though I barely understand what your point is. As far as I understood, if you want to make a ajax call in rails by using link_to_remote, you either use 'link_to_remote :update' in view and use 'render :partial' in controller or use 'link_to_remote'(without update field) in view and use 'render page.replace' in controller.

I was suggesting you try 'link_to_remote :update' as doing it a different way might help you to work out what the bug is. Ignore my point on RJS though - I've just got "helper-method" fatigue at the moment; comes from wanting to know and control what's going on under the covers all the time.

But you said 'Prototype will remove script tags whether it is set to process them or not'. Does that mean I actually can not insert a dynamic <script> tag inside the rendered page by using prototype javascript call?

I think you can insert script tags if you really wanted to. Maybe with prototype's Ajax.Request where you grab the response and inject it into the dom using your own handler. In firefox it will show up like a tag with a text node so not what you want unless you set it not to display. Just inserting it into the dom may not get the javascript interpreter to execute it. You might have to eval the contents of the script tag. Much easier just to use Ajax.Updater with evalScripts:true.