好的,在进入解决方案之前,让我们了解request-response 循环。
当您搜索 http://stackoverflow.com 时,您会从 client(您的浏览器)向 StackOverflow(SO) server 发送请求。客户端和服务器通过HTTP 协议进行通信,如果用户请求服务器知道的内容,它会提供(发送)响应(html、css、js 文件)。浏览器知道如何显示从服务器接收到的 html 内容。每个浏览器都有自己的样式表(user-agent-stylesheet),它还应用从服务器发回的 html 页面中链接的 css 文件中的样式。请注意,这一切都同步发生,当server 正在处理客户端的请求时,浏览器选项卡是inactive,因为它正在等待服务器的响应。单击链接时会发生相同的过程。它向服务器创建一个新请求。
来自服务器的响应可以是HTML、JSON、XML 等。您可能已经注意到,synchronous 通信并不是我们一直想要的。
如果我们发出一个新的synchronous 请求,浏览器会重新获取HTML、CSS 和JS 和image 文件(不要进入缓存)。而且我们不想为每个请求更新整个页面。
通常,只有部分页面在请求后更新,它提供了良好的用户体验。
这是 Javascript 擅长的地方。它有能力异步向服务器发出请求(网页不会重新加载),还可以使用称为AJAX(Asynchronous Javascript XML) 的方式更新页面的某些部分。
典型的 AJAX 请求是这样的。您向服务器发出请求,但这次是异步的,服务器以XML 而不是HTML 响应,Javascript 解析XML 文档更新页面的一部分。尽管它现在称为 AJAX,但 JSON 用于跨服务交换信息。
所以,要发出 AJAX 请求,我们需要一个链接,当单击该链接时会发送一个 XMLHttpRequest(异步请求),服务器应以 JSON 或 XML 响应,然后脚本应解析响应并更新DOM(Document Object Model)。在Vanilla JS(纯javascript)中发出AJAX请求很复杂,人们通常使用Jquery的ajax方法来发出AJAX请求(更少的代码行)。请参阅http://api.jquery.com/jquery.ajax/ 了解更多信息。
但在rails 中,它更容易。我们可以使用UJS(Unobtrusive Javascript) 发出 AJAX 请求。让我们看看它的实际效果。
要使链接发送 AJAX 请求,您需要在 link_to 助手中设置 remote: true。这会在生成的 HTML 中添加一个data-remote=true。
例如下面的erb
<%= link_to "All books", books_path, remote: true %>
生成html
<a data-remote="true" href="/books">All books</a>
好的。现在我们都准备好发出AJAX 请求了。修改你的代码为
<div style="margin-top:50px;" class="wrapper">
<div class="optionscontainer btn-group btn-group-justified">
<%= link_to posts_path, class:"options btn btn-primary", remote: true do %>
<i class="fa fa-book optionseach" aria-hidden="true"></i>All posts
<% end %>
<%= link_to stories_path, class:"options btn btn-primary", remote: true do %>
<i class="fa fa-rss optionseach" aria-hidden="true"></i>All stories
<% end %>
<%= link_to books_path, class:"options btn btn-primary", remote: true do %>
<i class="fa fa-users optionseach" aria-hidden="true"></i>All books
<% end %>
</div>
<div id="content">
<!-- The content goes here -->
</div>
我假设您已经设置了控制器、模型和视图。同时在终端中执行rake routes 以查看您的应用程序的现有路由。您应该看到以下内容(顺序不重要)
Prefix Verb URI Pattern Controller#Action
posts GET /posts(.:format) posts#index
stories GET /stories(.:format) stories#index
books GET /books(.:format) books#index
注意:这里format对应的返回格式可以是html、js、xml或json。
posts_path 在指向posts#index 的url_helper 之一中,这意味着每当在rails 应用程序中向服务器发出请求时,它首先到达路由器并被分派到相应的controller routes.rb中指定的操作
在这种情况下,如果我们向http://localhost:3000/books 发出请求,该请求将发送到 books#index 操作。在操作中,您可以从database 获取数据并将响应发送给客户端。
由于我们对 AJAX 感兴趣并且我们指定了 remote:true,rails 将期望向客户端返回一个 JS 响应(即负责动态呈现内容的 script)。
我将解释如何处理 BooksController 的 AJAX 请求,您可以将相同的想法应用于其他控制器。(posts 和 stories)。
class BooksController < ApplicationController
def index
@books = Book.all
respond_to do |format|
format.html #looks for views/books/index.html.erb
format.js #looks for views/books/index.js.erb
end
end
#other actions
end
我们在这里所做的只是告诉控制器在客户端请求 JS 响应时呈现 index.js.erb 或在 HTML 响应的情况下呈现 index.html.erb。当我们没有指定要渲染的文件时,rails 如何知道渲染index.html.erb 或index.js.erb?这就是 Rails 受欢迎的原因。Rails 遵循Convention Over Configuration。
实际上,controller 从 action 名称推断要渲染的模板。
下一步是利用@books 来更新#content div。在添加代码渲染所有书籍之前,我们需要一个模板来渲染对吗?这就是partials的用武之地。partials是可重复使用的view' and a partial in rails is prefixed with '_'. For example:_books.html.erbis a partial forbooks`。
创建部分app/views/books/_books.html.erb
<% @books.each do |book| %>
<div class="book">
#Display the fields
</div>
<% end %>
现在创建app/views/books/index.js.erb 并添加以下内容:
$("#content").html("<%= j (render 'books') %>");
这个单行将部分_books.html.erb 渲染到#content div 中。等待。它是如何工作的?让我们把它分解成碎片。
<%= %> 中的任何内容都是 ruby 代码。它被执行并且值被替换为<%= %>。 erb 模板引擎允许您在 javascript 中编写 ruby 代码。那么,这有什么作用呢?
<%= j (render 'books') %>
它将渲染books/_books.html.erb,从参数推断为render。它返回由_books.html.erb 生成的html。
j 是做什么的?它实际上是escape_javascript 方法的别名。用于对部分_books.html.erb返回的内容进行转义。
解释escaping html 的原因将使这个答案更长。我强烈建议您在this SO 线程中阅读 kikito 的答案(第三个)。
因此,我们将 html 从部分作为字符串(注意 <%= %> 周围的引号)传递给添加到 #content div 中的 html 方法。而已!
我建议您检查服务器日志并探索开发者工具中的 Network 选项卡,以深入了解 AJAX 的工作原理。
对其他控制器(PostsController 和 StoriesController)执行相同操作。
希望这会有所帮助。