Topic: Update List After Create

While I find my confidence in Rails growing daily, I am pretty moronic when it comes to AJAX stuff. Its one of those things I keep putting off.

As you can tell by the thread title, I am trying to do something basic. I keep finding a lot of outdated tutorials, so hopefully you RJS wizards can enlighten me:

# View Index
%table#things
  %tr
    %th Name
    %th URL
    %th{:colspan=>'2'} Actions
  = render :partial => 'thing', :collection => @things

# _thing partial
%tr
  %td= thing.name


Now instead of rendering an RJS template, I would like to have the page update stuff happen in the controller but am using RESTful actions with respond_to blocks, so that kind of throws me off as to where to stick that code.

Re: Update List After Create

Add a tbody and give it an ID - <tbody id="my_table_data"> and then you can just replace_html inside this using RJS.

page["my_table_data"].replace_html :partial => "thing"

Re: Update List After Create

I am still lost on how to specify this in my controller action. Its your typical REST deal, so where should the page block go?

def create
  @thing = Thing.new(params[:thing])

  respond_to do |format|
    if @thing.save
      format.html { redirect_to things_path }
      format.xml  { head :created, :location => thing_url(@thing) }
    else
      format.html { render :action => "new" }
      format.xml  { render :xml => @thing.errors.to_xml }
    end
  end
end

Re: Update List After Create

the answer is called render :udpate
it basically accepts a block with a page object, witgh which you can do the same stuff as in an RJS template.

def create
  @thing = Thing.new(params[:thing])

  respond_to do |format|
    if @thing.save
      format.html { redirect_to things_path }
      format.xml  { head :created, :location => thing_url(@thing) }
      format.js { render :update do |page|
                    page[:some_id].replace_html render :partial => "thing", :locals => { :thing => @thing }
                  end
      }
    else
      format.html { render :action => "new" }
      format.xml  { render :xml => @thing.errors.to_xml }
      format.js { render :udpate do |page|
                    page[:error_div].replace_html "Save failed"
                  end
      }
    end
  end
end

Last edited by Duplex (2007-08-27 19:18:50)

Re: Update List After Create

Yeh - as Duplex says, sorry, I wasn't very detailed in my reply.

Re: Update List After Create

Thanks for the assist Duplex..I'm almost there

Just need to set up the form that triggers this action. Perhaps a remote_form_for will do the trick, but I'm not sure.

Re: Update List After Create

remote_form_for should work fine

Re: Update List After Create

just do NOT define the :update option of remote_form_for, otherwise RJS/render(:update) won't work properly.

Re: Update List After Create

Thanks for the pointers guys.

I really like how this fits into the respond_to block..super clean.

For cases when the :update is a little more involved, is it possible to use this convention with an external RJS file?

In the meantime I am tinkering with a progress indicator for the impatient.

I know I can do this with

:loading => "Element.show('progress')",
:complete => "Element.hide('progress')"

I'm just wondering if this belongs in my remote form or in the controller action.

Re: Update List After Create

the Progress indictor belongs in your view obviously. it displays/hides the indicator with Javascript, so the controller is in no way involved in this.

for the first question, do you ask if you can use render :update in conjunction with a RJS template? no, at least not that i know of.
either you do

format.js { render :update do |page| ..... }

or you do
format.js

and have an .rjs file named like your action in your views directory

It's either..or

Re: Update List After Create

I'm 99% there. The indicator shows when I press the submit button but the page doesn't update and I get a strange error

#The Form

- remote_form_for(:thing,
:loading => "Element.show('ajax_progress')",
:complete => "Element.hide('ajax_progress')",
:url => things_path) do |f|
...

#The Table

%table
  %thead
    %tr     
      %th Name
  %tbody#things
    = render :partial => "thing", :collection => @things
    %tr
      %td#ajax_progress{:style=>'display:none'}= image_tag('/img/ajax_progress.gif')
       
#The Controller Block
format.js { render :update do |page|
              page[:things].insert_html render :partial => "thing", :locals => { :thing => @thing }                       
            end
}
       
