Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Invocation of callback function after update or replace #17

Open
dre3k opened this issue Mar 5, 2011 · 17 comments
Open

Invocation of callback function after update or replace #17

dre3k opened this issue Mar 5, 2011 · 17 comments
Labels

Comments

@dre3k
Copy link

dre3k commented Mar 5, 2011

Hi Nick.

What is our opinion on making possible to specify a callback function(or array of functions) that will be invoked after update or replace.

I'll try to explain. I use coffeescript and I don't embed javascript within haml view. I have application.coffee that bind events to ajax requests. One way to call a function after update is to hook a callback function on ajax complete event. For example in Peter's Guide when new tweet is added tweets list updated and it's necessary to make tweets draggble again. The same is necessary when tweet is dumped and tweet list updated. I think instead of specifying one callback for tweet add and one more the same callback for tweet delete, it's reasonable to let update(and replace) method accept something like :callback option, that can specify function that will be invoked when update is finished. Then it's probably will be useful to specify arguments that passed to this function(s), and maybe make conventional case on :callback => true parameter.

I just started going through Peter's Guide and I have quick&dirty solution, but its will be enough to catch the idea:
# config/initializers/apotomo.rb

Apotomo::Widget.class_eval do
  def update_with_callback(*args)
    update(*args) + callback_function(*args)
  end

  def replace_with_callback(*args)
    replace(*args) + callback_function(*args)
  end

private
  def callback_function(*args)
    func_name = ((arg = args.last).instance_of?(Hash) && arg[:callback]) ||
      self.class.name.chomp('Widget').underscore

    "((typeof(Rails.Callbacks.#{func_name}) == 'function') && \
      Rails.Callbacks.#{func_name}('#{widget_id}'));"
 end
end
@apotonick
Copy link
Owner

Do you mean a :callback parameter for render/replace/update that has the same behaviour as in a Rails controller?

@dre3k
Copy link
Author

dre3k commented Mar 5, 2011

Maybe it's better to name it "after update finalizers". I mean function that will be executed on client side after update/replace completed, like making tweets draggable again after tweets list got updated.

@apotonick
Copy link
Owner

Isn't that integrated in rails.js already, the before: and after: stuff which invokes a function after the AJAX request returns?!

@dre3k
Copy link
Author

dre3k commented Mar 5, 2011

Yes, I can specify callback on ajax success when I add new tweet, then I need to specify another callback on ajax success when I delete tweet. And this callbacks will be the same. What I wanted is to add code to respond from server that will call this function automatically on client side. It's maybe a bad idea and it's maybe violates some OOP separation principles, I'm not sure. I just did't want to specify the same callback in many different places.

@apotonick
Copy link
Owner

If you're really talking about the :callback mechanics from Rails (used in JSON-P) you hit the nail - that's something we definitely need. Do you have a link where that stuff is explained?

@dre3k
Copy link
Author

dre3k commented Mar 5, 2011

I'm not sure what the right way to implement that kind of functionality. I kind of asked for your opinion and advise.

I just find out about apotomo a couple days ago and I was going through Peter's Guide. But I was doing lessons by lesson in little bit different way. I didn't use :javascript part in haml views. So I have only markup in haml views. And in application.coffee(that gets compiled to application.js) I have all bindings, like bind on submit and bind on drop even. First in application.coffee I just had callbacks defined on ajax success on submit and drop event. Than I realized that this callbacks doing the exactly same thing. They make tweet list items draggable again after tweets list gets updated. So I'v extracted this functionality and put it in separate function named twitter under special namespace(in my case this is Rails.Callbacks). Then I'v made another instance method in Apotomo::Widget called #update_with_callback. This method use standard #update but appends string of javascript code that essentially calls the function of interest on client side. I don't define function itself, it's already defined in application.coffee(application.js). U can see definition of #update_with_callback in my original message.

For example response on submit or delete is something like this:
$("#twitter").html("...");
((typeof(Rails.Callbacks.twitter) == 'function') &&
Rails.Callbacks.twitter('twitter'));
The name of the function to call is value of :callback option or if :callback omitted then by my convention it's derived from widget class name, the argument passed to this function is widget_id.
Another example is when I click on heart image response is something like:
$("#tweet-76").replaceWith("...");
((typeof(Rails.Callbacks.tweet) == 'function') &&
Rails.Callbacks.tweet('tweet-76'));

So main idea is that I don't pass function definition within response, I just append js code necessary to invoke already defined function.

@bobanj
Copy link

bobanj commented Mar 6, 2011

I was having the same issue...using :callback => params[:callback]. Apotomo's render method is unaware of the :callback param, but including Rack::JSONP does the trick. Apotomo should not care about handling callbacks, it's middlewares job by my opinion.
Apotomo's render method is not the same with rails render method.

@dre3k
Copy link
Author

dre3k commented Mar 7, 2011

Hi Boban,

I got Rack::JSONP hooked up and callback works fine with regular controller #render.
But I can't make Rack::JSONP wrap response from Apotomo's #render. I'v read your post http://www.boban.jovanoski.net/posts/2-standalone-widgets and there your are using this:
def update
text = params[:callback] + '(' + PostLink.all.to_json + ')'
render :text => text, :content_type => 'application/json'
end
But could you please explain more how you managed Apotomo's #render and Rack::JSONP to work together.

@bobanj
Copy link

bobanj commented Mar 7, 2011

Hello dre3k
We have talked about this issue and long story short:
For now use the same sucky way as I've done rendering json with apotomo :S
and will be fixed in future version.

@jasonpvp
Copy link

What about changing wrap_in_javascript_for to:

def wrap_in_javascript_for(mode, *args)
  selector  = args.first.is_a?(String) ? args.shift : false

  if args.first.is_a?(Hash) && args.first[:after_update]
    execute=args.first[:after_update]
    args.first.delete(:after_update)
  end
  content   = render(*args) 

  response = selector ? 
    Apotomo.js_generator.send(mode, selector, content) :    # replace(:twitter)
    Apotomo.js_generator.send("#{mode}_id", name, content)  # replace_id(:twitter)

  return response+execute.to_s

end

@jarinudom
Copy link

Hey, so I'm a little confused by this. Is there a way to send a JSONP callback and then also execute whatever Apotomo was going to do right out of the box?

For example, I have an image uploader that uploads and then re-renders the gallery, and it works fine. Now, I want to call a Javascript function (to display an alert or something) and then have it do the same thing as before. Is this where I should be looking, or is there a better way to do it?

@apotonick
Copy link
Owner

@jarinudom I guess you want something like the following code to be sent back to the browser, after the upload:

* jQuery code to re-render gallery
* jQuery code to display an alert

Does that really require JSONP? Or could that be handled using two widget states getting triggered by the same event?

@jarinudom
Copy link

Oh, I can do that with two widget states? Essentially I just need to call function and pass in a message from the Apotomo controller (something like "Photo uploaded").

Sorry if this is super easy, I'm super new to Apotomo.

@apotonick
Copy link
Owner

Sure, you can even do that in the same state, just concat what you want it to send back.

replace view: :whatever + "alert(..);"

@jarinudom
Copy link

Oh excellent, thanks! Looks like I will make my deadline after all ;)

@apotonick
Copy link
Owner

Feel free to bug us on #cells on IRC (especially if there's a deadline)!

@jarinudom
Copy link

Ok just for anyone reading this, it was actually this:

replace(view: :whatever) + "alert('Message!');"

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

5 participants