Filtering, Searching, and Sorting ActiveRecord Lists Using Filterrific

48
Filtering, Searching, and Sor1ng Ac1veRecord Lists Using Filterrific Waihon Yew GitHub (waihon) TwiCer (@waihon)

Transcript of Filtering, Searching, and Sorting ActiveRecord Lists Using Filterrific

Page 1: Filtering, Searching, and Sorting ActiveRecord Lists Using Filterrific

Filtering,Searching,andSor1ngAc1veRecordListsUsingFilterrific

WaihonYewGitHub(waihon)TwiCer(@waihon)

Page 2: Filtering, Searching, and Sorting ActiveRecord Lists Using Filterrific

SearchingforaGem

Page 3: Filtering, Searching, and Sorting ActiveRecord Lists Using Filterrific

WhatDoesFilterrificProvide?•  Letyourapp'suserssearch,filterandsortlistsofAc1veRecordobjects.

•  PersistfilterseOngsintheHTTPsessionorDB.•  Integrateswithpagina(on(will_paginateorkaminari).

•  ResetfiltertodefaultseOngs.•  ReliesonAc1veRecordscopes.•  ShuClesfilterseOngsfromafilterUItothecontrollerandAc1veRecord.

•  CanbeusedforHTML/JSON/JS/XMLresponseformats.

•  Documenta1on:hCp://filterrific.clearcove.ca/

Page 4: Filtering, Searching, and Sorting ActiveRecord Lists Using Filterrific

Dependencies•  RailsandAc1veRecord3.xandabove.•  jQueryandassetpipelineforformobserversandspinner.

Page 5: Filtering, Searching, and Sorting ActiveRecord Lists Using Filterrific

WhatYouHaveToDo?1.  DefineAc1veRecordscopes.2.  Buildandstyleyourfilterformandrecord

lists.

Page 6: Filtering, Searching, and Sorting ActiveRecord Lists Using Filterrific

•  Awebappthatstoresbasicinforma1onofstudents.

DemoApp-ERD

has_many

belongs_to

Page 7: Filtering, Searching, and Sorting ActiveRecord Lists Using Filterrific

DemoApp–Base

hCps://github.com/jhund/filterrific_demohCp://filterrific-demo.herokuapp.com/

Page 8: Filtering, Searching, and Sorting ActiveRecord Lists Using Filterrific

DemoApp–Enhanced

hCps://github.com/waihon/filterrific_demo

Page 9: Filtering, Searching, and Sorting ActiveRecord Lists Using Filterrific

ShortDemooftheEnhancedApp

Page 10: Filtering, Searching, and Sorting ActiveRecord Lists Using Filterrific

Gemfilegem "filterrific"

Page 11: Filtering, Searching, and Sorting ActiveRecord Lists Using Filterrific

View•  DisplaytheformtoupdatefilterseOngs.•  Displaythelistofmatchingrecords.•  UpdatethefilterseOngsviaAJAXformsubmission.

•  ResetthefilterseOngs.•  FilterrificworksbestwithAJAXupdates.•  ThelibrarycomeswithformobserversforjQueryandanAJAXspinner.

Page 12: Filtering, Searching, and Sorting ActiveRecord Lists Using Filterrific

ViewComponents

Page 13: Filtering, Searching, and Sorting ActiveRecord Lists Using Filterrific

ViewComponentsindex.html

Thisisthemaintemplateforthestudentslist.Itisrenderedonfirstload.

Page 14: Filtering, Searching, and Sorting ActiveRecord Lists Using Filterrific

ViewComponentsindex.html

students/_list.html

Thispar1alrenderstheactuallistofstudents.It’sextractedintoapar1alsothatitcanbeupdatedviaAJAXresponse.

Page 15: Filtering, Searching, and Sorting ActiveRecord Lists Using Filterrific

ViewComponentsindex.html

students/_list.htmlform_for_filterrific

Filterrificaddsthe'form_for_filterrific'viewhelper:*AddsDOMid'filterrific_filter'*AppliesJavascriptbehaviors:-AJAXformsubmissiononchange-AJAXspinnerwhileAJAXrequestisbeingprocessed*Setsform_forop1onslike:url,:methodandinputnameprefix

Page 16: Filtering, Searching, and Sorting ActiveRecord Lists Using Filterrific

ViewComponentsindex.html

students/_list.htmlform_for_filterrific

Givethesearchfieldthe'filterrific-periodically-observed'classforliveupdates.

Page 17: Filtering, Searching, and Sorting ActiveRecord Lists Using Filterrific

ViewComponentsindex.html

students/_list.htmlform_for_filterrific

index.js

ThisJavaScriptupdatesthestudentslistaherthefilterseOngswerechanged.

Page 18: Filtering, Searching, and Sorting ActiveRecord Lists Using Filterrific

ViewComponentsindex.html

students/_list.htmlform_for_filterrific

index.js

Searches/_modal_form.html

DisplayamodalformforsavingfilterseOngs.

Page 19: Filtering, Searching, and Sorting ActiveRecord Lists Using Filterrific

ViewComponentsindex.html

students/_list.htmlform_for_filterrific

index.js

searches/_list.html

Page 20: Filtering, Searching, and Sorting ActiveRecord Lists Using Filterrific

ViewComponentsindex.html

students/_list.htmlform_for_filterrific

index.js

searches/_list.html

ResetfilterseOngstothedefaultsdefinedinthemodeloroverrideninthecontroller(reset_filterrific_url).

Page 21: Filtering, Searching, and Sorting ActiveRecord Lists Using Filterrific

SortbyColumnTitle

Filterrificprovidessortablecolumnheaderlinkswhichtogglesortdirec1onwiththefilterrific_sor1ng_link()method.

Page 22: Filtering, Searching, and Sorting ActiveRecord Lists Using Filterrific

SortbyColumnTitle

Themethodmustbeplacedinthe_list.htmlviewpar1al:<th><%= filterrific_sorting_link(@filterrific, :name) %></th>

Page 23: Filtering, Searching, and Sorting ActiveRecord Lists Using Filterrific

Thefilterrific_sor1ng_linkmethodisexpec1ngasorted_byscopewhichcontainsthecolumnheadersandthemodelaCributetosortby.Columnheadersareautoma1callycapitalized. scope :sorted_by, lambda { |sort_option| # extract the sort direction from the param value. direction = (sort_option =~ /desc$/) ? 'desc' : 'asc' case sort_option.to_s when /^created_at_/ order("students.created_at #{ direction }") when /^name_/ order("LOWER(students.last_name) #{ direction }, LOWER(students.first_name) #{ direction }") when /^country_name_/ order("countries.name #{ direction }").includes(:country) else raise(ArgumentError, "Invalid sort option: #{ sort_option.inspect }") end }

SortbyColumnTitle

Page 24: Filtering, Searching, and Sorting ActiveRecord Lists Using Filterrific

DisableAJAXAutoFormSubmits•  BydefaultFilterrificwillautoma1callysubmitthefilterformassoonaswechangeanyofthefilterseOngs.

•  Some1mesyoumaynotwantthisbehavior,e.g.,iftherenderingofthefilteredrecordsisfairlyexpensive.

•  Theautosubmitbehavioristriggeredbythefilterform'sidwhichisautoma1callyaddedbytheform_for_filterrifichelpermethod.

•  Inordertodeac1vateAJAXautosubmits–  OverridetheDOMidfortheformwithsomethingotherthanthedefaultoffilterrific_filter.

–  Addtheremote:trueop1ontoform_for_filterrific–  Don'taddthe.filterrific-periodically-observedclasstoanyinputs.

–  AddaregularsubmitbuCon

Page 25: Filtering, Searching, and Sorting ActiveRecord Lists Using Filterrific

DisableAJAXAutoFormSubmits•  BydefaultFilterrificwillautoma1callysubmitthefilterformassoonaswechangeanyofthefilterseOngs.

•  Some1mesyoumaynotwantthisbehavior,e.g.,iftherenderingofthefilteredrecordsisfairlyexpensive.

•  Theautosubmitbehavioristriggeredbythefilterform'sidwhichisautoma1callyaddedbytheform_for_filterrifichelpermethod.

•  Inordertodeac1vateAJAXautosubmits–  OverridetheDOMidfortheformwithsomethingotherthanthedefaultoffilterrific_filter.

–  Addtheremote:trueop1ontoform_for_filterrific–  Don'taddthe.filterrific-periodically-observedclasstoanyinputs.

–  AddaregularsubmitbuCon

Ifyous1llwanttosubmittheformviaAJAX(justnotautoma1callyoneverychange).OtherwisetheformwillbesubmiCedasregularPOSTrequestandtheen1repagewillreload.

Page 26: Filtering, Searching, and Sorting ActiveRecord Lists Using Filterrific

DisableAJAXAutoFormSubmits•  BydefaultFilterrificwillautoma1callysubmitthefilterformassoonaswechangeanyofthefilterseOngs.

•  Some1mesyoumaynotwantthisbehavior,e.g.,iftherenderingofthefilteredrecordsisfairlyexpensive.

•  Theautosubmitbehavioristriggeredbythefilterform'sidwhichisautoma1callyaddedbytheform_for_filterrifichelpermethod.

•  Inordertodeac1vateAJAXautosubmits–  OverridetheDOMidfortheformwithsomethingotherthanthedefaultoffilterrific_filter.

–  Addtheremote:trueop1ontoform_for_filterrific–  Don'taddthe.filterrific-periodically-observedclasstoanyinputs.

–  AddaregularsubmitbuCon

<%= form_for_filterrific @filterrific, remote: true html: { id: 'filterrific-no-ajax-auto-submit' } do |f| %> ... <%= f.submit 'Filter' %> <% end %>

Page 27: Filtering, Searching, and Sorting ActiveRecord Lists Using Filterrific

Model–AddfilterrificDirec1ve# student.rb Filterrific( default_filter_params: { sorted_by: 'created_at_desc' }, available_filters: [ :sorted_by, :search_query, :with_country_id, :with_created_at_gte ] )

DefinedefaultfilterseOngs

SpecifywhichscopesareavailabletoFilterrific.Thisisasafetymechanismtopreventunauthorizedaccesstoyourdatabase.It’slikestrongparameters,justforfilterseOngs.

EnableFilterrificfortheStudentclass

Page 28: Filtering, Searching, and Sorting ActiveRecord Lists Using Filterrific

Model–DefineSelectOp1ons# student.rb def self.options_for_sorted_by [ ['Name (a-z)', 'name_asc'], ['Registration date (newest first)', 'created_at_desc'], ['Registration date (oldest first)', 'created_at_asc'], ['Country (a-z)', 'country_name_asc'] ] End # country.rb def self.options_for_select order('LOWER(name)').map { |e| [e.name, e.id] } end

Theseclassmethodsprovideop1onsfor

selectdrop-downandarecalledinthe

controlleraspartofini1alize_filterrific.

Page 29: Filtering, Searching, and Sorting ActiveRecord Lists Using Filterrific

Model–DefineScopesscope :sorted_by, -> { |sort_key| # Sort students by sort_key direction = (sort_key =~ /desc$/) ? 'desc' : 'asc’ ... } scope :search_query, -> { |query| # Filters students that matches the query ... } scope :with_country_id, -> { |country_ids| # Filters students with any of the given country_ids where(:country_id => [*country_ids]) } scope :with_created_at_gte, -> { |ref_date| # Filter students whom registered from the given date where('students.created_at >= ?', Date.strptime(ref_date, "%m/%d/%Y")) }

Page 30: Filtering, Searching, and Sorting ActiveRecord Lists Using Filterrific

Model–DefineScopesscope :sorted_by, -> { |sort_key| # Sort students by sort_key direction = (sort_key =~ /desc$/) ? 'desc' : 'asc’ ... } scope :search_query, -> { |query| # Filters students that matches the query ... } scope :with_country_id, -> { |country_ids| # Filters students with any of the given country_ids where(:country_id => [*country_ids]) } scope :with_created_at_gte, -> { |ref_date| # Filter students whom registered from the given date where('students.created_at >= ?', Date.strptime(ref_date, "%m/%d/%Y")) }

FilterrificreliesheavilyonAc1veRecordscopesforfiltering,soitisimportantthatyouarefamiliarwithhowtousescopes.hCp://filterrific.clearcove.ca/pages/ac1ve_record_scope_paCerns.html

Page 31: Filtering, Searching, and Sorting ActiveRecord Lists Using Filterrific

FilterrificAc1onController•  Ini1alizefilterseOngsfromparams,persistenceordefaults.

•  ExecutetheAc1veRecordquerytoloadthefilteredrecords.

•  SendtheAc1veRecordcollec1ontotheviewforrendering.

•  PersistthecurrentfilterseOngs.•  ResetthefilterseOngs.

Page 32: Filtering, Searching, and Sorting ActiveRecord Lists Using Filterrific

Controller–ini1alize_filterrific# students_controller.rb def index @filterrific = initialize_filterrific( Student, params[:filterrific], select_options: { sorted_by: Student.options_for_sorted_by, with_country_id: Country.options_for_select } persistence_id: 'shared_key', default_filter_params: {}, available_filters: [], ) or return @students = @filterrific.find.page(params[:page]) ... end

Page 33: Filtering, Searching, and Sorting ActiveRecord Lists Using Filterrific

Controller–ini1alize_filterrific# students_controller.rb def index @filterrific = initialize_filterrific( Student, params[:filterrific], select_options: { sorted_by: Student.options_for_sorted_by, with_country_id: Country.options_for_select } persistence_id: 'shared_key', default_filter_params: {}, available_filters: [], ) or return @students = @filterrific.find.page(params[:page]) ... end

Filterrificlivesinthecontroller’sindexac1on.

Page 34: Filtering, Searching, and Sorting ActiveRecord Lists Using Filterrific

Controller–ini1alize_filterrific# students_controller.rb def index @filterrific = initialize_filterrific( Student, params[:filterrific], select_options: { sorted_by: Student.options_for_sorted_by, with_country_id: Country.options_for_select } persistence_id: 'shared_key', default_filter_params: {}, available_filters: [], ) or return @students = @filterrific.find.page(params[:page]) ... end

AnAc1veRecord-basedmodelclass.ItcanalsobeanAc1veRecordrela1on.

Page 35: Filtering, Searching, and Sorting ActiveRecord Lists Using Filterrific

Controller–ini1alize_filterrific# students_controller.rb def index @filterrific = initialize_filterrific( Student, params[:filterrific], select_options: { sorted_by: Student.options_for_sorted_by, with_country_id: Country.options_for_select } persistence_id: 'shared_key', default_filter_params: {}, available_filters: [], ) or return @students = @filterrific.find.page(params[:page]) ... end

AnyparamssubmiCedviawebrequest.Iftheyareblank,filterrificwilltryparamspersistedinthesessionnext.Ifthoseareblank,too,filterrificwillusethemodel'sdefaultfilterseOngs.

Page 36: Filtering, Searching, and Sorting ActiveRecord Lists Using Filterrific

Controller–ini1alize_filterrific# students_controller.rb def index @filterrific = initialize_filterrific( Student, params[:filterrific], select_options: { sorted_by: Student.options_for_sorted_by, with_country_id: Country.options_for_select } persistence_id: 'shared_key', default_filter_params: {}, available_filters: [], ) or return @students = @filterrific.find.page(params[:page]) ... end

Storeanyop1onsfor<select>inputsintheform.ThekeyreferstoscopenamedefinedinthemodelThevaluereferstomethoddefinedinthemodelthatreturnanarrayofop1ons

Page 37: Filtering, Searching, and Sorting ActiveRecord Lists Using Filterrific

Controller–ini1alize_filterrific# students_controller.rb def index @filterrific = initialize_filterrific( Student, params[:filterrific], select_options: { sorted_by: Student.options_for_sorted_by, with_country_id: Country.options_for_select } persistence_id: 'shared_key', default_filter_params: {}, available_filters: [], ) or return @students = @filterrific.find.page(params[:page]) ... end

Defaultsto"<controller>#<ac1on>"stringtoisolatesessionpersistenceofmul1plefilterrificinstances.Overridethistosharesessionpersistedfilterparamsbetweenmul1plefilterrificinstances.Settofalsetodisablesessionpersistence.

Page 38: Filtering, Searching, and Sorting ActiveRecord Lists Using Filterrific

Controller–ini1alize_filterrific# students_controller.rb def index @filterrific = initialize_filterrific( Student, params[:filterrific], select_options: { sorted_by: Student.options_for_sorted_by, with_country_id: Country.options_for_select } persistence_id: 'shared_key', default_filter_params: {}, available_filters: [], ) or return @students = @filterrific.find.page(params[:page]) ... end

Tooverridemodeldefaults

Page 39: Filtering, Searching, and Sorting ActiveRecord Lists Using Filterrific

Controller–ini1alize_filterrific# students_controller.rb def index @filterrific = initialize_filterrific( Student, params[:filterrific], select_options: { sorted_by: Student.options_for_sorted_by, with_country_id: Country.options_for_select } persistence_id: 'shared_key', default_filter_params: {}, available_filters: [], ) or return @students = @filterrific.find.page(params[:page]) ... end

Tofurtherrestrictwhichfiltersareinthisfilterrificinstance.

Page 40: Filtering, Searching, and Sorting ActiveRecord Lists Using Filterrific

Controller–ini1alize_filterrific# students_controller.rb def index @filterrific = initialize_filterrific( Student, params[:filterrific], select_options: { sorted_by: Student.options_for_sorted_by, with_country_id: Country.options_for_select } persistence_id: 'shared_key', default_filter_params: {}, available_filters: [], ) or return @students = @filterrific.find.page(params[:page]) ... end

ThismethodalsopersiststheparamsinthesessionandhandlesreseOngthefilterrificparams.

Page 41: Filtering, Searching, and Sorting ActiveRecord Lists Using Filterrific

Controller–ini1alize_filterrific# students_controller.rb def index @filterrific = initialize_filterrific( Student, params[:filterrific], select_options: { sorted_by: Student.options_for_sorted_by, with_country_id: Country.options_for_select } persistence_id: 'shared_key', default_filter_params: {}, available_filters: [], ) or return @students = @filterrific.find.page(params[:page]) ... end

Inorderforreset_filterrifictowork,it’simportantthatweaddthe'orreturn'bitaherthecallto'ini1alize_filterrific'.Otherwisetheredirectwillnotwork.

Page 42: Filtering, Searching, and Sorting ActiveRecord Lists Using Filterrific

Controller–ini1alize_filterrific# students_controller.rb def index @filterrific = initialize_filterrific( Student, params[:filterrific], select_options: { sorted_by: Student.options_for_sorted_by, with_country_id: Country.options_for_select } persistence_id: 'shared_key', default_filter_params: {}, available_filters: [], ) or return @students = @filterrific.find.page(params[:page]) ... end

ThismethodalsopersiststheparamsinthesessionandhandlesreseOngthefilterrificparams.

ReturnsanAc1veRecordrela1onforallrecordsthatmatchthefilterseOngs.Wecanpaginatewithwill_paginateorkaminari.Therela1onreturnedcanbechainedwithotherscopestofurthernarrowdownthescopeofthelist,e.g.,toapplypermissionsortoexcludecertaintypesofrecords.

Page 43: Filtering, Searching, and Sorting ActiveRecord Lists Using Filterrific

SavedSearches

•  @search.filter=session["shared_key"]•  @search.filter=session["students#index"] {\"sorted_by\"=>\"created_at_asc\",

\"with_country_id\"=>7,

\"with_created_at_gte\"=> \"01/01/2016\"}

Page 44: Filtering, Searching, and Sorting ActiveRecord Lists Using Filterrific

SavedSearches

•  @search.filter=session["shared_key"]•  @search.filter=session["students#index"] {\"sorted_by\"=>\"created_at_asc\",

\"with_country_id\"=>7,

\"with_created_at_gte\"=> \"01/01/2016\"}

Thiskeyisthe:persistence_iddefinedinthecalltoini1alize_filteerrific

Thiskeyisthe:persistence_iddefinedinthecalltoini1alize_filterrific

Page 45: Filtering, Searching, and Sorting ActiveRecord Lists Using Filterrific

Ini1alizePersistedFilterSeOngs•  Inthecalltoini1alize_filterrific,replaceparams[:filterrific]withamethodcall.

def filter_settings

if params[:filterrific].present?

params[:filterrific]

elsif params[:search_id].present?

search = Search.find_by(id: params[:search_id]) search ? eval(search.filter) : Student.default_filter_params

else

Student.default_filter_params

end end

# students_controller.rb @filterrific = initialize_filterrific( Student, filter_settings, ...

Page 46: Filtering, Searching, and Sorting ActiveRecord Lists Using Filterrific

Ini1alizePersistedFilterSeOngs•  Inthecalltoini1alize_filterrific,replaceparams[:filterrific]withamethodcall

def filter_settings

if params[:filterrific].present?

params[:filterrific]

elsif params[:search_id].present?

search = Search.find_by(id: params[:search_id]) search ? eval(search.filter) : Student.default_filter_params

else

Student.default_filter_params

end end

#routes.rb get "/students/search/:search_id", to: "students#index", as: "search_students"

Page 47: Filtering, Searching, and Sorting ActiveRecord Lists Using Filterrific

Ini1alizePersistedFilterSeOngs•  Inthecalltoini1alize_filterrific,replaceparams[:filterrific]withamethodcall

def filter_settings

if params[:filterrific].present?

params[:filterrific]

elsif params[:search_id].present?

search = Search.find_by(id: params[:search_id]) search ? eval(search.filter) : Student.default_filter_params

else

Student.default_filter_params

end end

UsingevaltoconvertthepersistedfilterseOngsfromstringtohash.

Page 48: Filtering, Searching, and Sorting ActiveRecord Lists Using Filterrific

ThankYouforYourACen1on&Pa1ence!

WaihonYewGitHub(waihon)TwiCer(@waihon)