(GoRails)在导航栏增加自动的搜索功能(jquery插件:easyautocomplete)(gem 'ransack' 搜索对象4000✨)
Global Autocomplete Search
需要用到一个JQuery插件和一个搜索对象的gem
EasyAutocomplete jQuery插件:
https://github.com/pawelczak/EasyAutocomplete
http://easyautocomplete.com/guide#sec-data-file
功能很强大,具体的看guide,写的很详细。
gem 'ransack'(4000✨)
一个面向对象的搜索:https://github.com/activerecord-hackery/ransack
我的app:见imac电脑 ~/自我练习/embeddable_comments ⮀ ⭠ autoquery ⮀
git上: https://github.com/chentianwei411/embeddable_comments
1. 激活一个Ajax request
2.server查询,把查询结果放入一个JSON对象,然后返回到浏览器。
3.JS库EasyAutocomplete将得到这些结果并显示它们。
第一步添加<script>
1.在<head>添加
<!-- Using jQuery with a CDN 加上jquery文件 --> <script src="//code.jquery.com/jquery-1.11.2.min.js"></script>
2.把文件放入assets/javascripts和stylesheets
官网下载,http://easyautocomplete.com/download
- 把easy-autocomplete.css和easy-autocomplete.themes.css放入Rails app的 app/asset/stylesheets文件夹。
- 把jquery.easy-autocomplete.js放入javascript文件夹。
- 在application.js中加上//= require jquery.easy-autocomplete
- 在application.scss中加上*= require easy-autocomplete和*=require easy-autocomplete.thems
⚠️ 我使用unprocessed sass file。
#在assets/stylesheets/application.scss, 添加: @import "easy-autocomplete";
3. 在浏览器的console上试试:
看文档:http://easyautocomplete.com/guide#sec-data-file
先要在web page上添加一个<input id="basics">
然后在console上输入(图):
在网页山的input标签上输入任意字符都会出现options的data列表:
⚠️在rails console遇到一个❌:
Refused to load the script 'http://code.jquery.com/jquery-1.11.2.min.js'
because it violates the following Content Security Policy directive:
"script-src 'self' https: 'unsafe-eval'".
这是因为我使用vue.js后根据推荐的步骤加上了下面的代码:
Rails.application.config.content_security_policy do |policy| if Rails.env.development? policy.script_src :self, :https, :unsafe_eval, :unsafe_inline else policy.script_src :self, :https end end
第二步: 添加路径。
get :search, controller: :main //或者 get 'search', to: "main#search"
输入rails routes可查看
建立一个main_controller.rb
class MainController < ApplicationController def index end def search render json: {movies: [], directors: []} end end
在浏览器输入localhost:3000/search
渲染JSON:
第三步,设置controller
添加gem 'ransack'(4000✨)
一个面向对象的搜索:https://github.com/activerecord-hackery/ransack
支持Rails5.2
任何增强搜索的gem都可以,还可以使用searchkick(4600🌟)
增加2个table:
rails g model director name
rails g model movie name director:references
rails db:migrate
#添加seed:
# 这里使用了Array#zip方法,即把几个数组压缩。
# https://github.com/gorails-screencasts/gorails-episode-192/blob/master/db/seeds.rb
rails db:seed
#添加路径routes.rb
resources :movies
resources :directors
class MainController < ApplicationController ... def search @movies = Movie.ransack(params[:q]).result(distinct: true) @directors = Directors.ransack(params[:q]).result(distinct: true)
//可以限制条数.limit(5) end end
新建views/main/search.json.jbuilder
json.movies do json.array!(@movies) do |movie| json.name movie.name json.url movie_path(movie) end end json.directors do json.array!(@directors) do |director| json.name director.name json.url director_path(director) end end
添加一个回调before_action :force_json, only: :search
这样浏览器输入的url后缀会被转化为.json
class MainController < ApplicationController before_action :force_json, only: :search ...略... private def force_json request.format = :json end end
class MainController < ApplicationController ... def search @movies = Movie.ransack(params[:q]).result(distinct: true) @directors = Directors.ransack(params[:q]).result(distinct: true).limit(5) //name_cont限制查询的key是name end end
此时在浏览器输入参数:http://localhost:3000/search?q=Wall
不会成功显示搜索结果,需要提供明确的column_name和predicate谓语:
这里使用:
- name_cont: 名字中包括。。。
- name_start 开始的名字是。。。
@movies = Movie.ransack(name_cont: params[:q]).result(distinct: true).limit(5) @directors = Director.ransack(name_start: params[:q]).result(distinct: true).limit(5)
简单解释:(具体看git)
1. ransack()用于查询数据库表:Movie
例子: Movie.ransack(name: "dent"):
Ransack::Search<class: Movie,
base: Grouping <conditions: [Condition <attributes: ["name"], predicate: cont, values: ["dent"]>], combinator: and>>
2. result()用于从得到查询结果,所有的匹配记录。
#<ActiveRecord::Relation [#<Movie id: 5, name: "Resident Evil: The Final Chapter", director_id: 5, created_at: "2018-11-22 02:43:18", updated_at: "2018-11-22 02:43:18">,
#<Movie id: 25, name: "Resident Evil: The Final Chapter", director_id: 25, created_at: "2018-11-22 02:43:18", updated_at: "2018-11-22 02:43:18">,
...略]
>
导航栏中的搜索框:
第四步:
修改controller: 根据url的格式来使用不同的模版
- http://localhost:3000/search.json
- http://localhost:3000/search.html
新增asset/javascripts/search.js,加上对搜索框的事件的操作
document.addEventListener("turbolinks:load" ,function() { //取导航条的搜索框input元素。 //input = document.querySelector("[data-behavior='autocomplete']") //因为要使用jQuery的事件, 所以必须用jQuery对象。 $input = $("[data-behavior='autocomplete']")
//指定参数1. getValue。根据对象中的key得到value。
//指定参数2. url。根据input phrase得到json数据;
//这里url是一个函数,接收phrase并发送它到一个api.
//从service返回的Response被转化为list. var options = {
getValue: "name", url: function(phrase) { return "/search.json?q=" + phrase;
//return "/search?q=" + phrase + "&format=json"; } categories: [ {
//listLocation.当响应的数据非常复杂时,可以指定list的位置。 listLocation: "movies" header: "<strong>Movies</strong>", }, { listLocation: "directors" header: "<strong>Directors</strong>", }
], list: { //easyautocomplete库的event
//这个插件提供的3个行为function函数:getSelectedItemData() onChooseEvent: function() { var url = $input.getSelectedItemData().url $input.val("") Turbolinks,visit(url) //使用Turbolinks.vist(location)发出请求并处理响应。 },
//可以添加动画函数,具体往下看👀。 },
//延迟发送请求,避免每个字母都发送请求。
requestDelay: 500 } $input.easyAutocomplete(options) })
附加:
autocomplete plugin:
Templates:
可以让你修改view的suggestions list建议列表。
不同的可视风格,包括输入框的形状,颜色。这些都可以自己客制!
还可以附加小的图片:http://easyautocomplete.com/
Animation
插件提供了简单的动画设置,如fade, silde.等视觉效果!
list: { showAnimation: { type: "fade", //normal|slide|fade time: 400, callback: function() {} }, hideAnimation: { type: "slide", //normal|slide|fade time: 400, callback: function() {} } }
其他辅助功能:
- 完全匹配match,
- 排序sort,
- 显示list的数量,
- 延迟请求的数据requestDelay: 500✅
- 这用于当准备ajax response data接收大量resources时。
- 可以避免发送多重的请求数据。当用户打字非常快时,需要一个延迟,来避免每个字母都发送一个请求。
附加:
如果使用ransack.
参考git.
在首页root添加一个搜索框,并显示搜索到的movies名字和url。
在controller, 添加
class HomeController < ApplicationController def index @q = Movie.ransack(params[:q]) @movies = @q.result(distinct: true) end end
在index.html.erb
- 使用helper方法search_form_for: 本例子需要提供url
- 使用helper方法sort_link()。sort_link有多种设置,根据需要看git文档
<%= search_form_for @q, url: root_path do |f| %> <%= f.label :name_cont %> <%= f.search_field :name_cont%> <%= f.submit %> <% end %> <h3>Movies</h3> <table class="table table-border table-hover"> <thead> <th> <%= sort_link(@q, :name,"Movie name", default_order: :desc) do%> <strong>Movie Name</strong> <% end %> </th> <th>url</th> </thead> <tbody> <% @movies.each do |movie| %> <tr> <td><%= movie.name%></td> <td><%= movie_path(movie) %></td> </tr> <% end %> </tbody> </table>
使用高级功能
为了能够生成复杂的查询语句如: AND/OR, groupings等,ransack使用Rails's nested attributes functionality。
这需要多一点准备工作,但它能够产生更绚丽的搜索交互。唯一一个明显的缺点是要求使用HTTP POST方法代替GET。
⚠️Ransack#search方法等同于ransack方法。但最好还是用ransack方法。因为search方法可能和其他gem冲突!
Associations
可以使用Ransack来搜索关联的对象。 has_many, belongs_to
加上includes()方法即可
def index @q = Supervisor.ransack(params[:q]) @supervisors = @q.result.includes(:department, :employees) end
Search Matchers
Ransack提供了大量的predicates
https://github.com/activerecord-hackery/ransack
Problem with DISTINCT selects
⚠️distince: true会生成SELECT DISTINCT
在使用includes关联上其他表时,需要注意。
验证白名单黑名单
默认,搜索和整理sort是在你的model上被通过验证的。无需把类方法/scopes加入白名单。
Ransack提供了ActiveRecord::Base4种方法。你可以从新定义类方法进行验证。
Grouping queries by OR instead of AND
默认使用AND grouping。可以通过添加m: 'or'来改变查询hash
def index @q = Artist.ransack(params[:q].try(:merge, m: 'or')) @artists = @q.result end
SQL query becomes WHERE..OR..
能够配合I18n
能够配合SimpleForm