Rails 5 - 在Ajax刷新后重新初始化Javascript?

时间:2017-04-20 19:05:17

标签: javascript jquery ruby-on-rails ajax ruby-on-rails-5

所以我对我涉及Javascript的问题有了更好的理解。问题在于,在AJAX调用刷新div的内容之后,该div中的内容被取消链接或未初始化为Javascript。我想要做的是让我的活动列表可以为用户排序。我使用优先级字段来保存活动的顺序,因此当用户刷新或离开页面时,则维护订单。

我收到的问题如下:

  

ActiveRecord :: RecordNotFound(无法找到带有' id' =项目的活动):

因此,不会保存活动订单。这发生在AJAX调用和刷新之后,并且它事先正常工作,保存顺序等等。我已经尝试了一些解决方案,例如one并将Javascript移到部分无法成功。

我认为上面的错误是活动没有正确链接到Javascript文件的结果,导致它的ID为" item"那么有谁知道如何解决这个问题或任何有关如何解决它的建议?谢谢!

主要观点:

<!-- home.html.erb -->
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1">

  <title>Bootstrap 101 Template</title>

</head>

<!-- the jScrollPane script -->


<!--IF THE USER IS LOGGED IN, DISPLAY THE FOLLOWING -->
<% if current_user %>
    <!--
    <style>
    .navbar {
      background-color:#2F4F4F
    }
    </style>
    -->
    <nav class="navbar navbar-default">
      <div class="container-fluid">
        <!-- Brand and toggle get grouped for better mobile display -->
        <div class="navbar-header">
          <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
            <span class="sr-only">Toggle navigation</span>
            <span class="icon-bar"></span>
            <span class="icon-bar"></span>
            <span class="icon-bar"></span>
          </button>
          <a class="navbar-brand" href='home'>TimeTracker</a>
        </div>

        <!-- Collect the nav links, forms, and other content for toggling -->
        <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
          <ul class="nav navbar-nav navbar-right">
            <li><a href="#">Signed in as <%=current_user.email %></a></li>
            <li>
              <%= link_to 'Sign Out', sign_out_path, method: :delete %>
            </li>
          </ul>
        </div><!-- /.navbar-collapse -->
      </div><!-- /.container-fluid -->
    </nav>

    <link rel="stylesheet" href="http://netdna.bootstrapcdn.com/font-awesome/4.0.3/css/font-awesome.min.css">

    <h1 align = "center">Activities</h1>

    <div id = 'activity_container'>


      <ul id="Categories" class="connectedSortable">
        <% @categories.each do |cats| %>
          <% if cats.user_id == current_user.id%>
                  <div class="panel-group">
                    <div class="panel text-left">
                      <div class="panel-heading">
                        <h4 class="panel-title">
                          <a data-toggle="collapse" href="#collapse<%= cats.id%>">
                            <%= cats.c_name %>
                          </a>
                        </h4>
                      </div>
                      <div id="collapse<%= cats.id%>" class="panel-collapse collapse">
                        <ul id="category_activities" class=".connectedSortable">
                          <% cats.activities.each do |activity| %>
                            <li class="list-group-item" >
                              <% if activity.hidden == false || activity.hidden == nil%>

                                    <label class="my_label"><%= activity.a_name %></label>

                                      <!-- Delete Activity -->
                                      <%= link_to destroy_act_path(activity.id), method: :delete, data: { confirm: "Are you sure?" } do %>
                                          <i class="fa fa-trash-o" aria-hidden="true" title="Delete"></i>
                                      <% end %>


                                        <!-- Edit activity -->
                                  <%= link_to edit_act_path(activity.id)do %>
                                      <!--<button class="editActivity" style="border:none; padding:0; background-color: transparent">-->
                                        <i class="fa fa-pencil fa-fw" aria-hidden="true" title="Edit" id="editActivity"></i>
                                      <!--</button>-->
                                  <% end %>


                                      <!-- Hide activity -->
                                  <%= link_to hide_act_path(activity.id), method: :post do %>
                                      <i class="fa fa-eye" aria-hidden="true" title="Hide" id="item"></i>
                                  <% end %>

                              <% end %>
                          <% end %>
                        </li>
                        </ul>
                      </div>
                    </div>
                  </div>
            <% end %>
        <% end %>
        </ul>


      <ul id="Activities" class="connectedSortable" >

      <!-- List each activity in database -->

        <% @activities.each do |activity| %>
          <% if (activity.hidden == false || activity.hidden == nil) && activity.category_id == nil %>
              <li class="list-group-item" id='<%=activity.id%>' style="list-style: none;">

                <!-- Display activity name -->

                <label class="my_label"><%= activity.a_name %></label>

                <!-- Delete Activity -->
                <%= link_to destroy_act_path(activity.id), method: :delete, data: { confirm: "Are you sure?" }, remote: true do %>
                    <i class="fa fa-trash-o" aria-hidden="true" title="Delete"></i>
                <% end %>

                <!-- Edit activity -->
                <%= link_to edit_act_path(activity.id)do %>
                    <button class="editActivity" style="border:none; padding:0; background-color: transparent">
                      <i class="fa fa-pencil fa-fw" aria-hidden="true" title="Edit" id="editActivity"></i>
                    </button>
                <% end %>

                <!-- Hide activity -->
                <%= link_to hide_act_path(activity.id), method: :post do %>
                    <i class="fa fa-eye" aria-hidden="true" title="Hide" id="item"></i>
                <% end %>

              </li> <!-- End of list item -->
          <% end %> <!-- End of if statement -->
      <% end %> <!-- End of activity loop -->
      </ul>


    </div>


    <ul class="pager">

      <li class="previous"><a href="#"><span aria-hidden="true">&larr;</span>Previous </a></li>
      <li class="next"><a href="#"> Next<span aria-hidden="true">&rarr;</span></a></li>

      <!-- *****************NEW*********************** -->
      <%= form_for @activity, :url => create_act_path, remote: true, data: {type: 'script'} do |a| %>
          <%= a.text_field :a_name, id: 'a_name_field', placeholder: 'Activity Name'%>
          <%= a.select :category_id, Category.all.collect { |c| [c.c_name, c.id] }, include_blank: "--No Category--" %>
          <%= a.submit 'Create', id: 'submitButton', class: 'btn btn-primary'%>
      <% end %>

      <%= form_for @category, :url => create_cat_path, remote: true do |c| %>
          <%= c.text_field :c_name, id: 'c_name_field', placeholder: 'Category Name'%>
          <%= c.submit 'Create', id: 'submitButton', class: 'btn btn-primary'%>
      <% end %>

      <!-- Button for showing all hidden items -->
      <%= link_to unhide_act_path, method: :post do %>
          <button class="showHidden" >Show Hidden</button>
      <% end %>

      <!-- Button to sort -->
      <button class="sortActivity">Sort</button>
      <button class="doneSorting">Done Sorting</button>

      <!-- ***************************************** -->

    </ul>

