GwtQuery is a rewrite of the jQuery popular library with has brought to the GWT world its sexy API and its simplicity for doing complex things.
In this session Manuel will provide an overview of the fundamentals of gQuery, how to setup and use it, and how to write code which being laborious in GWT can be simplified using gQuery.
NATIONAL ANTHEMS OF AFRICA (National Anthems of Africa)
Speed up your GWT coding with gQuery
1. Speed up your GWT coding with gQuery
Manuel Carrasco Moñino
GWT.create 2015
► Run these slides
1/35
2. About me...
Open Source advocate
Vaadin R&D
GWT Maintainer
Apache James PMC
Jenkins Committer
@dodotis
+ManuelCarrascoMoñino
2/35
3. What is gQuery?
Write less. Do more!
An entire GWT rewrite of the famous jQuery javascript library.
Use the jQuery API in GWT applications without including the jQuery,
while leveraging the optimizations and type safety provided by GWT.
3/35
4. It looks like jQuery
The API and syntax of GQuery is almost identical to jQuery.
Existing jQuery code can be easily adapted into GQuery and used in
your GWT applications.
jQuery gQuery
$(".article:not(.hidden)>.header")
.width($("#mainHeader").innerWidth())
.click(function(){
$(this)
.siblings(".content")
.load("/article/"+$(this).parent().id(),
null,
function(){
$(this).fadeIn(250);
});
});
importstaticcom.google.gwt.query.client.GQuery.*;
$(".article:not(.hidden)>.header")
.width($("#mainHeader").innerWidth())
.click(newFunction(){
publicvoidf(){
$(this)
.siblings(".content")
.load("/article/"+$(this).parent().id(),
null,
newFunction(){
publicvoidf(){
$(this).fadeIn(250);
}
});
}
});
4/35
5. But it's much more
It integrates gently with GWT
shares and enhances its event mechanism
knows about widgets hierarchy
GWT Optimizers
Provides a simpler deferred binding mechanism
Provide many utilities for GWT.
Plenty of useful methods
5/35
6. Current gQuery status
Very active development
3000 downloads per month
In the top 5 of the most popular GWT libraries
Stable versions since 2010
Official sponsored & supported by ArcBees
6/35
8. Easy DOM manipulation
GQuery eases traversal and manipulation of the DOM.
Use $() to wrap, find and create elements or widgets
Chaining methods: select & modify several elements in just one line
of code.
friendly css style properties syntax.
Use $$() to set style properties or for animations
It also supports type safe CSS
$(".slides>section").css("background-color","green")
.animate($$("top:+80%"))
.animate($$("top:0,background-color:transparent")).size();
$("<p>Hello</p>").appendTo(".current");
8/35
9. Full GWT Widget integration
Whatever you do with elements, you can do with widgets
Query, Enhance, Manipulate, Modify
Promote elements to widgets inserting them in gwt hierarchy
Improve widgets events
Adding events not implemented by the widget
Buttonb=new Button("Clickme");
b.addClickHandler(new ClickHandler(){
publicvoidonClick(ClickEventevent){
console.log("ClickedonButton");
}
});
//PromoteanyDOMelementtoaGWTpanel
Panelp=$("section.current.jCode-div").as(Widgets).panel().widget();
//UseitasanyotherwidgetinGWThierarchy
p.add(b);
//EnhanceWidgetDOM
$("<ahref='javascript:'>Clickme</a>").prependTo($(b)).on("click",new Function(){
publicbooleanf(Evente){
console.log("ClickedonLink");
returnfalse;
}
});
9/35
10. Event Handling
Handle and fire any native event.
Create your own events.
Support for event name spaces.
Delegate events.
Pass data to listeners using events
Tip: Replacement to EventBus
console.log("Clickoncodetexttoseethefontcolor.");
$("#console").on("foo",new Function(){
publicbooleanf(Evente,Object...args){
console.log(args[0]);
returntrue;
}
});
$(".current.jCode").on("tap","span",new Function(){
publicbooleanf(Evente){
$("#console").trigger("foo",$(e).css("color"));
returnfalse;
}
});
10/35
11. Promises
GQuery implements the promises API existing in jQuery.
when, then, and, or, progress, done, fail, always
Use it as an alternative to nested callback based code.
Declarative syntax.
Can be used in the JVM
//Launchthingsatthesametimewithwhen
GQuery.when(
$(".ball.yellow").animate($$("bottom:0"),1000),
$(".ball.red").animate($$("bottom:0"),2000)
).done(new Function(){
publicvoidf(){
$(".ball").fadeOut();
}
});
11/35
12. Ajax
GQuery provides an easy API for Ajax.
Syntax sugar
Promises
Progress
Works in JVM
$("#response").load("gwtcreate2015.html#hello>div");
12/35
13. Data Binding
GQuery ships an easy data binding system to and from JSON or XML
Light weight implementation: each object wraps a JS object
JVM compatible.
publicinterfacePersonextendsJsonBuilder{
PersonsetName(Strings);
StringgetName();
PersonsetAge(inti);
intgetAge();
List<Person>getChildren();
PersongetPartner();
PersonsetPartner(Personp);
}
Personme=GQ.create(Person.class).setAge(10).setName("Manolo");
console.log(me);
13/35
14. Utilities
GQuery ships a set of GWT utilities which makes your live easier
Avoiding writing GWT JSNI
export, import
Simplifying GWT Deferred binding
browser.isIe, browser.isWebkit ... flags
Browser syntax logging
console.log, console.err ...
Import external JS in JSNIBlocks
JsniBundle
14/35
15. All Right, but how can gQuery help in my GWT project ?
Writing less code !
For certain actions you can save 60-90% of code
Making your code more expressive
Chaining is more declarative.
Promises try to solve nested and complex asynchronous blocks.
Using pure DOM elements instead of creating widgets for everything.
The tendency is to avoid Widgets
Web-components are here
Reusing existing code (js, html)
15/35
16. When can I use gQuery in my GWT project?
1.- Doesn't matter the final GWT architecture of your project
Plain GWT, MVP, GWTP
GXT, MGWT, Vaadin
2.- As usual separate Views from your Business logic.
In your Views: gQuery will help you to enhance & manipulate the DOM
In your Logic: you can use Ajax, Promises, and Json binding and test in the
JVM
16/35
17. I want to write less and do more...
Show me the code
17/35
18. Don't extend GWT widgets to change it's behavior.
Modify the widget when it attaches or detaches.
Modify its DOM
Add effects
Add events
Tip: when GWT attaches/detaches you must set events again
Easy to use in UiBinder classes
Widgetwidget=new Label("Hello");
widget.addAttachHandler(new Handler(){
publicvoidonAttachOrDetach(AttachEventevent){
$(event.getSource())
.append("<span>GWT</span><span>Create</span>")
.animate($$("y:+200px,x:+50px,color:yellow"),3000)
.on("click","span",new Function(){
publicvoidf(){
console.log($(this).text());
}
});
}
});
RootPanel.get().add(widget);
18/35
19. Decouple your widgets
Find any widget of a specific java class.
By default it looks for widgets attached to the RootPanel
Enclose your search using especific selectors like '.gwt-Label' or specific
contexts.
Then you can use those widgets as usual in your GWT application
//YougetalistwithallMyBreadCrumbmatchingtheselector
List<MyBreadCrumb>list=$(".gwt-Label").widgets(MyBreadCrumb.class);
MyBreadCrumbcrumbs=list.get(0);
crumbs.addCrumb("Rocks");
19/35
20. GWT Ajax never was simpler.
Just one line of code vs dozen of lines using RequestBuilder
Reusable responses via Promises
Advanced features
Upload/Download progress, FormData, CORS
Usable in the JVM
//ConfigurableviaPropertiessyntax
Ajax.get("/my-rest-service/items",$$("customer:whatever"));
Ajax.post("/my-rest-service/save",$$("id:foo,description:bar"));
Ajax.loadScript("/my-cdn-host/3party.js");
Ajax.importHtml("/bower_components/3party-web-component.html");
//OrviatheSettingsinterface
Ajax.ajax(Ajax.createSettings()
.setUrl("/my-3party-site/service")
.setWithCredentials(true)
.setData(GQ.create().set("parameter1","value")))
.fail(new Function(){
publicvoidf(){
console.log("Thecallshouldfailbecauseurl'sareinvalid");
}
});
20/35
21. Make your async code more declarative and simpler
Avoid nesting callbacks
Chain promises methods to add callbacks
Reuse resolved promises to avoid requesting twice the same service
resolved status is maintained for ever
Pipe your callbacks
//Wecanreusetheloginpromiseanytime
PromiseloginDone=Ajax.post("/my-login-service",$$("credentials:whatever"));
GQuery.when(loginDone)
.then(Ajax.post("/my-rest-service",$$().set("param","value")))
.done(new Function(){
publicObjectf(Object...args){
returnsuper.f(args);
}
}).fail(new Function(){
publicvoidf(){
console.log("LoginError");
}
});
21/35
22. JSNI sucks, how does gQuery help?
Export java functions to JS objects
This will be covered by JsInterop
But sometimes you will be interested on simply export one method
Execute external functions
Automatically boxes and un-boxes parameters and return values
Wrap any JS object with JsonBuilders
It supports functions
Load external libraries via JsniBundle
JsUtils.export(window,"foo",new Function(){
publicObjectf(Object...args){
returnargs[0];
}
});
Stringresponse=JsUtils.jsni(window,"foo","ByeByeJSNI");
console.log(response);
22/35
23. Simplifying deferred binding
gQuery Browser flags are set in compilation time.
They return true or false, making the compiler get rid of other borwsers
code
Not necessity of create classes nor deal with module files
if(browser.webkit){
console.log("WebKit");
}elseif(browser.ie6){
//Thiscodewillneverbeinchromepermutation
Window.alert("IE6doesnothaveconsole");
}else{
//Thiscodewillneverbeinchromepermutation
console.log("NotwebkitnorIE.Maybemozilla?"+browser.mozilla);
}
23/35
25. How to extend gQuery?
Plugins add new methods to GQuery objects
It's easy to call new methods via the as(Plugin) method
Plugins also could modify certain behaviors of gQuery:
support for new selectors
synthetic events
new css properties ...
publicstaticclassCss3AnimationsextendsGQuery{
//Wejustneedaconstructor,andareferencetothenewregisteredplugin
protectedCss3Animations(GQuerygq){
super(gq);
}
publicstaticfinalClass<Css3Animations>Css3Animations=GQuery
.registerPlugin(Css3Animations.class,new Plugin<Css3Animations>(){
publicCss3Animationsinit(GQuerygq){
returnnew Css3Animations(gq);
}
});
//Wecanaddnewmethodsoroverrideexistingones
publicCss3Animationscss3Animate(Propertiesproperties){
super.animate(properties);
returnthis;
}
}
$(".ball").as(Css3Animations.Css3Animations).animate($$("rotateX:180deg,rotateY:180deg"
25/35
26. How to port a jQuery plugin to gQuery
Gesture Plugin Differences
Take original JS code
Create JsBuider interfaces so as syntax is similar to JS
Set appropriate java types to JS variables
Gesture Plugin Source Gighub
Gesture Plugin Demo
$(".current.jCode").as(Gesture.Gesture).on("taptwo",new Function(){
publicvoidf(){
console.log("DoubleTap");
}
});
26/35
27. What is the future of gQuery
Type safe functions and promises
Full support for java 8 lambdas
Mobile friendly
Integration with JsInterop
GQueryg=$(".ball");
g.each(new IsElementFunction(){
publicvoidrun(Elementelm){
$(elm).animate("scale:1.2");
}
});
g.on("tapone",new IsEventFunction(){
publicBooleancall(Eventevt){
return$(evt).animate($$("rotateX:90deg")).animate($$("rotateX:0deg")).FALSE;
}
});
//PredefinedreturntypesinthegQuerychain
g.on("taptwo",(e)->$(e).animate($$("x:+=50")).TRUE);
//Gettheresultofmultiplecallbacks
$.when(()->"aaa",()->"bbb",Ajax.get("/lambdas.html"))
.done((Object[]s)->console.log(s[0],s[1],s[3]))
.fail((s)->console.log("Fail"));
27/35
31. Example: Wrapping Web Components with gQuery
Use bower to install components in the public folder
bower install Polymer/paper-slider
Use Ajax utility methods to load polyfills and import templates
Web Components can be created and manipulated as any other
element.
We can change its properties or bind events.
Ajax.loadScript("bower_components/webcomponentsjs/webcomponents.js");
Ajax.importHtml("bower_components/paper-slider/paper-slider.html");
GQueryslider=$("<paper-slider/>").appendTo($("#sliders-container"));
slider.prop("value",67);
slider.on("change",(e)->{
console.log($(e).prop("value"));
returntrue;
});
$("#sliders-container").append("<paper-slidervalue=183max=255editable>");
31/35
32. Example: Binding Attributes of Web Components.
JsonBuilder can wrap any JavaScript element
Light Weight wrapper
Type safe
Chain setters
publicinterfacePaperSliderextendsJsonBuilder{
//get/setprefixesareoptional
intgetValue();
//Chainingsettersisoptional
PaperSlidersetValue(intvalue);
PaperSlidermin(intvalue);
PaperSlidermax(intvalue);
PaperSliderstep(intvalue);
PaperSlidersnaps(booleanvalue);
PaperSliderpin(booleanvalue);
}
//Waituntilthepolyfillandthewebcomponenthasbeenloaded
GQuery.when(
Ajax.loadScript("bower_components/webcomponentsjs/webcomponents.js"),
Ajax.importHtml("bower_components/paper-slider/paper-slider.html")
).done(new Function(){
publicvoidf(){
//CreateandappendtheelementasusualingQuery
GQueryg=$("<paper-slider>").appendTo($("#slider-container"));
//WrapthenativeelementinaPOJO
PaperSliderslider=GQ.create(PaperSlider.class).load(g);
//Useitasajavaobject
slider.setValue(300).max(400).step(50).snaps(true).pin(true);
}
});
32/35