2

An update method for one of my controllers looks like this:

def update
  @node = Node.find(params[:id])
  @video = @node.media
  @node.update(node_params)
  @video.update(title: @node.name, description: @node.description)
end

I need a callback to make sure that the @node and @video updated. What's the best way to do this?

mike0416
  • 461
  • 4
  • 17
  • Do you need a specific function as the callback (e.g. to do some other task after saving) or are you just checking that the update was successful? – wspurgin Oct 20 '15 at 21:11
  • I need a specific function. My front-end UI developer has some jQuery that I need a callback for. A bit new to the AJAX stuff so let me know if you need more info. I figured I can create the conditional with UJS (update.js.erb). I can figure that out though, my main issue is I don't know how how to check for save and then perform a function. I couldn't figure out if `after_save` would work, and if so, how I would implement it. – mike0416 Oct 20 '15 at 21:14
  • I think I'll need a little bit more information. You want to return a URL with a callback that validates whether the video/node was updated? So that your UI developer can send an AJAX request to it? Or are you saying that based on whether the video and node were updated correctly you want to send a callback? – wspurgin Oct 20 '15 at 21:17
  • `after_save` is good if you need to do something with an instance of video and node *every time* you save or update a video. – wspurgin Oct 20 '15 at 21:18
  • Sorry - yeah, the second one `Or are you saying that based on whether the video and node were updated correctly you want to send a callback?` I'll want to do something every time I update the video and node from the update method. – mike0416 Oct 20 '15 at 21:20
  • I think in this case you'll want to use something like `update_attributes!` that method produces an exception if the update fails. You can rescue that exception if it fails and do something else, and if it doesn't fail then you know it succeed! – wspurgin Oct 20 '15 at 21:22

3 Answers3

1

You can test if the update succeeds...

  if @node.update(node_params)
    # handle success
    if @video.update(title: @node.name, description: @node.description)
      # etc...
    end
  else
    # handle fail
  end

In an ajax request, you can put the condition in the view:

<% if @node.errors.any? %>
  alert('<%= j(@node.errors.full_messages.join("\n")) %>');
<% else %>
  alert('Success')
<% end %>
Mark Swardstrom
  • 17,217
  • 6
  • 62
  • 70
0

Based on what you said, I think this is what you might want

def update
  @node = Node.find(params[:id])
  @video = @node.media
  @node_saved = true
  @video_saved = true
  begin
      @node.update_attirbutes!(node_params)
  rescue ActiveRecord::RecordInvalid
      @node_saved = false
  end

  begin
      @video.update_attributes!(title: @node.name, description: @node.description)
  rescue ActiveRecord::RecordInvalid
      @video_saved = false
  end

  render "mypage"
end

Inside your update.js.erb

...
if (<%= @node_saved %> === false) {
   // trigger flash message
};

if (<%= @video_saved %> === false) {
   // trigger flash message
};

I may still be misunderstanding what you want, but this or @Swards answer should answer your question.

Community
  • 1
  • 1
wspurgin
  • 2,600
  • 2
  • 17
  • 20
  • Thanks @TheSpurg. Let me take this a step further. What if I wanted to do something within `update.js.erb` instead of `my_callback`? For example, if both update correctly throw a flash message that the update was successful, but if it wasn't throw an alert that the update was unsuccessful? – mike0416 Oct 20 '15 at 21:45
  • Maybe set some variable that you'd pass to your template? That would be one approach. – wspurgin Oct 20 '15 at 21:49
  • @mike0416 made update based on your comment. Is that more of what you were looking for? – wspurgin Oct 20 '15 at 21:55
  • Thanks. I believe you're right b/t your answer and @Swards I should be able to come up with something. I'll test it out and check back in. – mike0416 Oct 20 '15 at 22:26
0

Rails' .save and .update return true/false (boolean) depending on whether the action was successful.

The standard way of utilizing that in your flow is as follows:

def update
  @node = Node.find params[:id]

  respond_to do |format|
     if @node.update(node_params)
        format.js #-> invokes /views/nodes/update.js.erb
        format.json { render json: @node.to_json }
        format.html
     else
        format.js #-> invokes /views/nodes/update.js.erb
        format.json
        format.html
     end
  end
end

You shouldn't be using the @video.update using the exact same data as @node - you can do that in the model. You'll be best using associations etc, which I can explain if required.

#app/models/node.rb
class Node < ActiveRecord::Base
   belongs_to :media #?
   before_save :set_media, on: :update

   private

   def set_media
     self.media.name = self.name
     self.media.description = self.description
   end
end

Ajax

My front-end UI developer has some jQuery that I need a callback for

Since I don't know the spec for this, I cannot give you any specific code.

What I can tell you is that if you're using ajax, you have to make sure you understand the difference between server-side & client-side code.

I don't want to insult your intelligence but I'll explain it for posterity's sake...

Frontend ajax will look like this:

#app/assets/javascripts/application.js
$(document).on("click", ".element", function(){
   $.ajax({
      url: "...",
      data: ....,
      success: function(data) { },
      error: function(response) {}
   });
});

... since this is client side code, you can only deal with data returned from the server. A common misunderstanding is many people thinking they'll somehow be able to use Ruby code/methods in this. No.

You can only send back pre-packaged data to this, so if you wanted your data to be condition, you'd do something like this:

def update
   @node = Node.find params[:id]
   @returned = "yes" if @node.update(node_params)
   render [x], layout: !request.xhr?
end 

This will allow you to send back conditional data, with which you'll be able to use the front-end JS to manipulate:

$.ajax({
 ...
 success: function(data) {
    ... do something with data
 }

--

If you wanted to use server side code, you'll be better using the inbuilt format.js functionality:

def update
   @node = Node.find params[:id]
   respond_to do |format|
      format.js #-> app/views/nodes/update.js.erb
   end
end

This does allow you to use Ruby code, as the JS is rendered server-side, and passed to the browser. I'm not sure as to the specifics, but I know you can do the following with it:

#app/views/nodes/update.js.erb
<% if @node.something %>
   $("js")....
<% else %>
   ...
<% end %>
Richard Peck
  • 76,116
  • 9
  • 93
  • 147