<!-- IF THE USER IS NOT LOGGED IN, DISPLAY THE FOLLOWING -->
<% else %>

    <nav class="navbar navbar-default">
      <div class="container-fluid">
        <div class="navbar-header">
          <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
            <span class="sr-only"></span>
            <span class="icon-bar"></span>
            <span class="icon-bar"></span>
            <span class="icon-bar"></span>
          </button>
          <a class="navbar-brand" href="#">TimeTracker</a>
        </div>

        <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
          <ul class="nav navbar-nav navbar-right">
            <li class="active">
            </li>
          </ul>
        </div>
      </div>
    </nav>

      <div class="loginContainer" align="center">
        <h1 align="center">
          <b>
            Please log in or sign up
          </b>
        </h1>

        <div class="container">
          <br>
          <%= button_to 'Login', sign_in_path, :method => 'get', class: 'btn' %>
          <br>
          <%= button_to 'Sign Up', sign_up_path, :method => 'get', class: 'btn' %>
        </div>

        <!--<div class="container" style="background-color:#D3D3D3">
          <input type="checkbox" checked="checked"> Remember me
          <span class="psw">Forgot <a align="center" href="#">password?</a></span>
        </div> -->
      </div>


<% end %>



<script type="text/javascript">
    function changeImg(img) {
        if (img.src == "<%=asset_path('_unfilledbubble.png')%>"){
            img.src = "<%=asset_path('_filledbubble.png')%>";
        }
        else {
            img.src = "<%=asset_path('_unfilledbubble.png')%>";
        }
    }
