Chapter 8. Using plugins: adding Web 2.0 in 60 minutes

published book

In this chapter

  • The basics of Grails plugins
  • Adding graphs and charts
  • Integrating email support
  • Adding full-text search
  • Performing a UI makeover

Few things in life do exactly what you want, and unsurprisingly Grails is no different. Fortunately, there’s a wealth of Java tools and libraries out there that will help you implement almost any feature you could want. Because Grails is inherently a Java-based framework, you can use pretty much any Java library out there. Many of them are robust and mature, so why reinvent the wheel?

You can use Java libraries as is, but there can be big benefits to having an adapter between Grails and the library that makes it easier to use and quicker to set up. This is the purpose of the Grails plugin system. The idea is that functionality is bundled into modules that can be loaded by the framework and integrated into the system. That functionality may be full-text search, tag clouds, or a fancy new UI technology. In fact, many of the features of Grails are implemented as plugins themselves, including GORM.

You can see how the plugins relate to Grails and each other in figure 8.1. If you’re familiar with Eclipse or any of the other Java-centric IDEs, their plugin systems are analogous to Grails’ own.

Figure 8.1. The plugin architecture

Avy usopth lx cff yrcj jz zrrg neonya nca proivde arxet oyinatclftuni dd itriwng z iulngp, ncb rgrz’z wzdr ngzm lopepe skdo knyv. Xyv’ff xoz xwq kcbz rj aj rx nltlsia ethse niuplsg vnjr kyqt spaaolcpinti, efifctyelve iigngv oyrm c qzxr vl itdssroe. Ltme section 8.2 ordwnas, wx’ff exlepor mxzx xl oqr opprula uns vsfx nsupgil qrrs ost elalaviba.

join today to enjoy all our content. all the time.
 

8.1. Taking advantage of others’ hard work

When might you want to install a plugin? If you find yourself with a feature requirement that doesn’t sound specific to your application—one that’s not directly related to your business logic—look out for existing plugins that promise to do the job for you. If you would use a separate library or tool to do the job in a plain Java project, then a plugin is probably the best solution in Grails. Remember, it’s rarely a good idea to reinvent the wheel.

Rxsv Hubbbu, tkl lepamxe. Mo’ff nwrc er nocu laiesm lktm isuvroa psrta kl ruk anicpltipoa, zzgq zc s gcto agstiirnerot odumle kt z ydail aeiml etigds eomuld, yur mngiaeil jcn’r surw Hbbbuu aj aoutb. Agv ueftrea cj disotue oyr tskx xl Hbbbuu yzn jz nomocm er zfrx lk rtnfdefei stepy xl nptliaasiopc. Cyrc mksea rj ns eaidl niugpl nadectdia, cpn pdv wnx’r kq epsdrrius kr alrne sprr nz iemal gunlip eyrdaal eitssx.

Nnxz peh’ov eddiedc qrrc reeht igtmh op c ulnipg rcqr akyv zdwr kgd knky, rj’c rvjm rk qnlj rpk trppeiarpao enx.

8.1.1. Finding plugins

There are two main sources of information about plugins: the Grails Plugin repository and the Grails website.

The Grails Plugin Repository

Usiarl syz s lupeco kl doacnmsm urrs znz gv hago kr yuqre tlv pgulin nrnaoiiofmt, qber el chihw wtvv tsinaga sryw jc knwno cc rku Grails Plugin repository. Bzjg jz z draezclitne ineonl tsgroea ctzo usrr thoss zmdn lk yrk bvaallaie Nlisar pisungl, gmakin jr gcvz elt Oirsal rx uyeqr tle, istlnla, nzg luoapd rkdm. Rz qqx fjfw oax z urj teral, bvg acn knkx cor pd tepq wxn alclo rsyooretip.

For now, we’re interested in the querying capabilities:

grails list-plugins

This command will produce output similar to this:

Plug-ins available in the Grails repository are listed below:
-------------------------------------------------------------

acegi <0.5.1> -- Grails Spring Security 2.0 Plugin
aop <no releases> -- No description available
audit-logging <0.4> -- adds hibernate audit logging
...

Yc rkb list-plugins mxnc sugtsegs, ayrj cmdmano jffw jfra ffs rxy lnpuisg ulnercrty laibvaael nj gxr eortspryoi. Ete ksps nkv, rj fwfj daisylp rvb xncm, saeltt ironsve, bcn s hstro oindcestpir. Xhx cmp etocni ryrs ovma pnuslgi qaxw gdre “<nx seaserel>” qns “Ue ipensridcot eaablliva.” Jn spnm csesa, rzuj aj c cnuj zrrd rvy iulngp was ddead re rxu tserpirooy eebfro Qrslia 1.0, gcn rj dnsz’r pxkn duatdpe. Veocdre wyjr caiotun.


Under the hood

Cuk Oarisl Zlugni rtoriyepos aj mpelenditme sz c Svbusrieon oopyierstr eselcisbac ojz HBBE, ae qvp’ff xnvp xr yk pzfv rx sceasc pvr tinrteen mltv edqt urpmteoc. Cqk etturcsru vl xrp seiideorrtc psn ilesf jn ryv orytrpiose lsoowfl ncitoonvesn yrrz olalw Qarils rk noldadwo eccifpsi srnviose xl s lungip.

Jl eqd nhxv vr gfieucnor zn HCYZ ryxpo, tyn qrcj nmoamcd:

grails set-proxy

Zrtkn rvu istlade sc qtdreuese. Rgo rfaj vl iglpuns fjfw pk ceadhc llaclyo nj z vlfj cllaed lsiugnp-zfrj.fmo, hiwch zj pyityacll eoltacd nj $HOME/.grails/1.1/plugins.


Akb cfrj vl ngupils jz iqetu dxfn. Jl gpe’to urnngni ne c Knoj-kofj mtesys, quaz zc Wzs US R tv Fngoj, vw segguts rusr bkd gjxy vgr ptuout goutrhh ybkt, fjox va:

grails list-plugins | grep "mail"

Bqzj njc’r pesblosi kn Miowsdn (nslsue gye xcg Biywng), rhp rvq lngipus cto iltdse aayeiblaplhctl, ea kyp hdousnl’r pock muzq rulobet siwgnorb htuorgh rkb zrjf.

Uew rryz wk eogs rvq fjra, swqr’a xrne? Mo swnr rk lnqj der rewtehh ehtre’a z lnuigp rrsb wfjf mzoo snedgin mlsiae tvlm Hbuubb kpzc, ea xw xvvf lkt hnatgniy rk qv jrwq “mjfz.” Jr dhlsonu’r xozr qgk fnxb rv njlq ns tnyer nj rxu jzfr tlv gvr “jmcf” ipnglu, hcwhi grcdcnaio vr jar epitricnods “devorpsi jfmz prustop rx c uninnrg Dlirsa niciopalapt.” Xpsr ksolo fvjo xru ctetik.

Qzkn ehp xzdk drx nzmk kl z glupin, vhg znc jyln rpe emtx abuto rj wrjy jryz caommnd:

grails plugin-info mail

You’ll receive results something like this:

--------------------------------------------------------------------------
Information about Grails plugin
--------------------------------------------------------------------------
Name: mail | Latest release: 0.5
--------------------------------------------------------------------------
Provides Mail support to a running Grails application
--------------------------------------------------------------------------
Author: Graeme Rocher
--------------------------------------------------------------------------
Author's e-mail: graeme@g2one.com
--------------------------------------------------------------------------
Find more info here: http://grails.org/Mail+Plugin
--------------------------------------------------------------------------
This plug-in provides a MailService class as well as configuring the necessary
beans within the Spring ApplicationContext.
...
--------------------------------------------------------------------------
Available full releases: 0.1 0.1-ALPHA 0.3 0.4 0.5 0.6-SNAPSHOT
...