#The Error

RJS error: TypeError: Value undefined
(result of expression $("things").insertHtml)
is not object.

$("things").insertHtml("<tr>\n  <td><strong>fds</strong></td>\n  <td>hg</td>\n  <td>\n
<a class=\"edit\" href=\"#\" onclick=\"new Ajax.Request('', {asynchronous:true, evalScripts:true});
return false;\">Edit</a>\n  </td>\n  <td>\n    <a href=\"/things/33\" onclick=\"if (confirm('Are you sure?'))
{ var f = document.createElement('form'); f.style.display = 'none'; this.parentNode.appendChild(f); f.method = 'POST';
f.action = this.href;var m = document.createElement('input'); m.setAttribute('type', 'hidden'); m.setAttribute('name',
'_method'); m.setAttribute('value', 'delete'); f.appendChild(m);f.submit(); };return false;\">Delete</a>\n
</td>\n</tr>\n");

Re: Update List After Create

You use "insert_html", which will append (or prepend) html to the code that is already inside the given tag/DOM-id. but therefore you have to define where to put it:

format.js { render :update do |page|              
              page[:things].insert_html :bottom, render :partial => "thing", :locals => { :thing => @thing }                                   
            end
}

And then there is _replace_html, which will replace the whole content of tbody#things with the returned partial. here you don't have to define :bottom or sth. obviously.

Which one should it be? i guess insert_html als you return one item and want to append it to the list hm?

Last edited by Duplex (2007-08-28 11:02:08)

Re: Update List After Create

Sorry.. I should have explained myself better.

I want to append a row to that table, instead of refreshing the whole thing

Re: Update List After Create

So I tried this with the :bottom specified and the action does create the instance in the database, but it fails to append the partial to the tbody tag

I get a similar error that pops up in an alert message

$("things").insertHtml("bottom", "<tr>\n  <td><strong>dd</strong></td>\n  <td>fff</td>\n  <td>\n
<a class=\"edit\" href=\"#\" onclick=\"new Ajax.Request('', {asynchronous:true, evalScripts:true}); return false;\">Edit</a>\n  </td>\n
<td>\n    <a href=\"/things/34\" onclick=\"if (confirm('Are you sure?')) { var f = document.createElement('form');
f.style.display = 'none'; this.parentNode.appendChild(f); f.method = 'POST'; f.action = this.href;var m = document.createElement('input');
m.setAttribute('type', 'hidden'); m.setAttribute('name', '_method'); m.setAttribute('value', 'delete'); f.appendChild(m);f.submit(); };return
false;\">Delete</a>\n  </td>\n</tr>\n");

Re: Update List After Create

the Javascript seems correct. it just needs to be run.

It pops up in an alert message? Are you using some JavaScript debugger?
Do you have the :update option specified in the form_remote_tag ? (You shouldn't).

Re: Update List After Create

I pastied everything to keep the clutter out of this thread

I'm not using any JS debugger (at least to my knowledge).

Totally perplexed here

Re: Update List After Create

You have spelt :update wrong on line 15 of research_link_controller.rb in your pastie. Actually, duplex typed it wrong and you copied it wink

(Although, thats not your problem here)

Re: Update List After Create

Well thats just embarrassing!

All the same, I fixed the typo and still get those errors

SOmething tells me this thread will go on for another 2 pages till I get this right..

doh

Re: Update List After Create

For the love of God, I finally figured it out.

It turns out Duplex' syntax was a bit off.

He had:

page[:things].insert_html :bottom, 'things', render(:partial #etc etc....

When it should be

page.insert_html :bottom, 'things', render(:partial #etc etc....

Thanks regardless for the lesson on RJS fellas. Between you and the API, I think I may have this down.

Now I am going to try something similar with the delete action.

Wish me luck!

Re: Update List After Create

hm... so insert_html does'nt work with page[:dom_id] style ... nice to know. (never tried it myself wink)