</script>
<script type="text/javascript">
    $(document).ready(function() {
        $(function () {
            $('.scroll-pane').jScrollPane({showArrows: true});
        });
    });
</script>

<script>


    $(document).ready(function(){
        function callAll(){
            set_positions = function(){
                // loop through and give each task a data-pos
                // attribute that holds its position in the DOM
                $('.list-group-item').each(function(i){
                    $(this).attr("data-pos",i+1);
                });
            }
            //    ready = function(){
            // call set_positions function
            set_positions();
            // ************NEW execept for #Activities
            $('#Activities').sortable({
                connectWith: ".connectedSortable"
            });
            $('#Activities').disableSelection();
            //        $('#Categories').sortable({
            //            connectWith: ".connectedSortable"
            //        });
            //        $('#Categories').disableSelection();
            $('#category_activities').sortable({
                connectWith: ".connectedSortable"
            });
            $('#category_activities').disableSelection();
            // *******end of NEW
            $('#Activities li').on('click','li',function (){
                var myid = $(this).attr('id');
                alert(myid);
            });
            // after the order changes
            $('#Activities').sortable().bind('sortupdate', function(e, ui) {
                // array to store new order
                var updated_order = []
                // set the updated positions
                set_positions();
                // populate the updated_order array with the new task positions
                $('#Activities li').each(function(i){
                    updated_order.push({ id: $(this).attr('id'), position: i });
                });
                // send the updated order via ajax
                $.ajax({
                    type: "PUT",
                    url: '/home/sort',
                    data: { order: updated_order }
                });
            });
        }
        $(document).ajaxComplete(callAll());
    })
</script>

部分视图:

 <!-- List each activity in database -->
  <% @activities.each do |activity| %>
      <% if (activity.hidden == false || activity.hidden == nil) && activity.category_id == nil %>
          <li class="list-group-item" id="item" data-id="<%activity.id%>" style="list-style: none;">


            <!-- Display activity name -->

            <label class="my_label"><%= activity.a_name %></label>


            <!-- Delete Activity -->
            <%= link_to destroy_act_path(activity.id), method: :delete, data: { confirm: "Are you sure?" }, remote: true do %>
                <i class="fa fa-trash-o" aria-hidden="true" title="Delete"></i>
            <% end %>

            <!-- Edit activity -->
            <%= link_to edit_act_path(activity.id)do %>
                <button class="editActivity" style="border:none; padding:0; background-color: transparent">
                  <i class="fa fa-pencil fa-fw" aria-hidden="true" title="Edit" id="editActivity"></i>
                </button>
            <% end %>

            <!-- Hide activity -->
            <%= link_to activity, method: :post, :controller => :activities, :action => :set_hidden_true, remote: true do %>
                <button class="hideActivity" style="border:none; padding:0; background-color: transparent">
                  <i class="fa fa-eye" aria-hidden="true" title="Hide" id="item"></i>
                </button>
            <% end %>

          </li> <!-- End of list item -->
      <% end %> <!-- End of if statement -->
  <% end %> <!-- End of activity loop -->

创建活动JS文件:

<!--create_activity.js.erb-->
$('#Activities').html("<%= j (render 'object') %>");

家庭控制器:

