开发者

Partial not updated on first click

I am having problems with a remotely executed action and a partial that doesn't update the first time I click the link.

Inside the view (a partial named books) I am creating a link:

link_to "⊗", read_book_path(book), :remote => true

The read_book_path is defined in routes.rb There is also a conditional that displays a different text when that book is read.

Inside my controller, I have defined a new action:

def read
  @books = Book.all
  @book = Book.find(params[:id])
  @book.read = !@book.read
  @book.save
  respond_to do |format|
    format.html { redirect_to(books_url) }
    format.js {render :layout => false, :locals => { :book => @book } }
  end
end

This means I need a file read.js.erb, this file's con开发者_Python百科tent is:

$("#books").empty().html("<%= escape_javascript( render(:partial => "books") ) %>");

When I click the link, I can see in the terminal window that the database field is updated but the partial is not. Clicking the same link again updates the partial.

Changing the link to :remote => false also works but the page reloads (as expected).

I have tried to debug it with Safari and the Developer tools and I can see the server's response when clicking the link for the first time. Something is wrong there, the HTML generated by <%= escape_javascript( render(:partial => "books") ) %> contains the wrong HTML with the old content of the partial. Only the second or third click shows the updated HTML.

I have integrated jquery-ujs - is that the reason the partial doesn't update the first time or am I missing something else?

This really gave me a headache, can you help me?

Edit: If that helps: I created a listener in application.js to ajax:before and ajax:complete. The first one shows a little spinner, the second one hides it. When I click the link, the spinner shows but it doesn't hide.


It looks like you have an ordering problem that's causing the trouble. You're capturing a complete set of books into the @books variable and then modifying a separate copy of a single book. This change will not be propagated back.

# Load and modify the one book by flipping the flag
@book = Book.find(params[:id])
@book.read = !@book.read
@book.save

# Load all books
@books = Book.all

As a note this is an extremely inefficient way of doing things, so I hope you're not working on a large amount of data. You might find it's easier to do this by simply toggling the one field with a simple UPDATE query:

Book.update_all({ :read => true }, { :id => params[:id] })

I'm not sure why you're calling $(...).empty().html(...) instead of simply $(...).html(...) since the html() method should replace the HTML wholesale with no need to clear it in advance.

One thing that might help is using .js.rjs where the equivalent would be:

page[:books].replace_html(:partial => 'books')

With simple JavaScript, RJS allows you to eliminate a lot of the drudgery. You can use JS in RJS as well for cases where there is no equivalent:

page << '$("#books").empty()'
page[:books].replace_html(:partial => 'books')

To make this more Rails friendly, you could call your partial _book which would make the local variables redundant. Each partial has a default variable with a name matching the template name:

render(:partial => 'book', :collection => @books)
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