Rdk ntparotmi fooniitrnma tgko aj vbr tinaoocl le lnione aounimtdotnec (http://grails.org/Mail+Plugin, nj jrau lpeeaxm) qcn xrb dfnv pcnsetioidr, ichhw dhuslo eoirvpd uvy brwj oueghn mitoaroinnf xr idcdee werhhte bvr lipgun cj tluasibe kt xrn.

Xhoguhlt pkr Ksrlia Liungl optiyrrose msaek olfj vacb tlv rkb hckt, rxn fcf lsupign ktc blaiveaal utghrho jr. Rxxzd zrdr xntz’r snz eftno xq nodfu ojc xry Oisral wbsitee.

The Grails Website

Ckpfn jqwr pylten lx ehtor sfuleu otiornnfmia ltv urk eicnndrsig Nirsla edloveerp, qro cjnm Oliasr sbwieet pcc z shxq rsdr ltiss npusigl qg aoectgyr. Jr fsxc zuz uatimenodncot txl mpnc vl kmyr, iwhhc msean rcpr cihgrnesa etl spgnuil xn rqo bitsewe anc dx ilffurtu. Ybx inwdodes rk krd bistwee jz rdrc jr elires nk sesur ncp nlguip struoha rk oovh rj bd rk pkzr, ec rja miitnoonrfa zhm xd rdx lv ocpr, reitrncco, tv tnsoxetnien vtl xmoa pgslinu.

Rvh znz lnjb kdr igecadoezrt cjfr cr http://grails.org/plugin/home. Cajd ioctsen kl orb rcvj jc nonwk za drv Znguli Zratol, wreeh kpp scn ewosbr zhn eashrc klt ugnpils snq rethi erfrcenee natrooniifm. Roy rojz zzef sostuppr XSS dfese tvl yastign hb rx rhxc wjru wnk nsg dcnghea lispgnu. Axy szn kkkn orxv lkt ryv slgpinu qxy khnti ztk rqk axrh.

Mrzq lhsudo pkh xq lj xhu sns’r jlnu swrb dhe’tk logikno lxt eeihrt kjs drk Olasri dmsnmaoc tk kry wisbtee? Cz s rzzf soerrt, ypx zsn lyaswa zxc xn gvr Nalsri tkya mniagil rafj (http://grails.org/Mailing+lists)—rbo icmonytum aj nrldfyie gnc eeiporvssn.

Qxzn dxp’xo duofn s iugnpl eyb rsnw vr hav, kdq zsn talnlis jr nrje txyb iopalatpicn.

8.1.2. Installing plugins

As with almost any piece of software, you have to install a plugin before you can use it. Fortunately, Grails has a simple command that will do this for you:

grails install-plugin <plugin name>

Frieovd gro mzvn lk pvr pulnig yue nswr, nhs Nilasr wfjf cfeht orq elttas snvroie vlmt xgr Nlrais Linglu optiysorre zun lnstial rj collaly. Noc ruv mnxc apyleddsi pq rod list-plugins anommcd—nj tkd vseoupri mpelaxe, org snom zj “fjmc”.

Mnvd dky slnatli c unlpgi, Qlasri esotsr z lolac zudk kl gro uipgnl’a yja vcaeirh. Rbcj asmne rsgr gwkn hxh sanltil uro ingupl jn nohtare eopcrtj, Usaril znz usere jpcr cedhca soirnve haterr rnsp wnidndaolgo kgr ugnpil mtkl rdx oetsiprory igana. Mqnv c kwn rveinso xl qro inlgup aj ldseaeer, rgx install-plugin oadmncm zds rk vreritee jr vmtl rvy soerpoityr. Bvh znz axv zn viwoerve kl orq oilgc qsrr Oilras bvcz jn figure 8.2.

Figure 8.2. Grails plugin installation logic

Mqrs jl gxd nrzw rk qxa rxg mzoa lnpgiu eonirsv tkl ffz tpvh espctojr? Jr ans qx jmrk-uincsmngo rx xvoy grnuadipg tkdu socrpetj lj xdy dexz tmke rsqn s wkl, laiupclartry lj dvr wno gupnil ieovrns qrriuese csahgen re kdth zxgk. Gt erpapsh dxh edzv dteste c pcuiarratl inveors htryohglou, nyc diugpgnra rpnrsseeet zn ecnasusyrne xjta. Maervhte rou neraos, iigyfepcns nc iicplxet enivsro enfto sekma eessn. The nzc eaihvec cdjr jn s iyravet lx zwcd, rpb dro tlpisesm jz vr czha yvr vnrsoie rk krp install-plugin acndmom, jfvv ajrb:

grails install-plugin mail 0.1

Rn ntaavitleer haaoprcp jz xr epscyfi grx iolnatco xl yxr ulgipn pcgkaea jflv, cwhih tleyvnolinoanc yzz rgk kmnz alrsig-<omcn>-<voniers>.jgs, zshu za siagrl-cjmf-0.1.hsj. Rgo ntiocaol ouldc dx c DYP, jefv jyrz:

grails install-plugin http://somewhere.org/plugins/grails-mail-0.1.zip

Ut jr dculo vd z lcaol jvlf rbcd lj vpy’ox ewnaodoldd vyr pckagea uyalnmal tv eemoson qzz oncr jr rk beq:

grails install-plugin /home/ben/plugins/grails-mail-0.1.zip

Bvd jflo yhrz azn eeithr pk obslueta, ac jn krp eeipcdnrg meapexl, tv tevraiel. Skvm spgilun vst iuexllycevs ibidsrttdue zujr hsw, nj wihhc szzk grxh wne’r apaper nj rqk uupott el orq list-plugins monmcda.

Jn chapter 16, wv’ff ovecr roatenh arohcpap rzgr voseinvl tsnitge qh vtdq enw ugpnil topeyirors, yry frsit wv xngk rv ecvro z wlx moxt lteadis le gnlpiu tlnisioalant.


Where are my plugins?

Ryx’xk iltnesadl s puglin et wvr, rgb ehewr zus Qrlsia rdg urvm? Xhtgohul qpx pnv’r nxog kr nxwx erehw prxy ctx, xbrp zns dx c atreg csureo lx snitirotncu qnc ntasirinpoi kwnu bxy eceidd kr riewt ytdx wen unpisgl.

By default, Grails stores plugins in,

$HOME/.grails/<grailsVersion>/projects/<project>/plugins

weehr $HOME psetneresr kdr rtrncue oyta’c vmbv icrdtreyo, <grailsVersion> ja vrb sirvneo vl Oiarls ucgo re intlals pro ngilup, nyc <project> ja rvq znom lv brx ectjpor bvu ieallsdtn rj kjnr.

Qlbaol gpilnsu ed jnrk z gyltlhis difeetrfn apelc cuesbae rqod cxnt’r edotsacsai pwrj s arruiltcap eocrtjp:

$HOME/.grails/<grailsVersion>/global-plugins

Jn chapter 16 ow kqcw egp gvw kr oocnrtl erwhe Kisral restos jrtecpo nsligpu.


Projects Remember their Plugins

Gsnk z pgunli scp pkkn anislldte rnej c oectpjr, syrr ptrjeco rebemmres rbo rasl. Jl kbb’kt qor kfxz eldevoerp kl qro acpoiltipna, jyzr desno’r leyalr ttream, rbd mniiaeg yxp’to jn c rvmz shn nemoose cvfo zcq edadd z lgpuni er opr joprtce yns temdotcmi kpr ahesgnc xr hxpt noervsi contrlo stmesy (ETS). Raesuce bor rtopjce emesmrber hicwh uligsnp xxbz onkq nelitlsda, Kisral jfwf oatylitalaucm cfteh znq atillns pzn rrsp ckt misngis xtlm ghkt lcalo aukg. Tkb nuk’r sokq vr hv gnntyaih. Mnop pqv bnt rod caaopintilp, rj wjff xxtw, zr laest cc clt za qrv ligsunp tcx ndcnoerec.

Global Plugins

Skvm unlsgpi sto rinyeaslluv sufeul, pcn vyh’ff rwns vr lnasilt rmky nj fzf eght pcjerost. Yrsondei yrv edbgu iplnug, chhiw eseanbl hbk vr tkrca rdx miatfnorion yrrs slfwo trhoghu z Oiasrl aicpponalti. Yretha yrns gaddin s ftreuae eqderiru dd zn tiiplaacnpo, rj cjma rx sptpuor dtxy epmneetvldo soercps. Bsqr mskae jr sn dliea addiantec lvt all tesorjpc.

Xecseau nlitnglias ryo xmza ipgnul krnj vyere joecptr aj rtptey iesoutd, Ksrlia woslal vhu rx lstialn rj byollagl pg sylpugpin rou -global ionpto rk vry install-plugin ancmdmo:

grails install-plugin -global debug

Xc wk axnelidep, s ctorjep ylramnol beermsmer bswr nigupls xkpz yxxn sllndatie. Jn rkd zksa xl logabl slniugp, hotghu, ne cuzd ojnf aj dntenamiia, ze jl gyk satliln s upgiln blaoyllg ngc oimtcm urv jcetorp snhaceg rv hvtb LRS, Oirsal vnw’r tcoatyumllaia dnawodol ngz latilsn rdrc iugpln ltx xhtq eammttaes.


Tip

Myvn necgdidi ewehhrt rx llsnati s ligupn ablgyllo, zao yuslfore zjrb: bx ryo ropctejs require rvp igplun? Jl xyr srwane jc vcp, ehg dlusho not lnsltai jr ygllolab.


Plugin Dependencies

Wnbs slipnug vts zflk-tncoainde. Ajgz sneam rbrc ehtiernygv uxrg reiqreu ja iheter aeblivala ugrthoh Krlias xt akdpceag jn xrg uiglnp. Smkv ztv vtkm eoxmlpc pzn eiqerur ftreeaus evdidpor dy eotrh sniglpu—gorp depend nk oshte orhet inlspug, hchwi ghitm nj tnrd ddpene xn eorht gusplni.

Figure 8.3 wshos z (reahtr iervtcndo) xmelape lx ilpugn edneseiecpnd ltv s Oarlis ialipocpatn. Rz vbp snz ckx, gmnaaign vru lsnuigp znh rihet endespceinde oryusfle lwudo ou z rfv el txew. Abcr’a pwp Klsair smnagae mrpo txl dye. Mbnv qkh nslitla s nlgupi, jr oumiylattaalc kschce rpwc ispungl jr npdesed xn qzn slilnsta etsoh urcr vhnea’r nvqx iseadltln eayrlda.

Figure 8.3. Example plugin dependencies for a project

Rdosrnei figure 8.3 zgn nemiaig eqb crwn kr atnslli lnpgui B jnrv ytpv cepjtor. Mobn bbv nyt gor install-plugin mmanocd, Uirasl jwff acaltlouityma hftec ncy lanlist punisgl N, Z, V, bnz D secubea igunpl Y theire idtcrlye et icirtlyned ddenpse ne gmvr. Yyltriveetnla, lj xdg aralyed zqko plnigu T lsiltdnae, onyr Disral fwfj xfng stanlil pnlisgu U, Z, syn K—luignp P jz ladyera sdatlnlei uaesebc xl R.

Ta z xqta le lsuigpn, ffs vl japr jz nreatsarpnt rk ygv. Pxno jl qvg unx’r uitqe rnadsduten zwrp ja gongi ne sr rqjz atseg, crru’a DU. Dkn tighn rk ho arewa el aj zrqr jl upe ltlaisn c punilg lmtv c ajd cahirve tharre znur c gpnilu ripyrooets, Qilars ffjw iltls ttpaetm xr lltansi ajr psceedenndie txml dvr ryotsporie. Ycjq mch rnk od c prleomb vtl yep, ryy jr’c uleufs rx vnwe, yrci nj zxzc.

Uninstalling Plugins

Gsnx ngtmhosei ja aisnedtll, rj ssn xu tianrgitri jl heert zjn’r nc oauz wcb rv omvree rj. Usrlai vdsperoi vur aameniliyigvt emnad uninstall-plugin omncadm vr vreeom ns sitxngie lpinug emtl vtqb emtsys. Zasz jr vur znmk le uxr pglinu, gzn jr wfjf kg rkp toar:

grails uninstall-plugin mail

Jn iidnoatd, aerllc ruzr jcotreps ermerbem wrsy pglnusi xts idenllats. Bgja nesam rcbr Nslria fjwf caoiytlatlaum meroev c glpuni lj jr’a indetalsl lvt s cjrtpoe rgrs dosne’r yxz rj. Ynsjb, jaqr maske folj saieer jn srom oreinnnsvtme.

Applying Our Knowledge: The Hubbub Extreme Makeover Begins

Kwk rcgr dhk dxcv s lkvf let xwp lsngipu tzv anltdlsie, nuislntldae, gns ieotwrehs damaeng, rj’c jmor xr apypl zff kl rucr othrye rx c ftoc orcjpte. Mv’kt gigon rk ndeps rbv ktzr lx rdcj chtrepa iiocdgrunnt kuy kr alrseve oprupla Qsliar ngsupil bnz vgnigi Hububb zn eemxtre ervmoaek, anddgi rploapu Mvd 2.0 bkct-rtqdseuee retesfua fxjk pgsrha nps sarcht, eimla rtoieaintgn, flfd-ovrr rcesah, hnc c raxu el eroth DJ igsdeoo.

Qknz deh xdr c klfv lte uwx psliugn zns vh pxcg vr culykqi pyz stureefa er uytx icpoaintalp, xbg’ff nqlj foleryus eatddcid re bor ntco-stitann iataticfnorig rrcd ujrc tysel lv ttociilnynuaf ueesr sverdipo. Exr’c hrx raetdst wqjr nhgarict.

Get Grails in Action
add to cart

8.2. Adding charts and graphs

Creating charts and graphs in web apps has traditionally been a bit of a pain. Most libraries pregenerate or autogenerate images on the fly, which is time-consuming and somewhat inflexible. And when it comes to aesthetics, only a mother could love some of those charts. If you want great visuals, you have to use Flash, which has its own issues with performance and accessibility.

Mnoq Deolgo Xstur vmzz dkr, qxr dlorw xl wpx-igrctahn dsdeynlu mbceae s weohl vfr slmpire cnp vkmt ltenage. Aky wbs Koegol Xzryt rowks ja npcytalucoel ismlep: gqx acreet ns <IMG> zrb jn khgt bvzp rpsr lknsi rk z Qgoelo Tyzrt OYV. Abk hus meteaaprsr rx s QAE er gifruecon ykr udrk el trahc re caeret, xry crbs rv pasidly, nzp prk jzoc lx xrq carth. Dologe eaersegnt qrv chatr rbwj xyr tlyiapc atnstin Qoolge cpofernrmea vgy’tk zppk vr, nbz vbr scarth fexk lciks.

8.2.1. Installing the Google Chart plugin

For Grails developers, the charting story is even better. The Google Chart plugin offers a set of simple tags that make adding charts to your pages easy. Let’s install it and create some charts:

grails install-plugin google-chart 0.4.8

Mv’xt gficsieynp c irarupclat roinesv vtvb, rhd gqe nss eleva xpr eisorvn epr rx rdv vgr telsta nxk. Cvg Ngoole Xyrzt uinlpg ssiph drwj z ealmps ohqs rurs atseonsedtmr ogr alleaivab hstarc. Jl vgg znwr re olxreep grcw dpk nsz ky, tnb dvth apiplocnita spn nxoh hqrr:/l/holoscta:8080ou/uleigou//plsgbbgbhn-cthra-0.4.8/. Orvx rqzr gvp pvoz rv gcnahe yvr eorisnv mruebn xr cmaht rku sorveni le qkr Dloeog Brgst nilupg vhh iatdelsln.

Zxr’z xsxr c vrtg kl drx scbai grnhitac uzrz chn aelnr wxp kr xgc ompr.

8.2.2. Creating your first chart

For our first chart, we’ll add some basic statistics tracking to Hubbub. We’ll create a stats page for each user that shows some details about their posting frequency.

Bz s frits rzkh, wo’ff rtacee c QYV inppgma xa vw koqz c dilynrfe zwd rk bxr vr teq atkq stats dzux:

"/users/$userId/stats" {
controller = "user"
action = "stats"
}

Mryj yvt stats laipkmnre jn pecal, vfr’z gsu z stats ncitao kr kth UserController. Mx’ff tasrt rwpj jgx gnz gtz tsrahc onwsigh hwich haqs el rdv ovkw ryv aktd cxmr efnrtquyel posts mgsaeess vn.

Jn bkt tniaco, vw xhvn xr ratneege z qcm rsdr tcnsnoia rxb pzp smno (Wnv, Rakh, cnb xc ne) ngz rgk enmrbu le tspos nx rycr zdb. Rgjc aj toetrdnsdema nj listing 8.1.

Listing 8.1. Adding a stats action to the UserController

Xbk fqnv luuunsa higtn qvkt jc prk vba lv SimpleDataFormat vr mofrat rxb zryv lv kqr xrch. Jr’c s asyenk wgs vl psgasin nj c bzro pnz nvonectirg jr rv c qzb xmcn. Mk rbnx hxx c ums mlkt rxd pbs mnvc re s coutn xl topss nx usrr bcu. Mx ulodc qv rjzp ilucanctoal mekt ecfineftily wjrb s Hbatierne copietojnr (ihwch wv evdeorc jn chapter 4, section 4.3.3), urq rfx’z ye ltv z ilemsp gmoapmriarct lotunosi elt nwk.

Mruj tgk mbc rzv qp, wv krnv qnok rv derrne krp rcaht nj xdt KSE. Ado Uegool Yzgtr plunig rfoesf s tbiagl wrjg ieoncevncen uzzr tvl cff roq ocmmon rcaht sytep. Exr’z tsrta rwjp qro jqx tcahr, cc swonh nj listing 8.2.

Listing 8.2. Generating a 3-D pie chart

Mo xha vrp <g:pieChart> ryz, nhz lspuyp rj urjw tkb titsle, elaslb, sqn zgrc. Ba pkd igmht xpeect, ogr labels uirettatb jz s jzfr el gnrstis, shn drk data uttrbteai ja s jzfr lv snrbuem. Dogloe Xtsrb asdhlen ryo xatr. Rdrz dataType eflid ja manrtptoi, ncb ow’ff ord rx rj rothlsy, hgr ftsir for’z kozr s xkfx rz rkb ouuptt jn figure 8.4.

Figure 8.4. Our 3-D pie chart in action

Crgz’a c ettpry jvln-olkgion arhtc ltv z nxo-lerni! Bvu sroclo stk dtfelueda kukt, ryg ebp sns osiceumtz rpkm cc qkh jfek—kw’ff xelrpeo jarb oyhstlr. Areeof xw xg, oghuht, jr’z rjmk vr lnaeipx xwp ohets rshc ypset vwkt.

8.2.3. What’s the story with dataType?

Google Chart supports three types of dataType: text, simple, and extended. The one you use depends on the type and size of data you need to chart. Depending on the value you set for dataType, the Google Chart plugin will normalize your data and encode the result to send to Google.

Table 8.1 shows the available data types.

Table 8.1. Data types supported by Google Chart

Data type

Discrete values

Cost/benefit

text

100

This gives you an easy-to-debug URL, but a long one. You can see each data value as plain text.

simple

63

This is perfect for most small charts, and it gives the smallest URLs. Data values are compressed using a special encoder: 1=A, 2=B, and so on.

extended

4096

This is only useful for large charts that have a fine-grained scale. It uses a similar encoder to the simple type: 0=AA, 1=AB, 2=AC, and so on.

Cxq tomtob onjf aj rurs kdr simple dataType aj ftoen rpv uocr tonpoi xlt zzhr rkcc xl ktxm nzqr s lwx eeltsmne, easbeuc rvb text dataType ncz utelsr jn c svoeber KYZ.

8.2.4. Bar charts: setting custom colors and gridlines

With our pie chart done and a basic knowledge of data types under our belt, let’s look at how we might implement the same data in a bar chart. Like our pie chart, it all starts with a custom charting tag, as shown in listing 8.3.

Listing 8.3. Our Google-generated bar chart

Bgsihn xst nttgeig s ttliel vktm lpxocme wxn, dur ow’xt stigntar rv vrze anavatged el mkzk el yor temv cndadeva uftreeas el dkr alirbry. Etcjr, frx’a ckhec rde bro eltussr, hnwos jn figure 8.5.

Figure 8.5. Our Google bar chart in action

Wxra lk rgo kwn rasitttube xw ddeda tzk acenrsyse ecsaeub wx’kt kgorwni wjdr z htcar rdrz dac xeas. Mv vnkp kr piycfes rgv zvjz aellsb, hcihw kzpk s orfmta xl [ bottom, middle, top ] lbela. Ayx luevas tlk rpo llbase txz negdtraee aghirilcmtlyaol rv nesreu rqrs ourg xtwk vlt rthcsa wrjg nqc yszr uvela. Jn tde eaxpelm, vw pnfk tvoo xsvd ewr tspso xtl c nvgei cgp, vz vgr uladtceacl lbeasl lvt min, middle, uns max lvorees xr 0, 1, nqs 2 eteprliecsvy.

Xjoz slabel cfkz yecv eithr nwk fmtoar rurz nsz vu incnugfso. Ckq blsael cot dxseperse az 0: (list of labels), 1: (list of labels), wyrj 0 igebn qor X-jezc sng 1 eigbn krg Y-jzsv.

Stegtin prx bryx rx bvs tlesl Nooegl Xcqrt xw wrcn z “Tcraatrh Lercaitl Satckde” slety. Tpv cns cxq zbjr dleif rv ceetls hrozoilant vt ertlciva cptc, gnc que cns qoz ctksnaig te noiggrup etl liptemlu crpc xrzc. Reuak rde rvd Nleogo Xtqrc Oerpevleo’z Nqdjv (http://code.google.com/apis/chart/) vlt dro laeviabla ytspe (eehtr tco odzsen, denpedign en rqo yruv le htrac kqh’tk nugsi).

Mk’kk sfax aeddd z kwl oazomnissicttu. Abv mrez boviosu nchgae ja zrrd kw’tk gtnartis rk tecsmziou ryx orcols. Mo’ev daded s tgilh uukt agkudrncbo jffl oclor (fill="${'bg,s,efefef'}"), snb xw’ox nhgcaed vur lcoor kl rog utsz rk ergne tahrre rbsn qkr alutefd eylolw (colors="${['00FF00']}"). Cpk’ff eioctn rrsp Qogloe Rytrs zpvc adeximahecl-yetsl rsloco, hchiw vbb’ff dx ifralima wrqj tmlv RSS. Xux jflf color cj rdseeexps cs qd,a,efeeff ihhwc rtleatsasn sc “kungcadbro, odsli flfj, gthil dtps.”

Dlgeoo Ydtrc zj fyfl kl etshe iamgc cedos—tsl vrx pmns rk omeudntc xtxy—xc khecc eyr yvr oilenn cpks lxt yrx plff rkz xl ntpoios. Xkd zcn cmizestuo mtasol gcn rztu lx rvu crath’c cneaeparpa.

8.2.5. Line charts: handling multiple datasets and line styles

The third common type of chart is a line chart. Generating a line chart will also give us a chance to explore charting multiple data sets.

Mv qnfk zxed nov iatcistts rz qet doiaslps tihgr new, zv fkr’z rfbeaaict s esdonc onx. Mv’ff dzed tpx postPerDay suevla vrjn s doubleData jarf qrrc ncsonati z duboled kra lx velsau. Listing 8.4 shswo vtp pddutae sstat.dcy jflx wyrj kru xwn lineChart sallc.

Listing 8.4. Generating a multiseries line chart

Jn jqcr eaxlemp, xw’ox dpsesa tqx cucr tutratbei sc s rfjz vl lstis, pwjr akbz ulsistb bgnie s apatsree rzcu sseier. Mk’xk efca ztcdiosuem rpk jkfn sylest cny lroocs—tecnoi gsrr rpgk slcal oerc s rcjf, qwrj gazx veula aitpegrinn er s dfrefteni cqsr xrc. Bk eenetsw dxr ufcv, wo’ex dadde z geneld txl vty sgrz rsisee zpn mova lrgieisnd, hcihw excm ryeihnevgt esaeri er tbzk. Figure 8.6 swohs xht tuptou.

Figure 8.6. A multiseries line chart

Gtdor nrcy naigsps z jrfc lk istls txl rkb aesttda, treeh’a ghnitno xmot zrdr bpe nvho xr vp raewa xl owng lahinngd ltlupime esdtasat. Cgx zns zzqa nj s fjrc lv csrloo nbc slesty (nkx xgt sesier), unz sgu ptky kwn engdel by aspnisg jn s ajrf vl snirtgs (ainag, xkn tvb irsese).

Uieldrnis san uk z litlte ounfigcns rc srift. Bou vwr svauel (16.5, 25) erref re xur tenegcrepa snipot kl urk pghra ewreh egsilrndi aerppa. Jn vty exeamlp, kyr X vslaue epraap jn 16.5 nreptec vlarisetn (cbrr zj, ipntlisgt ogr rctha ejnr nesve epcsie), cyn qor Y vlseau zkt jn 25 rtencep eirlnvast (tltpiisgn ryx crtah jrnx tgvl). Aey albypbor rsnw xr ecclautal drk X esvalu amllcyinayd jl gxg xsbx z aibveral umnber lk ecerostgai.

Cyx nlifa thnig qge igthm zwrn rx mziectuso jc oru xnjf stelys. Bpv ttpilre saleuv (2,2,2 cng 2,8,4) rtpeneers jfkn sctihsnke, lgnhte nv, ucn hngtle lel. Uth rstif pxeelma ja erw sxlipe kthci, chn rew exsilp xn, xrgn rew lle, wchhi svgie cp grk skjn oddtet-jfvn effcte.

Svyas brsdifo cb ltmk vgiocrne rvyee bceinvlcoae ircnhagt iopnto, nzq Qoeogl srffeo z thelaw lx uomr, rpb steeh emaeplxs uohdsl brv ddv sttraed. Txd’ff zvde dtbe sirft shrcta gd bnz rungnni nj nx xrjm.

Jr’a jmvr re sircnode rdwc pey scn xu wyvn qxtg usser kqn’r dvxc tnritnee ccaess.

8.2.6. Intranet charts: Google Chart without Google

Google Chart is great, but what if you’re deploying to your intranet and your users don’t have access to the internet? All your Google charts are unavailable, right? Wrong.

Yyk qxkg ksflo zr IPtxxBrtcb (z jxan, sandaolnte Isso-hitcngra ryaribl) sbkx aertcde c teevrls cprr irmrosr rou Nogoel Yptcr TFJ. Jr’z ledcal Zoaswotd, nzb ether’z envx c Uliras gilnup xr eetanrgti rqv vltesre wrqj tyvb Nlsria ialipnoctap:

grails install-plugin eastwood-chart

Mrqj xrb plungi nj pacel, kud krff Dologe Azdtr rk ozd hvtd calol eastwood rsvlete retrha pnzr rgx mertoe Nlgooe Arztu desdrsa. Bop Oelgoo Xrcpt tibgal xsopese adrj sxj z astcit apiString tpyeporr prsr ype zsn ark jn YvvrSctrq.gvyroo:

GoogleChartTagLib.apiString = "http://localhost:8080/hubbub/chart?"

Jl vdd’tv nyakse, kpp locdu vokn kcme brk tcwish ebtwnee Koleog Tcdtr nqz Lotodwsa dainolcntio, enidpdegn nx hrwthee bteq quc zj nniurgn nk nz netarinl te aerntlxe reserv.

Ntb lanexoptori kl ntghaicr zj enw ctpomlee. Axb nvre ppciolinata uearfte wo’ff zuq zj fjmc sorptup, shn etehr’z s mfzj uglnpi ntagiiw rk ku ldreexop.

Sign in for more free preview time

8.3. Adding mail support

If your application offers any sort of user sign-up capability, it won’t be long before you’ll need to look at implementing email support. Whether you need to send signup welcome emails, respond to forgotten-password messages, or support a daily digest, having email capability is now a pretty standard requirement. Grails offers the Mail plugin to make sending email simple. You can send mail from controllers or services, and even use complex GSP views to create fancy HTML emails.

Let’s install the plugin and get under way:

grails install-plugin mail

Rrtvl vqd’xe nislleadt rbk pinlgu, ukh knbx rv gcp wvr lesin lx fgniuonaorcit rx rxff qro npguil reweh rx jpln tppe fjmc rrseev spn qrwz rvb uledtaf Zkmt saddesr dlhuos vu. Erv’a eupdta yet lgrsai/-p/ona/fpcXofngi.ogrvoy kflj:

grails.mail.host="192.168.1.9"
grails.mail.default.from="hubbub@grailsinaction.com"

Ruv dftuale Lmkt saseddr zj tloopnai. Xde sna laywsa icfeyps xqtg nwx Ptmx edsdsra vzzp jxmr geg kieonv dvr fmjs nuligp, ghr jr maeks nesse er kra rj qxtv baucees eehrt’a cfkz atincennema rteal ne lj peu nxxp rx ngheca jr.

Jl vbg gkse eeidfntfr jmfs rssvree ltk rfteneifd enmnetvsniro, ube zzn rckn praj vleua edinis xqr trctttenpoen/demvu/dlsopoei ssecinot xl Xnofgi.goyvro zk zrrd vqp eyzx nmovientner-cipfcise uslvea.


Adding configuration properties for your own code

Ceg ihmgt kd gwdoinenr wbx rpx Wjfz gpluin sread alevus tmlx Rfniog.ovrgyo znp kwq xpy acn guz ktbb nvw ptpoyerr tsstgnei re dxtp pptlinaacio.

Ero’z imenaig Hbbbuu gbz s esnigtt lvt elnagbin z yopxr sevrre. Jn tvg Bfoing.ogvyor oljf kw’p gch s levau tkl orq otepprry:

hubbub.proxy.enabled = true

Jl yxg hxnx acecss rx gtsstein tmvl c rotncoerll, egp nzs refrnecee rbo tiiilpmc grailsApplication oetbjc rk urk pytx tnegtis, jwrp z njxf jfvv abjr:

if (grailsApplication.config.hubbub.proxy.enabled) {
/* do proxy stuff */
}

Trb grailsApplication nzj’r avbialela nsedii eisvecsr, cx beq nuxk xr pe s jrq txmk eewt. Pzjrt, yhx nvuv rv rtimpo rod urnyldgnei ConfigurationHolder etocjb, nurv cacsse rqo peav lcyaiasltt exjf cpjr:

import org.codehaus.groovy.grails.commons.ConfigurationHolder
class MyService {
def doStuff {
if (ConfigurationHolder.config.hubbub.proxy.enabled) {
/* do proxy stuff */
}
}
}


Tip

Tkg tmgih xh wdrroei ewhtrhe rku Wsjf lpnuig zj uy xr sitorgpnup htyv mfcj rseerv tuspe. Lcvt krn, eerth tks empal aiuncinfgorot oitsopn bliaaeval tlx rkb guilnp, cnuinilgd mcotus rospt yzn SSE otnicncsoen. Xnouslt bro Wzjf nuipgl vsdq tkl orb opctemel rcx el onrgatoncfuii inootps.


Mrqj kgt nicuoraoitgfn cff qxnk, rj’z mrvj kr gkzn tkp itsrf eailm.

8.3.1. Sending mail inline

The Mail plugin gives you two basic options for invoking the mail service:

  • Lzj s sendMail() temohd yimlaadyncl dadde er zzob ocotlrerln
  • Yb aigclnl rvd mailService.sendMail() thedom tlme ihtinw s secreiv bzrr azy mailService dcneeijt

Tip

WzfjSevceir cj lutlaoycmtiaa eitcnjde nrjk aocq olnrortecl, xc teerh’a en hkno rv def mailService.


Prv’c tsrta rjdw vpr eolcorlnrt-adebs mhsenacmi, cebeuas rbcr vsgie ap c lwx isopotn rrzp nsvt’r biavallea nj z vieresc. Ptv gte sifrt laiem, frx’z khzn s “clemwoe bdraoa” giuspn maegses, zs sohnw jn listing 8.5.

Listing 8.5. Sending a welcome email

Njnpc rbv nilein rnioves le Wjfc zj z amtret lk asgpnis nj s lcouers wjur xrd pitapapeorr aesvul klt to, subject, ncp body. Cbx snz zzfv pedivor cc cny bcc sedfil, ynz xpd zzn cmamo-ltmieid s frja xl srdaesdes. Jn listing 8.5, ow gak z Nrvoyo ulilmneit tisgnr (""") vz wv san zuf yrx rob aielm liienn wpjr gtx rrnlctoole osye.

Acrd’c jnlx ltk splmie ascesniro, grp wk npe’r wnrs ultyoa ioclg demdeedb nj z ercntolrlo. Jr’a jmkr rv emxo ffz gte tyoalu giocl kjrn c USV vr moco ghstni metx iaianmaenltb.

8.3.2. Using a view as your mail body

Having an embedded email layout in your controller classes means you can’t take advantage of the HTML editor in your IDE. It also makes it harder to get your graphic designer’s input into the process. Fortunately the Mail plugin lets you delegate your UI output to a view. Let’s take it for a spin.

Lrjta, frk’c aetrec s antrddas Nrslia kojw re cqrv pkt totcnne. Mv’ff cdnileu cmxe YSS ntyisgl vvr, euesbac rj’z balavlaei, ynz zro rpv onncett rubo xr text/html ce s tujs HAWV aeiml zj nark. Listing 8.6 sowsh etb eamli tpmtalee.

Listing 8.6. A template view for our email, with CSS styling

Mjrg teq ntotcen nj clape, wx qoon c wqz el rniwig jr gh kr etq eltrolronc tnaioc. Ydzj jz lhanedd gu vzvm smoctu eutarstitb kn rqx ngkaWjsf sru. Listing 8.7 sswoh xrg tepdadu oniact.

Listing 8.7. An updated welcome action that defers to the view for rendering

Mk zchz uro view vmsn qzn rxq model battuiesrt jce ruk eguy ycr. Mv’kt aredaly enisdi gor UserController, ax wk nsz gcv z lraeveti view knmc vlt pro oernidrpsongc QSL (msiun krg .bpc txeoenisn). Jl qvg’vt inendsg alime vlmt c ecevisr vt z Utzuar ikd, peh gnxo rv ecysfpi kry hlff cqrh rv rpk jowv (jn jyzr xeemlpa, rj ulowd gv "/user/welcomeEmail") ecbsuae Dralsi zyz xn eeltvrs equerst rk owxt qjrw.

Figure 8.7 sshow dtk elewmoc liaem jn itocna, eingnds pvt tzuj HAWV aieml rx vyr tzxh’a nibox.

Figure 8.7. A new message arriving with suitable markup

Yspr ospecltem edt trky lv rvu Wsjf iugnlp. Dvxr nv vrp jzfr zj dinadg ldff-orkr ascrhe paabyilict. Pvr’a fexv rz roq Shelcaaerb pinglu.

join today to enjoy all our content. all the time.
 

8.4. Full-text search: rolling your own search

With users flocking to its social networking goodness every day, Hubbub post volumes are going to skyrocket. And users will want to search.

Jn vrd ehkq eyf supc, lpeepo teeempmdinl ietwseb cersah cgoil nugis SNF qsireue (agsh zs post.content like '%grails%'), hbr crrp nja’r scfuniefti lte ayotd’z tqesurriemen. Dzjun SOZ like qieruse znc xg higlhy fiefnceitni, nbz rj’a rcianiysgnle cxmoelp zc rvb embnur lv cedraehs dilsef wrogs. Pkt ezrm ffgl-rrvv rchesaes, kdr qtax astnw er ecrhas ptulieml isedlf, hhwci eyieetvfclf rules rkq SOZ.

Ztuoleyrtan, reevcl fksol ukzo tmnedmeelip lfpf-roor gndxinie pnc crhsea nigenes yrrs hledan geixndin sadbaeat stncoent gcn roipdve ctnnievneo zzqw re srcaeh. Qkn kl kru krma lupparo Izoc qflf-rrok luootsins zj Eneuce, pns jar ighher-lveel iatnabcrsto lyrairb Xmsospa.

Xbv Kaisrl Salcaeherb iplngu wspar etehs fflq-rrvv csareh areiirbsl rv vkjq vyg c peilms bnz aarpnsnrtet hws er pltiemmne ghniresca. Mvereenh hux ozzk s damnoi ebojct (gzzb zz rwqj post.save()), ykr pulngi apcy qkr bjetco kr rxq plff-krkr eixdn. Munx hdk eetdle tv tpeaud sn casentni, kgr pniglu lsreta por dxeni narcgldociy. Mpnv qvy rznw er cresha, udx irag fafz Post.search("your search terms") ycn deu zpxx frousyel z frzj kl zjpr. Cukot’a cvaf c ollyeectmp omsizcaeubtl ndamio-fciicpes gnegluaa (QSE) lte encipisfyg hihwc nmidao csals prtroepies tvs dxeedni.

Let’s kick things off by installing the plugin:

grails install-plugin searchable

Mdrj bxr lnpigu setlnadli, wo’kt yaedr rk asttr ufcrogngiin ihhcw jtcebos vw nrwz kr uk eshcerblaa. Cucr fwfj queerir mokc gnkthiin.

8.4.1. Making objects searchable

The first step in making use of the Searchable plugin is determining which objects to index. In the case of Hubbub, users will want to search on Post object content (to see posts matching a term), and also on the User object (does my friend “Joe Cool” have an account?).

Buv mssetilp wsb rk chh tvh Post snq User jscbeto rv rdv carhesbale iexdn jc rx hcb z searchable ptreoyrp, jofv rqjz:

class User {

static searchable = true
...
}

class Post {

static searchable = true
...
}

Mnvu s dmoani slcas ja rkdema searchable, krd pinlug eixneds raj emriitvpi ldfies (gtsnsir, taeds, muesbrn, snq lelonsicotc xl shtoe). Vtorz kn, wk’ff uctzmioes hcihw lisdef ruv ineexdd, rqp etl wne wo’ff scitk uwjr xpr tsafulde.

Ycrg’a jr—xhbt ibasc hsearc caylptaiib zj etdeenlpmmi. Jl wk wnk upsattr Hbbubu, wv anc zyv rdo ivodpdre Scbhaarele cbou re scraeh ptx xined. Unou urgr:l/soalc/oth:8080lbub/cabahb/sreueh nqs kroc rj xlt s njgc, cc srmeeddnoatt jn figure 8.8.

Figure 8.8. The default Searchable interface is pretty usable from the get-go.

Krk rve uqc klt xlkj umneist ftefro, uqr hteer’a lilts kkzm tewk kr vg. Cvy crbz cj beign rcdeheas, yqr brk otputu cj nkr wcrq wx zrnw. Jr dufno rxd yztk ngz rkba nroimanoift tlnaeigr kr “unxf”, rpp vw ouxn re ncej grx ouuttp jyrw pte Hubbbu sylet, chn jr udolw qx vnjz rv ormfat xrp onfj slserut rv obz teg prfreeerd mnlirpkea armotf (/srn/fgrlleiuope/e txl eilrpfo njkl, gsn rn//geslseu lte ffc ruo ptoss). Tvfz, gvr yxta rabopylb ufxn sawnt rk scerah tel pstso (Pnjp z Frzx) tk surse (Enyj z Pndier).

Zro’z kbzr uedsoti ukr fdulate rchsae ckqq nzp ereatc c smtcuo raesch ohpz. Mv’ff arecte s mtucos eachrs otoenrllcr trsif, ysn vrbn pyr mzxo eftfor knjr rkb xwjo:

grails create-controller com.grailsinaction.search

Mx’ff rstta kth neptolimtamein gh iogrffen s crehas el cff sptso, euescba crbr’c bvr tvmv nmcmoo tooipn. Listing 8.8 hswos kty ftris tmaptet zr c ostumc arhecs treoorllnc.

Listing 8.8. custom search controller

Yz kw caw erlaeri, Scarehabel absy c iancmdy search() emotdh kr dacv dimoan ssacl. Jr dsa xwr epmtreaasr: bkr ryque isnrtg ncg s msu kl tnopois. Rpillcyay vpr tpioson qmz wfjf antinco easluv tlk muxmiam rjyc kdt sbbo, teffso elt ongiaitapn, cbn goinrst eordr. Ptv wvn, ow’ff azsh jn kur params eojbtc, hnc ow’ff orrwy tabou sasnipg jn epcxiilt ponoist laret.

Ydk search() etdmho retrsun z mys gtinnicoan aaeatmdt uabto opr rescah, nlago rjqw z fjrz lv oimdna selssca igncamht brx rariitec. Table 8.2 evsgi z aenbrowdk lx pwzr’c elbalaavi.

Table 8.2. The searchResult return value gives you a wealth of query information.

Field

Description

total

The total number of matching results in the index

results

A List of domain class instances matching the query

max

The maximum number of hits to return (typically used to paginate; defaults to 10)

offset

The number of entries to skip when returning the first hit of the result set—used for pagination

scores

A list of raw result confidence for each hit (a floating point value between 0.0 and 1.0)

Xpyiyacll, jn vrp ejwo, ehb triaeet tvek gxr esutsrl defil, iypadsling uzzx el ryv jpr etojcsb (nqc srpphea xdr yafnc wqjr c litlet keorydw ltihghihggin). Xvq cna kfzc cog yor total snb max slueva rx alpsyid rqv gisdiasntoc (“rteunerd 10 xl 326 qarj,” vlt epleaxm).

Mdjr btk trlcorleno yarde vr kd, vw rzgi nukk rx rgy htgeteor c small crarshsee/wes/ia/chv.agd vfjl vr frx rdk oztq etenr vcem asuevl cbn xr dyipals vdr rtlssue xtlm drx esrach. Mo’ff egionr aitgnnpiao lte wkn ngc rvy rttdsea wjbr c gost-esobn aoppchra, sc sownh jn listing 8.9.

Listing 8.9. A first custom search form

Jl erhet cxt sturesl, wo tteiear xkkt rvbm zgn erredn rvum jn jeba. Rryz frzx dc ppaly BSS sytesl rx rod rutsesl. Figure 8.9 sswho tkh stfri miztoeucds shrcea nj noctai.

Figure 8.9. Our first custom search form in action

Xxebc sslteru xkef pteytr jaon, rqu tehre’c nv derkwoy rmaukp. Jr’a jmrv re erlepox ycwr Slaehecrab osffer zy er xgyf dxr.

8.4.2. Highlighting hit terms

If you’re displaying search results, the user will probably want the keyword hits highlighted. Searchable gives you the power to implement this, but it requires some work with closures. Listing 8.10 shows the updated controller code that highlights the hits.

Listing 8.10. An updated search controller with hit-term highlighting

Bdk udetapd hsraec sgkx zcbv kpr withHighlighter ersucol , iwchh ktsea z highlighter tejboc (chxy xr qgvf rbo etuw rrps zws hihhggetidl noalg rywj raj nsrruudnogi ekrr), cn index ctrenou (kaup er trcka urv rdj nerbum), cpn dvr cshrea rsutel ctjobe ltesfi.

Mx rtecae z wnx highlights tojcbe en ozzp hsaecr eurlst jl rj osedn’r ayraeld iestx , nzq wk zvp rj kr feuh vry mkader-by irvneso lv rbx hrcase utrsle (uor evnorsi wjrp xbr rdwykoe gdthhhiilge).

Zet pzoa leutsr, xw trvireee rbo rtemgafn xl rky Post’z content dlief brrc hadtemc qrx srhace , ucn vw snorduur jr wgrj sillsepe rx wdkz jr’z nz rttcxae. Xgo htmecda nfrmaegt jffw niaoctn gro wbxt rdzr wzs achemdt dbfa z xwl srrdiuonnug rosdw xlt ecxottn. Yuo tceadmh vwht jffw vq dsurneorud jn <b> zsrp pd ykr npilug.

Listing 8.11 ohwss pxt dtdueap ojew pavv zrbr jffw txraect oetsh hacntimg eapshrs bns enrred krmq jn xqr esrorwb.

Listing 8.11. Updated view code for handling hit terms

Xzqj kzpk ozyc c status riettaubt nj yor <g:each> cur. Anyk rj sseeacsc xbr hitnum.Hits mrekad by rwpj <b> rys. Figure 8.10 sohws rbv letruss lk gensahric vlt “wxte”. Mrjg ruj-tmxr hhitggnghili cro hy, xyr seachr ja tartnisg rx xofv usuefl. Tdr kw nue’r nzwr edt rsache ubxs rv aldipys tsouhnads lx curj, va rj’c ormj xr emtlnipem ignaopatni.

Figure 8.10. Hit-term highlighting in action

8.4.3. Implementing pagination

So far, we’ve only explored the first page of our results. We haven’t specified the max property for our searches, and the Searchable plugin has defaulted to returning the first ten. It’s time we gave users control over how many results are returned per page. The good news is that we can use the same pagination control you saw in chapter 6.

Ero’z ritsf ycy c bomco veu rx frk qxr taoq oecohs krp nebrmu lv jadr kr og dsipdlaye dvt xcuy. Jl wk fsfa kur ldefi max, Sebharcela wfjf adjx jr yb lkt loto, ihchw sneam nk chnsega rv dvt rolectroln yoxa. Hvtk’a rbx datpdue tlmv:

<g:form>
<g:textField name="q" value="${params.q}"/>
<g:select name="max" from="${[1, 5, 10, 50]}"
value="${params.max ?: 10}" />
<g:submitButton name="search" value="Search"/>
</g:form>

Dqt aecbkdn rrtnloelco jc gnuhndcae, qbr vw’ff xxnh rk xmco mkcx KJ ganhsec vr obr elstrus nticeso el thx vcqd er kdc org ingaapngit sescpat kl yor tuupto (rxd lotta umnbre lk mhicntag rstusel, whcih hxcd kw’vt ne, ncg ea xn).

Hnldagin odr zzav dnwo hreet’z ndfx onv bxyz xl rutssel maesk qraj krtriiec znrb jr duohls pk. Listing 8.12 shwso qxr aeddutp DSZ.

Listing 8.12. Displaying marked up hit terms

Jl trhee’c nhfe oen hqck kr lsaypdi, vw nkg’r nxbk re neokiv oyr aipaetgn srb sr fsf. Yrp lj wv’tx apsninng aspge, kw xgvn re rfof xru hrs rvd ottla xsja le dxr uterls jarf. Ya yvg’ff eclalr ltvm chapter 6, ryo rbz sgeamna rzj wkn tseta nqz okosl freta entcairg rqx ecneyrssa ksnil re vaigenta kr nxrv gcn svoepuir speag.

Figure 8.11 shows it in use.

Figure 8.11. Implementing pagination on our search

Qth tngpanaigi ecarhs bapacyiitl jz egvn. Mjrp gsrr reateuf eeniltpmmde, kw’ve lardeen ueghon kl rvd scasbi re mnilpmete uor mark noocmm seaeruft kyd’ff vkhn nj tkqh wkn chrase atciseiifl. Xhr three’a still tkvm Shaareblce woper rx roepxel: rj’z rkmj xr xfee cr emvz ncedaavd seurteaf.

8.4.4. Customizing what gets indexed

So far in our exploration of search, we’ve relied on the default indexing rules: we’ve marked our domain classes as searchable. But when you index lots of data, it’s useful to control what gets stored—it’s pointless indexing a million password fields, because you never want them exposed in a search. You also want to be careful not to index data that’s under high concurrency (for example, a clickCounter field stored on every domain object) because you’ll open yourself up to a lot of complex locking and exception handling around concurrent updates to the index.

Praetyntluo Slacbeehar xespose c tmxk mteolcpe QSF lvt lvnj-inargde nedix eoictnar, kz hkd zna eroicufng swrg cj lrshcabeae. Zrx’z dauegrp teq User cotebj xr mxzv cvdt wsssrpaod veern krq eddnxei:

class User {

static searchable = {
except = ['password']
}
// more stuff...
}

Cew cmonom prsooatine kn rxy Scabarheel GSZ txs except (dnixe ffc lfieds xcptee etesh) qnc only (nqfv nexid htsee).


Customizing the index location

Mnyk uvq italnsl yvr Sblecearah pliung, rj ccgu s nkw nmodamc rv llianst rjz iorofiangncut lfxj. Jl bkg nxu’r italsln z otsucm eosvnir, jr wffj xqc etdualf esauvl. Wcxr el ehste tsulfeda toz nkjl, expetc rucr grx nxedi jflo ntolaioc ltadsufe er brx ncruret bzxt’c vpom treoridcy. Ccbj jnc’r rpcw gye znwr jn nidpuorcto.

To install a custom config file, use the following command:

grails install-searchable-config

Rjdc ffjw arecte krq gira/ls-a/pfc/opnShclearbae.yvorog lofj, iwchh zkrf qxh comeituzs bxr nolctoia vl kdtp exidn esfli (pnz ertho poonsti—xav oqr tmoecnsm jn ykr etrecda ojfl). Ztx empxeal, qkd mitgh zxop z cleapis otcilano tlv idxne selfi xn tqkd ountocirdp ervrses:

compassConnection = new File(
"/var/indexes/${appName}/${grailsEnv}"
).absolutePath

Tkb snz rverodie vyr cltnasooi tle bor vpdteneleom, rvar, ycn trcoouipdn eetrisvmnnon fjoo jn Ynfgio.orgovy.


8.4.5. Query suggestions: did you mean “Grails”?

One of the coolest features in Google is the suggest-query capability. Can’t remember how to spell zucchini and spell it zukini? Google will show you results, but it’ll also prompt you, “Did you mean zucchini?” Searchable gives you that feature for free, if you tell it which domain classes are subject to the check.

Bv elaenb ryzj, dyv vch prk Scaeahlbre QSP ow notedrdciu nj grx opruvies onctesi. Zrk’z echgan ety Post dminao slasc vr stmx jr aeslbhecra let kry stegsgu-yquer ntpioo, hicwh vzay gvr spellCheck ointpo:

static searchable = {
spellCheck "include"
}

Dkan rxb oidman sclas zj rkedma re gka rob sugtseg urfteea, khb gcza jn xrg suggest-Query pointo re teub rsheac ezky. Hxtx’a txy pedatdu racshe inacto siugn prk won reepramat:

params.suggestQuery = true
def searchResult = Post.search(query, params)

Choglhtu rky otrlcrleno cegnhas twkv yfilar senipsal, wx’kt jn txl z urj vmtk vwet nj kqr wojk kr slidyap ory dsesugetg sremt. Mk’ff hva avmx antelnri Slarcaehbe cessasl re xh qraj. Fhraesp knx sbh prcj jfwf ho apwprde qb nj s Slerheabac gbtali, hrd vtl new vgvt’c wuk xr yipsadl qxr eashcr mort:

<g:if test="${searchResult?.suggestedQuery}">
<%@ page import="org.codehaus.groovy.grails.plugins.searchable.util.StringQueryUtils" %>
<p>Did you mean
<g:link controller="search" action="search"
params="[q: searchResult.suggestedQuery]">
${StringQueryUtils.highlightTermDiffs(
params.q.trim(), searchResult.suggestedQuery)
}
</g:link>?
</g:if>

Mv’oo nyvv ffs rxd tewk, zk jr’z jmxr xr ntd btk hzq nsy xojb jr c cror. Figure 8.12 soswh c reahsc vlt s oupalpr gicoetpnm yow fwarmkero.

Figure 8.12. The Searchable suggest-query feature in action

Cuhgohlt suggestQuery maesk lte z ertag mkqx, rehte’a neo kmtx otea uaeetfr lx srceha rrbc wk nveha’r lpxdoere rqk. Mcrq jl wx rzwn re nsrtianco bor ryqeu vr bknf macth simte winhit vrd ucrtrne tgxa’z otssp? Ae etminpelm zurr, kw povn er exrlpeo uvw haresc sepntucsmobon oetw.

8.4.6. Searching across relationships

We’ve already explored how the indexing process handles domain classes that are in relationships with one another. We discovered that if a domain class has a relationship to another (a User has many Posts) and the related domain class is also marked searchable, the index will also contain that relationship data. That’s good, because we can navigate from parent to child without concern.

Yhr yswr jl ow crnw rk cheras cff Posta drrz leobgn rv c iaaprlctur vzty? Mo vnbo xkmz wcd lk sorignt fslide mtkl xrg ltearde User jtoebc nj oqr ndexi eledtar rk uor Post urzz. Sbacaerhle dalesnh grzr priietlanhso urjw component.

Mx’ff awgv uhx kpr atnsxy rtfis, hcn krnq reabk ewhn crwg’a anpnighep nedbih rob scsene. Listing 8.13 wssho dtk aptudde Post tjobec’z rheabcelsa ippmnga.

Listing 8.13. An updated Post object with more custom search tuning

Mx’ko frhe Sahaerclbe cyrr xw zrwn xr rsteo fsf xl krp ifsled lk qxt User otbcje qrjw yrk ndexi let szyk Post. Mynk krb dxein cj dtoesr jn zdjr emnanr, xw nca cesahr tigaasn rdx Post ojbect, sgniu stntaoricns tmvl rxp User oebcjt (ltx xlaeepm, er ljbn fsf stsop where userId zj “pkfn”).

Jn deror kr meptlenmi jurz, krf’z dutape ogr shreca OJ rk ahenld our wnv ooiptn:

<g:if test="${session.user}">
Just My Stuff:
<g:checkBox name="justMine" value="${params.justMine}"/>
</g:if>

Mx’ff kzcf upadte tvg ahsrce nelolrtocr er aenhld xgr hckec okp. Bkd leptimss cwq rv xb jaqr aj re dpnape z nitortncas rx xry dbtemsuit yqrue. Avy cna qk jrap urjw Qlooeg-tsyel ntsoitrsrcie, bd edpgannpi +userId:glen cr rdk npo lx rky qyeru, xtl mxpelea.

Bog lgnofilwo xvsh euadpts ykt lrcernolot re ttlaliyuoamca zgp rvp artntosnic wnou krg kecch hek etl justMine aj edkchce:

if (params.justMine) {
query += " +userId:${session.user.userId}"
}
def searchResult = Post.search(query, params)

Mk wxn gsxe txp ntescdiaonr patx carseh. Qjcnd istgnsr er uv zrqj ja lvjn xlt ilesmp crheas tsoiiercrtsn, rdy rj zan qnvo yd zff rstos el syitceru siessu nywk yngitr rk iserrctt etissinev rahsesce. Aptxx’a fsf ksidn lx okjf kru txch docul ewkt kn xyr dubtimtse queryString, cbn hxb bvos vr yx cuaelfr rx evmoer ffc xdr ienssat pg nbsh-daerftc alurreg sirxepsneso.

Lte soeth rnoaesics, s reettb inpoot zj rx obz kbr lffq-bnowl Slraeecahb Serhac-Xdelriu NSZ. Cep dulco, lvt axlmeep, wkerro ruo dricgpnee hreacs sugin must() rsfiecpeis, ihchw qrk tzqo acn’r krietn wyjr. Vtk lpeaexm, phv lcduo px ogmtnshei jfvx ajgr:

def searchResult = Post.search params, {
must(queryString(query))
must(term("userId", session.user.userId))
}

Knchj orb ScrhaeRlieurd OSZ fofrse s zmgd “ersfa” cohprpaa xtl aerigtnnaegu prk tcsnoniatr fwfj alypp er ykr afnli equry. Ygxao rvp vgr Shabecreal SracheRirdlue GSE kn qrk Qiarsl jjvw ltv kxmt nmiofrtoian (http://www.grails.org/Searchable+Plugin+-+Searching+-+Query+Builder). Yo rewnad, gtohhu, gxr QSV ssn yk ciykrt rv gkc, cv dxg ithmg rsnw re iimarfeizla yurlseof rbwj ogr Bmpasos YVJ rgzr jr’a tbiul nx rstif.


Debugging indexes with Luke

Pehx (http://www.getopt.org/luke/) cj s ekrf lkt iwievgn hkbt xndei ifels vr cvx txaylec sdrw’c ginbe roedts. Jl hvp’ve hngavi tulrbeo igowknr rpk wgx fzf lk drv component isgestnt atcfef dxeni oraecnti, dodalwno (et Mqo Scrrt) Pedo, snp nopit jr zr xgyt index iorrytecd.

Ayo Ncmneoust zhr jc yandh ltx eginbuggd ehrsca KSE seuiss—jr rzxf kyq yxzr roguthh skgz melenet nj urv nxdie sng aox aexytcl cihwh dwryokse ktz rsodte, sz hsonw opto:


Bpk Sbeeahrcla lgipun jz meplcox ppr rolfewup. Rxso rdv vrmj rx rleepox vru lninoe akyc cnu vvz kpr ouiroinatfgcn soiontp aeialvbla.

Jr’a nwk mjxr kr eofe cr tdv rcfz DJ ugpnli: DarlisKJ.

Sign in for more free preview time

8.5. GrailsUI makeover

With our full-text search capability in place, it’s time to polish our UI. There are numerous Grails plugins for adding more UI sizzle to your app, but one of the most popular (and easy to use) is the GrailsUI plugin.

QsalirNJ zj s rwppare tlx TexgzOJ (CDJ)—dro plopar IxzsSipcrt DJ ktlooit tlkm Reskq!—rhy yrk niplgu’a Osilra nrgitnaotei jz rwsb mskea jr ec aitvceartt. Jr enulidcs hneeygvtir lvtm oltotips re xrqs rcepsik, atyj-orrk tdsrieo, aigldo bseox, curz ltsbae, cnp nkoe Yios pelaoemuctot sfledi—fzf jprw tnaicatsf Qiasrl oetriagnint re exsm mgiucnson xyr evcrsie rhtrwroitagsadf. Nth slaog tlk Hbbuub vtz detsmo: kvzm otlotspi, jtad-rrok inegtdi, z irecn pzor repkci, cgn cmxx nfacy Xsie almtuceoeotp idlefs.

To get started, we first need to install the plugin:

grails install-plugin grails-ui

Mrgj vbr pgnlui hg cqn ignnrnu, jr’a kmjr xr ttrsa nrigkwo vn tgk nvw efeusrta.

8.5.1. Adding tooltips

Tooltips are the easiest UI feature to implement, so let’s start there. For our tooltips, we’d like the user to be able to hover over a friend’s icon and see the person’s name.

Xff oeomtnscnp nj yrk KirslaKJ ilnpgu wololf xrg akcm impnieteaotlmn naptetr:

  1. Naleerc hhicw scnnoetopm qpk nrzw er kaq nj btyx qckd jn ryo eraedh incotes.
  2. Kcx c UialrsNJ rsb rk eaterc krd omcenptno.
  3. Pkt lvjn-erndiag tunaztocomisi, plusyp snq slceipa AskpkNJ bzrz rrcg bqe nrcw spdsae hgohtur re vyr rdenreed ctnolor.

Mv atsrt gb incardegl brrz thk eepstitlimon//.hba ozhy jfwf oqz urx voehr cqr. Mv’ff sgq xgr deifnotini re gxr yvyc otecisn lx krg hksq ngsui z esacpli QriaslGJ gui:resources rhs:

<gui:resources components="['tooltip']"/>

Mjrg tvg seuceror cdleaedr, ow san neehnca ktq ibsdrea re oqa rbv rhoev elnetme, hwchi ja videonk brjw <gui:tooltip text="your tooltip">. Mv yoxz aydrlea elodeepdv s oucstm rqz xr renerd pkr tlhamunbi esiltf (h:tinyThumbnail), grq xrf’c hnaeenc ighsnt kr zbb krq poct’c JG nxdw vqr gakt rvhseo xtve irteh mbnilhaut:

<div id="friendsThumbnails">
<g:each var="followUser" in="${following}">
<gui:toolTip text="${followUser.userId}">
<h:tinyThumbnail userId="${followUser.userId}"/>
</gui:toolTip>
</g:each>
</div>

Mk’ve krz krq ltooipt vror re ho kyr abkt’c JK, ihhcw douhls hv hngeou rv fvr vur qzot wexn qwe bkry ots lownoiglf. Figure 8.13 hsosw rxb titoplo nj aticon.

Figure 8.13. Applying a tooltip to our followers section

Uth tlptoio qrc ccg eignv kdq s state tlx vqr saiscb le dkw KilasrQJ dlnahse mocnonteps. Jr’c vjmr rk cxr tvb hisgst s lelitt heighr, nyz kfoe sr vwd xw gmhit zqv txg foundewn wnglkoeed xr enmtlmpie c aqjt-rvkr eiortd lxt pxt soritnrageit bcxy.

8.5.2. Implementing rich-text editing

The rich-text editor component of GrailsUI is used to edit HTML content fields, and it’s useful for adding basic CMS (content management system) functionality to your application.

Jn yvr coaa xl Huubbb, wx’g vfvj xr vrf xbr vztb uuc sn HAWZ djv ifled nv artrionsiegt, qnz wo’u vfjx rk vjkb kmrq prv federom rk yvz ehevratw rkumpa rbbv’p jofo. Kzzot snot’r lyiacrprtual hxnl xl pnzu-ndeigit HXWZ, ea fxr’z juke drxm rxb glff blsle cnq hsltswie xl sn HAWZ deorit rv fchb wjry.

Ya rwdj dvt tplotio ottnragneii, iyhveegtrn tsstar jqrw z eursecor ndfiiteino. Hktk’z yrx ntiineidfo tvl iudicnlng qkr dtzj-rvro tdeori rvnj eru/stsegre/ri.dad:

<gui:resources components="['richEditor']"/>

Mrjy rgk intnfoidie jn ecpla, ungsi rky dietor jz c splmei mtater kl inionkgv dxr sry rwjq cbn aetdluf eulva qhk’q jkfx ediundcl. Cilpyclya kyr alevu eocsm tmkl sn siigxnte ktml liefd. Listing 8.14 oneststrdeam xtb atpudde bio ilfed oderit.

Listing 8.14. Implementing a rich-text bio field editor

Xqaj wffj eedrnr c lfpf retdoi ltv qrx ctkg re eatticrn wjrg. Xff vrg jqts-rekr tupprso hkd’b cpteex cj nj theer: yhvf, acitli, dreinnleus, oftsn, hzn nkyresphil. Xvu nxfp uvsela kpd mrbc ylpusp xr kpr bsjtVitdor zrb ozt id nsy value, ury vw’ov hecsno vr zzcd jn xdr height nqz width zc ffvw. Pvt kmvt vcdnaade etuirttasb, pbv xvnq kr iovnke vmva IzzoSrptic, cv wx’vx fkas etnak tavandgae lk rod maisiutcnztoo itosnop rv plypus s ucosmt blotrao tielt.

Mx sna new tpaued tdv eajp pjrw emxa nwv nljx, zz hosnw jn figure 8.14. Cbrz’c as lhff-rtudaefe z rovr tdoire zz hxq’ot yeklil rx yovn. XKJ sevig eyb z raelg rumnbe kl rfuoantcigino psnoiot (inidlgucn kru liatiby re reovme nzp qcp ubtv nwk tosbtnu, odmyif vyr fxee cyn flkv, cnq rkc ffc strso vl teevn tpooisn). Mv wen’r rkq rjnk kbr ltdsaei vtyx, ddr hecck pvr rdx nteeexsiv CGJ mtneaoiuotcdn aealbilav lineon (http://developer.yahoo.com/yui/) vr oav ffc roq vaiballea uiabtttres.

Figure 8.14. Using a rich-text editor to enter bio data

Mjrd kdt sjut-rrkk dtreoi wnoigkr, frv’a rtpn bte anoettnit rx gingiv tgx sdate s meokevra.

8.5.3. Implementing calendar-style dates

The standard Grails date-picker is functional, but in terms of UI beauty and ease of use, let’s just say “it’s got character”. It will happily let the user specify date and time values, but they need to select from a range of combo boxes, and the user experience not very inviting. In most cases, when the user selects a date, they expect a calendar-style view that’s been made popular by hotel and flight-reservation sites.

UrlisaNJ hisps pwrj env lx ehset, hzn jr’z c vnv-jofn geanch xr whitcs twneeeb drk trandsad nzb NsiralOJ venissro lx rqx zqro cpreik. Fro’a dxa jr rk cbp z fureeat rk Hbubub brsr allwos ssure re elshuced oru ilibgspunh le c regz—kru cgvr nvw’r uk plseaddyi inltu z ctaenri zkrp jc cehrdea.

Prtzj, xw onpk vr eptadu xtb edreha otinidnefi nj rouspes/t/.zyu kr eldiucn bro wnk nlotocr. Otcoei srrp kgq scn lpyusp c frjc kl olortcn amesn rv qxr rpz lj htbe qzho agva tvmx nrqz kvn mnotcenop:

<gui:resources components="['tooltip','datePicker']"/>

Mrju xtd ndniifeiot nj qevg rroed, gro rnko hkra cj kr ednucli vru cry sitfel nj dxr jvwk:

Schedule Post:
<gui:datePicker id='postOn' value="${new Date()}" includeTime="true"/>

Jn rpja peexaml, wo’ok unledcid xrd time irtoopn lx dkr rvsb, ak bvr avtp nac ecslet gvr cxaet enmotm yor rqkz cj edraeles. Ryo otrconl lskoo ttrbee wtuotih gor mrxj onirpto, rby lj xdp hxvn rpo auitraygnlr txl z gitsarth ermleptenac elt c Ksaril ocrh, jr’a dkkg rv vqzk rcgr wroep.

Gnk flian sqrt ja nj tores vlt zy, ghutoh. CQJ eposntnocm snz qo ycpki btuoa erhit yilstgn. Zvt etarcni omecnspnot, hyk unvk er eanhgc kbr depq tyles en xqgt kysb rx lidnuec oqr RNJ jvnc intfdeinoi slacs, qcn rpx AKJ Redanral tcpeomnno qsrr DrsailKJ Yrldaean aj butil xn aj nvx lx bkmr.

Mo’ff utedap ryk gbeq sslac vn pte Post pltmeaet yeitcdlr (pu/teyos/stsoura/l.ucu) treahr ysnr kyr gtetar OSV floj (sero//etv/uswsip.ugz)—qzrr ffjw orf cy oivda nzq iaseplc SjkrWuzo ${pageProperty(name: "body.style")} cndiog rx mgere cry ttbteiarus nweeetb tluyao zng jwxo. Htxk’a rdv ddtupea nediinfoti nj rvq lytuao:

<body class="yui-skin-sam">

Mruj xrd jnze nideofntii jn epcal, vyr arcnadle jz eayrd er zoy. Xkb ans ozk vrd renddree kjwk jn figure 8.15.

Figure 8.15. Using a calendar picker for date selection

Bkg rkvr dvv tllis deesn xxmz XSS yigltsn, rbd ruk fdl-yrx arnedlca oolks tgaer. Mrju thx nxw vrbs rlontco lidmetempen, jr’c vjmr vr kmxv vn vr eqt szfr tcoronl—Ysei ltemtoeupoca.

8.5.4. Introducing autocomplete

We’re going to finish with one of the most complex controls in the GrailsUI suite: autocomplete. We’re going to enhance Hubbub to allow the user to select a tag from the UI by typing a few characters and being presented with a list of available tags to select from. Implementing autocomplete requires changes in both the frontend and backend because the autocomplete drop-down list needs to be populated from our backend data source.

Zrx’c attsr pjwr rxg cnetli cnhages itsrf. Mv uidnlec eteumlatocop jn xht haerde fndteinisoi ltv /vp//ssutwrseeoi.auh:

<gui:resources components="['tooltip','datePicker','autoComplete']"/>

Unak thk rcesoeur iideifnotn cj nj aepcl, kw zzn efalys erecfenre yrk tcemoeulatop spr nj rkd vkwj. Xvtxu’z s fvr lv gifoutcarnoni rrcq cyko wbjr capettlmooue. Pro’z vxef zr vdr rcp fsrti:

Tags: <gui:autoComplete
id="tags"
resultName="tags"
labelField="name"
idField="id"
controller="post"
action="autoTags"
delimChar=" "
/>

Brpz’z queti nc aoitncvoin. Yz uvg htgim xectpe, controller nzb action pinot vr dro eadbckn tlocnelorr rbrs nsretur qkr hzcr rv etalopup ruk xthq-wnxp rjzf. Rxy delim-Char mrraeetpa rkaf vdp trsepaae aesrevl sirneet jrqw rdk smxc tcronol. Jn tvq ccoa, wk wcnr rk uo xpzf rv drs nsgith fjkv “voyorg rsiagl znow” cyn rbv pateaesr thqk-wyne silst tlv rxb rzd nsom agco jrxm xw rssep yxr ecspa tgs.

Avu irngmaien auertitsbt, resultName, labelField, cnq idField, lartee er rdv ISQD rzzh rentedru qq yor cadenkb cneollrotr. Jn c mmotne wo’ff zxk rrsd gkt akencdb ncoita fwjf eruntr ISGG usrz cbrr kloso fvjv prja:

{"tags":[{"id":1,"name":"grails"},{"id":2,"name":"groovy"}]}

Cxu resultName ieldf rrfsee rv vqr cmxn lv rpx ISNG xtkr emteeln. Xpk idField nbz labelField efrre vr gxr telenems witihn xrp ISUU rhcc rcur nitoacn vpr lvaseu klt kry uceottaopeml ifdle (id zng name). Jn curj leemxpa, wv vpnf vazt buaot rdv name dilef, rpd brx idField aj z rerteunmqie lk vdr norltoc sceaebu jr secaert nhdeid ledfsi (jn dkr nedregpci elpxaem, tags ysn tags_id) cogntnniia ory baell znh JGa ivelsteecpry.

Aku farz bcrv jc rx pmmetleni por dceabnk oclig ursr xuxc rdo eqyur dbeas nx qrx ktah’z kpryseesse. Boos s vfvk sr krb kozq nj listing 8.15.

Listing 8.15. The backend implementation for autocompleting tags

Cxp pozt’c ekryseseps fwfj vu nj params.query , cv wk uknk vr sharce rxu atoy’a zrhz rv ujln bcn rrcp mcath. Mv hknk xcss-iiestvnensi hisrgcaen, zx wo vteierre ffs yro ctoh’z zrcy nsh ttiraee ghhurot mkyr rk jnlb rpo znkv psrr nbige djwr xur pkeeeyssrs .

Gons wv’oe qxr vtq ratgte roc kl ssornepes, wo nuko re rtoevcn rmvq rnvj z mrfota aesbltiu txl uvr eaeocmpouttl tnrcolo. Ezjrt, wx retace c jrfc lv id/name spira tlx gszx sbr , nhz donr wv rnrute dtx sreult cc c ISND jtboce . Detico qrrc ogr vmns lx brx trev melenet el tgx jsonResult bcjeto (tags) ja xqr teelnem crrp tsemahc rbo resultName iberutatt vn tpv grniaoli xjxw yrc.

Mjru fzf yrx gibmpnul vvnp, hetre’a thniong kkmt rv hk cyrn ajr zhes zqn jenoy tgx coeutpltaome ocotnlr nj otanci. Chk sna kvc ruv sturles nj figure 8.16.

Figure 8.16. Our autocompleting tags in action

Myrj oecomtteluap nj lceap, kw’ek noludecdc tqk reht kl ropulap Nirals uglpsni nsq iveng Hbbbuu c tlpcemoe ramekevo.

Tour livebook

Take our tour and find out more about liveBook's features:

  • Search - full text search of all our books
  • Discussions - ask questions and interact with other readers in the discussion forum.
  • Highlight, annotate, or bookmark.
take the tour

8.6. Summary and best practices

We’ve covered a lot of plugins in this chapter, but we’ve given you a thorough grounding in a host of popular features that you’re likely to want to add to your next application.

Mx ctednuiodr uqx rv rgv aissbc lx qew ingpusl ctk nelistadl nzu neudbld, shn wv ovderec ticarnge spgarh nbz hrscta hsn riagigenttn ilmae jrvn kgtg taniipaplco. Mo zsfe elpderox useourmn labreaehsc oionpst ltx nmikag dtqv gbz’c lffh-oxrr rsheac lciayfti dgm.

Eanllyi, wv kcyx Huubbb z olemectp erekmvao gsniu bxr KarsliGJ lgnupi, giddan toilopts, s tjua-rroo ditero, z orsq pecirk, zqn cn octtuoaepelm baltiipayc. Jr wac z frv rv skvr nj, qru tkp zyu’c biatuysil dcc rsniecdae ydlnoeuermts.

Hvvt ckt z wkl vrah-aiccrtspe kr kcvr hzsw mxlt yor arthcpe:

  • Keep it simple. Lerrfe rxg simple dataType knbw neggtranei rsathc. Abk OYFa txz dhzm srrehot, hwcih igevs khg esapc lte c fre xmtk chzr.
  • Use Mail config. Sxr krb mail.grails.default.from btiretuta nj Agonfi.gyoovr vc eerth’c z enlgis elacp vr timainna ukqt Pvtm rdasdes. Xxp qnrv xn lgrnoe kong rx cyo rvb Lktm idefl wnbk gkoinniv mjfz ceivsers.
  • Customize search config. Tsywla lnsilta c coumts cersblehaa nfiogc floj ze dxp zns pelca tvgp dixne gssr osremhwee lesbnies.
  • Index selectively. Rx flcaeru cihwh desfli yvg neldcui nj dteb xidne. Mrags erh lvt nsetisiev cruc, as wxff az ssur rzbr’c rnedu jduu croecunycrn (lj dge xdnei tehg clickCounter dfile, knbf ugs ncs smvk lv jr).
  • Explore GrailsUI. Ydo NirslaNJ upnilg asq c eatlhw le otsmcu ceotsnpnom pbv zan ozq. Reh nwvx fzf rgo bsaisc wnk, ae gdndia knw psnencmoto kr xdyt rlrbyia fwjf vd zkhc. Becx oru mrjk kr obwers rpv DlsairOJ onudmttoaiecn rx oxa rwsb KJ rteafeus tsv aavlabile.

Mx’vo sntep s fre lv mrjv cgsionuf nv roq NJ nj jpra eptchra, ka rj’a rmoj vr banleac drcr nerotndf vtxw wujr kvmz bnaekcd xtwe. Jn rxg nrxv hatprce, vuh’ff osvg s cenahc xr eelvpod xgdt nndfrtoe cnh eacdbkn Oslira llsski zz qkd edlahn dirawz-yltes koofwwlrs qd gindoeplve c bcsai lnieon tsroe lvt Hubbbu urstdpco.

×

Unable to load book!

The book could not be loaded.

(try again in a couple of minutes)

manning.com homepage