class HomeController < ApplicationController
  respond_to :html, :js

  def new

  end

  def index

  end

  def home
    @activities = Activity.all
    @activity = Activity.new

    @categories = Category.all
    @category = Category.new
  end

  def create_activity
    @activity = Activity.create(activity_params)
    @activity.user_id = current_user.id
    @activity.priority = @activity.id

    @object = Category.all

    @activities = Activity.all
    @categories = Category.all

    if @activity.save
      flash[:success] = 'Activity created successfully'
    else
      flash[:notice] ='ERROR: Activity could not be create'
    end
  end

  def create_category
    @category = Category.new(category_params)
    @category.user_id = current_user.id

    #@category.priority = @category.id

    @object = Category.all
    @activities = Activity.all
    @categories = Category.all

    #@category.priority = @category.id
    if @category.save!
      flash[:success] = 'Category created successfully!'
    else
      flash[:error] = 'ERROR: Category was not saved!'
    end
  end

  def destroy_activity
    @activity = Activity.find(params[:id])
    @activity.destroy

    @object = Category.all

    @activities = Activity.all
    @categories = Category.all
  end

  def welcome
  end


  def hide_activity

    @object = Category.all

    @activities = Activity.all
    @categories = Category.all

    @activity = Activity.find(params[:id])
    @activity.update_attribute(:hidden, true)
    respond_to do |format|
      format.html {redirect_to activities_url}
      format.js
    end
  end

  #NEW 4/15
  def edit_activity
    @activity = Activity.find(params[:id])
  end

  def update_activity
    @activity = Activity.find(params[:id])

    if @activity.update_attributes(activity_params)
      flash[:success] = 'Activity updated successfully!'
    else
      flash[:notice] = 'Activity was not updated'
    end
  end

  def unhide_all

    @object = Category.all

    @activities = Activity.all
    @categories = Category.all

    @activities = Activity.all
    @activities.update_all(hidden: false)
    # redirect_to root_path
  end

  def sort
    params[:order].each do |key, value|
      Activity.find(value[:id]).update_attribute(:priority, value[:position])
    end
    render :nothing => true
  end

  private

  def activity_params
    params.require(:activity).permit(:a_name, :category_id)
  end

  def category_params
    params.require(:category).permit(:c_name)
  end
end

更新

我尝试了following并作为测试,用以下内容替换了html中的脚本:

    $('#Activities').on('click','li',function (){
        var myid = $(this).attr('id');
        alert(myid);
    });

在Ajax之前,当我点击一个活动时,它正确地给了我它的id。但是,在进行Ajax调用之后,当我尝试点击某个活动时,我得到了相同的错误,声称它无法找到一个活动&#34; id = item&#34;

2 个答案:

答案 0 :(得分:0)

这里有很多选项,一个选项是将绑定更改为文档或正文,而不是直接更改为元素的id,因为这是以dinamically方式创建的,例如:

$("body").on('click',"#Activities" ,function () {
   var myid = $(this).attr('id');
   alert(myid);
});

您可以在呈现不推荐的元素时再次初始化ajax请求上的事件

或使用像https://github.com/adampietrasiak/jquery.initialize这样的插件,如果将它绑定到类,ID或其他选择器,可以重新插入插件。

答案 1 :(得分:0)

我已经创建了一个示例,您可以使用AJAX请求从索引页面创建Person。创建Person后,将people的新列表发送到索引页面。为了模拟你的JavaScript,我使用jQuery将每个元素的背景颜色设置为蓝色。

让我们从index.html.erb本身开始。

<!-- app/views/people/index.html.erb -->
<p id="notice"><%= notice %></p>

<h1>People</h1>

<table>
  <thead>
    <tr>
      <th>First name</th>
      <th>Last name</th>
      <th colspan="3"></th>
    </tr>
  </thead>

  <tbody id="people_tbody">
    <%= render 'people', people: @people %>
  </tbody>
</table>

<%= render('form', person: @people.new) %>

接下来是两个部分_people.html.erb

<!-- app/views/people/_people.html.erb -->
<% people.each do |person| %>
    <tr>
        <td><%= person.first_name %></td>
        <td><%= person.last_name %></td>
        <td><%= link_to 'Show', person %></td>
        <td><%= link_to 'Edit', edit_person_path(person) %></td>
        <td><%= link_to 'Destroy', person, method: :delete, data: { confirm: 'Are you sure?' } %></td>
    </tr>
<% end %>

_form.html.erb 从中解雇AJAX请求

<!-- app/views/people/_form.html.erb -->
<%= form_for(person, remote: true) do |f| %> <!-- remote true, makes it an AJAX call -->
  <% if person.errors.any? %>
    <div id="error_explanation">
      <h2><%= pluralize(person.errors.count, "error") %> prohibited this person from being saved:</h2>

      <ul>
      <% person.errors.full_messages.each do |message| %>
        <li><%= message %></li>
      <% end %>
      </ul>
    </div>
  <% end %>

  <div class="field">
    <%= f.label :first_name %>
    <%= f.text_field :first_name %>
  </div>

  <div class="field">
    <%= f.label :last_name %>
    <%= f.text_field :last_name %>
  </div>

  <div class="actions">
    <%= f.submit %> <!-- when submit is clicked the AJAX request is fired to PeopleController#create -->
  </div>
<% end %>

要模拟您的JavaScript问题,我有以下文件:

// app/assets/javascripts/people.js
function init_people() {
    $('#people_tbody tr').css('background-color', 'lightblue')
}

$(document).ready(init_people)

这为所有行提供浅蓝色背景。

正如您所看到的,我使用AJAX发送填写的表单,因为设置了remote: true。如果表单已提交,请求将以PeopleController#create的形式到达,如下所示:

# app/controllers/people_controller.rb
class PeopleController < ApplicationController
  before_action :set_person, only: [:show, :edit, :update, :destroy]

  # GET /people
  # GET /people.json
  def index
    @people = Person.all
  end

  # GET /people/1
  # GET /people/1.json
  def show
  end

  # GET /people/new
  def new
    @person = Person.new
  end

  # GET /people/1/edit
  def edit
  end

  # POST /people
  # POST /people.json
  def create # AJAX request arrives here
    @person = Person.new(person_params)

    respond_to do |format|
      if @person.save
        format.html { redirect_to @person, notice: 'Person was successfully created.' }
        format.json { render :show, status: :created, location: @person }
        # I added the underlying line to handle the AJAX response.
        format.js { render :index, status: :created, location: @person }
      else
        format.html { render :new }
        format.json { render json: @person.errors, status: :unprocessable_entity }
        # I added the underlying line to handle the AJAX response.
        format.js   { render nothing: true }
      end
    end
  end

  # PATCH/PUT /people/1
  # PATCH/PUT /people/1.json
  def update
    respond_to do |format|
      if @person.update(person_params)
        format.html { redirect_to @person, notice: 'Person was successfully updated.' }
        format.json { render :show, status: :ok, location: @person }
      else
        format.html { render :edit }
        format.json { render json: @person.errors, status: :unprocessable_entity }
      end
    end
  end

  # DELETE /people/1
  # DELETE /people/1.json
  def destroy
    @person.destroy
    respond_to do |format|
      format.html { redirect_to people_url, notice: 'Person was successfully destroyed.' }
      format.json { head :no_content }
    end
  end

  private
    # Use callbacks to share common setup or constraints between actions.
    def set_person
      @person = Person.find(params[:id])
    end

    # Never trust parameters from the scary internet, only allow the white list through.
    def person_params
      params.require(:person).permit(:first_name, :last_name)
    end
end

正如您所看到的,我添加了两行来处理js中的PeopleController#create格式。如果渲染来自format.js { ... } rails知道选择index.js.erb文件,而不是index.html.erb文件。如果您不想要此行为,则可以指定特定文件。

我的index.js.erb看起来像这样:

// app/views/people/index.js.erb
$('#people_tbody').html("<%= j(render('people', people: Person.all)) %>")

这会将#people_tbody的内容替换为新设置的op人(与您的示例类似)。但现在我遇到了和你一样的问题。 JavaScript不再被触发,每行的背景都是白色的。要在新加载的内容上触发JavaScript,我只需要调用init函数。 这意味着在文档准备好之后以及应该将AJAX请求提取到单独的函数之后要应用的任何逻辑。我将我的逻辑放在函数init_people()中。

这意味着index.js.erb应如下所示:

// app/views/people/index.js.erb
$('#people_tbody').html("<%= j(render('people', people: Person.all)) %>")
init_people()

现在,在加载AJAX内容之后,触发init_people()函数为新加载的内容设置JavaScript。