简体   繁体   中英

Rails association form with Ajax update

I have a model, book, with a has_many association, owns. I have it set up where a user can click a button to add the book to their collection. The controller looks as such:

def create
    @book.owns.where(user_id: current_user.id).first_or_create
    @own = Own.where(user_id: current_user.id, book_id: @book.id)
    Own.update(@own, :quantity => "1")
    respond_to do |format|
        format.html { redirect_to @book}
        format.js
    end
end

The view looks as such:

<% if user_signed_in? && current_user.owns?(@book) %>
    <%= link_to book_own_path(@book), method: :delete, remote: true, class: "btn btn-danger btn-circle", title: "Remove from collection", data: { toggle: "tooltip", disable_with: "<span class='fa fa-minus'></span>"} do %><span class="fa fa-heart-o"></span><% end %>
<% else %>
    <%= link_to book_own_path(@book), method: :post, remote: true, class: "btn btn-success btn-circle", title: "Add to collection", data: { toggle: "tooltip", disable_with: "<span class='fa fa-plus'></span>"} do %><span class="fa fa-heart"></span><% end %>
<% end %>

So that when the button is clicked, it automatically adds a quantity of 1 to their inventory. What I want to do is have the user be able to update their owned quantity from the book show view so I added this:

<% if user_signed_in? && current_user.owns?(@book) %>
    <span id="<%= @book.id %>_quantity">
        <% Own.where(:user_id => current_user.id, :book_id => @book.id).each do |z| %>
        <% if z.quantity == 1 %>
            <form>
                You own <input type="text" placeholder="<%= z.quantity %>" id="quantity" name="quantity" size="1" class="text-center" /> copies of this book
                <input type="submit" onclick="<% Own.update(@own, :quantity => params[:quantity]) %>.submit();" hidden />
            <form>
        <% else %>
            <form>
                You own <input type="text" placeholder="<%= z.quantity %>" id="quantity" name="quantity" size="1" class="text-center" /> copies of this book
                <input type="submit" onclick="<% Own.update(@own, :quantity => params[:quantity]) %>.submit();" hidden />
            <form>
        <% end %>
    <% end %>
    <br />
    </span>
<% end %>

The above DOES update the own quantity when I click enter, BUT it redirects me to the same url with ?quantity=1 (or whatever the quantity value is) added to the end. It also does not show the quantity in the input placeholder unless the page is refreshed.

My question is, is there a better way to go about this, or is there a way to actually show the placeholder value. Also, how could I set this where the form is submitted in the background instead of refreshing the page?

To build your form in Rails, you should use what Rails gives you : Rails doc for Form helpers

or gems likes SimpleForm

This example assume you are using Rails 4+.

First, you want to display your book page, so load your book and load your owns (when you manipulates models is always better to do that in your controller) :

# Books Controller
def show
  @book = Book.find(params[:id])
  @owns = current_user.owns.where(book: @book)
end

and your view, could now be something like this :

# Book show
<% @owns.each do |own| %>
  <div> You own <span class="<%= own.id %>_quantity"><%= own.quantity %></span> of this book </div>
  <%= form_for own, remote: true, method: :put do |f| %>
    <%= f.hidden_field :quantity, value: own.quantity + 1 %>
    <%= f.submit "Add One" %>
  <% end %>
  <hr>
<% end %>

We iterate through our owns and create a form which will add 1 to quantity. The request will go to Own#update because of the PUT method on the form. The form will be send via AJAX because of the attribute "remote: true" (This is one of the Rails ways to use AJAX in form : Rails doc for AJAX ).

# Own Controller
def update
  @own = Own.find(params[:id])
  respond_to do |format|
    if @own.update(own_params)
      format.html do
        redirect_to book_path(@own.book), notice: 'success'
      end
      format.js
    else
      format.html do
        redirect_to book_path(@own.book), alert: 'fail'
      end
    end
  end  
end

def own_params
  params.require(:own).permit(:quantity)
end

Here, we will load our Own and try to update it. If JS is disabled, the request will take the HTML part and will redirect where you want. If JS is enabled, then you can create a "JS view", which are rendered exactly like the html one (you have to name them after the name of the action). So, it could be something like :

# views/owns/update.js.erb
$(".<%= @own.id %>_quantity").html("<%= @own.quantity %>");

It will replace the content of the span with the new quantity.

I haven't tested it, but at the end, your code could be something like this

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM