Rails 5 - Ajax 刷新后重新初始化 Javascript?
Posted
技术标签:
【中文标题】Rails 5 - Ajax 刷新后重新初始化 Javascript?【英文标题】:Rails 5 - Reinitializing Javascript after Ajax refresh? 【发布时间】:2017-09-17 13:56:31 【问题描述】:所以我对涉及 javascript 的问题有了更好的理解。问题在于,在 AJAX 调用刷新 div 的内容之后,该 div 中的内容未链接或未针对 Javascript 初始化。我想要做的是让我的活动列表可以为用户排序。我使用优先级字段来保存活动的顺序,因此当用户刷新或离开页面时,顺序保持不变。
我收到的问题如下:
ActiveRecord::RecordNotFound(找不到 'id'=item 的活动):
因此,活动订单将不会被保存。这发生在 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">←</span>Previous </a></li>
<li class="next"><a href="#"> Next<span aria-hidden="true">→</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 -->
创建 Activity 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 调用之后,当我尝试单击一个活动时,我得到了同样的错误,它声称找不到具有“id=item”的活动
【问题讨论】:
乍一看,我的猜测是$(document).ajaxComplete(callAll());
修复了一些与活动相关的内容。这意味着$('#Activities').html("<%= j (render 'object') %>");
后面应该跟callAll();
。如果这触发了很多 JavaScript,您应该提取修复活动 JavaScript 相关内容所需的部分到它自己的函数,并在最后调用该函数。 (在你替换了$('#activities').html(...)
之后)。
这个问题可以通过明确指定 ajax 请求的触发位置来进一步解决。所以我们知道要遵循什么路径。
@JohanWentholt 为迟到的回复道歉。截至目前,我试图简单地让创建函数使用 AJAX,这样用户就不需要刷新整个页面,但是在 AJAX 调用之后,这就是我的问题发生的地方。
【参考方案1】:
这里有很多选项,一个选项是更改绑定到文档或正文,而不是直接绑定到元素的 id,因为这是动态创建的,例如:
$("body").on('click',"#Activities" ,function ()
var myid = $(this).attr('id');
alert(myid);
);
你可以在渲染不推荐的元素时再次初始化ajax请求上的事件
或使用https://github.com/adampietrasiak/jquery.initialize 之类的插件,如果您将其绑定到类、id 或其他选择器,则可以重新初始化插件。
【讨论】:
【参考方案2】:我创建了一个示例,您可以在其中使用 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
如您所见,我添加了两行来处理PeopleController#create
中的js
格式。如果渲染来自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
的内容替换为新的集合操作人员(类似于您的示例)。但是现在我和你有同样的问题。 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。
【讨论】:
以上是关于Rails 5 - Ajax 刷新后重新初始化 Javascript?的主要内容,如果未能解决你的问题,请参考以下文章