24
The Joy of writing JavaScript Applications How Qooxdoo puts the fun back into programming for the web Tobias Oetiker 22nd Large Installation System Administration Conference

LISA Qooxdoo Tutorial Handouts

Embed Size (px)

DESCRIPTION

The Handouts for a halfday qooxdoo tutorial

Citation preview

Page 1: LISA Qooxdoo Tutorial Handouts

The Joy of writing JavaScript ApplicationsHow Qooxdoo puts the fun back into programming for the web

Tobias Oetiker

22nd Large Installation System Administration Conference

Page 2: LISA Qooxdoo Tutorial Handouts

1 Introduction

The Browser: my application platform

• Applications in the Browser: Netscapes original Plan.

• Performance: SquirelFish, V8, Tracemonkey engines.

• JavaScript graduated with Web 2.0

• Widget libraries for breakfast, lunch and dinner.

• A separte hack for every browser.

It is said that people at Netscape back in the nineties had the vision of escapingthe Microsoft dominance by enhancing their browser so that it could become aplatform of its own for running client side applications. It is also said that MS wasnot thrilled by this thought.

Netscape is no more but the vision has become a reality. The Web 2.0 hypesparked a slew of highly interactive web applications that used JavaScript snipetson the browser to enhance the user experience.

Qooxdoo: application in the browser

• Web 2.0 is all about the look and feel.

• Web pages with eye candy.

• Applications running in the browser.

• Back to client/server computing.

• Qooxdoo is a complete environment.

Page 3: LISA Qooxdoo Tutorial Handouts

Qooxdoo features

• Turns JS into a grown up OO language.

• No HTML or CSS knowledge required.

• Cross Browser: >= FF 1.5, Safari 3, Chrome, IE6, Opera8.

• Multilingual.

• Full API Documentation.

• Users works ’inside the box’.

• LGPL, EPL

• Fun.

Qooxdoo is way more than yet another JavaScript widget collection. Apart froma cool collection of widgets, it introduces fully object oriented programming tothe JavaScript world. Similar to the way OO got introduced in the Perl world, theQooxdoo folks designed a framework that provides all of the OO bits that wereleft out of JavaScript’s initial design.

2

Page 4: LISA Qooxdoo Tutorial Handouts

2 Hello World

Jump right in

• Install Python. http://www.python.org/download/

• Unpack Qooxdoo. http://qooxdoo.org/download/

• Get Js2-mode for emacs http://code.google.com/p/js2-mode/

• Do this NOW!

Some claim Qooxdoo has a steep learning curve since it does just publish someJavaScript files you can link into your web page. While there are ways todo this allthe same, I think it is actually a good thing since Qooxdoo’s main objective is toprovide an environment for writing standalone, browser based applications. Withsuch a scope in mind, the developer should treat herself to a decent programmingenvironment.

Generating the first application

• Point your path to qooxdoo-0.8-sdk/tools/bin

• Change directory to your development space.

• Run create-application.py -name hello

• CD into the hello directory.

• Run generate.py source

• Point your browser to hello/source/index.html

Qooxdoo comes with many sensible defaults. One could argue, that a lot of Qoox-doo’s appeal comes from the many defaults. Normally when I start to write a pro-gram from scratch I am faced with way too many decisions at once. I often spendconsiderable time mulling about seemingly trivial decisions instead of just start-ing to program. Qooxdoo takes a lot of this “freedom” away by setting a standardon how to write your application. Many of these defaults can be changed, but Ifound that they are actually quite a good aproximation to my optimal program-ming environment, so there is no immediate need to change them.

3

Page 5: LISA Qooxdoo Tutorial Handouts

generated files

hello/generate.pyhello/config.jsonhello/source/resource/hello/test.pnghello/source/translation/readme.txthello/source/class/hello/test/DemoTest.jshello/source/class/hello/Application.jshello/source/index.htmlhello/Manifest.jsonhello/readme.txt

source code: hello/source/class/hello/Application.js1 /* Tell qooxdoo that we need the resources in hello/*2 #asset(hello/*)3 */4 qx.Class.define("hello.Application",5 {6 extend : qx.application.Standalone,7 members :8 {9 main : function()10 {11 // Call super class12 this.base(arguments);13 // Enable logging in debug variant14 if (qx.core.Variant.isSet("qx.debug", "on"))15 { // native logging capabilities16 qx.log.appender.Native;17 // additional cross-browser console.18 // Press F7 to toggle visibility19 qx.log.appender.Console;20 }21 // Create a button22 var button1 = new qx.ui.form.Button(23 "First Button", "hello/test.png");24 // Document is the application root25 var doc = this.getRoot();26 // Add button to document at fixed coordinates27 doc.add(button1, {left: 100, top: 50});28 // Add an event listener29 button1.addListener("execute", function(e) {30 alert("Hello World!");31 });32 }33 }34 });

The original Qooxdoo hello world application, modified to fit the slide.

4

Page 6: LISA Qooxdoo Tutorial Handouts

3 The finer Points of JavaScript

Qooxdoo and JavaScript

• It’s just like Perl and OO

• It’s all about anonymous functions

• and closures . . . and scoping.

A function pointer example1 var add_alert=function(a,b){2 alert(’The Result is: ’+(a+b));3 }4 add_alert(1,2);

Calling function creates a function object which I can use exactly like a normalfunction.

Scoping for the naïve

You know scoping from other languages1 var j = 100;2 var z = 22;3 for (var z = 1;z<10;z++){4 var j = z;5 }6 alert(’And the Winner is j:’+j+’ z:’+z);

Or so you thought

5

Page 7: LISA Qooxdoo Tutorial Handouts

Scoping for the disillusioned

JavaScript scoping works only within function blocks.1 var j = 100;2 var z = 22;3 for (var z = 1;z<10;z++){(function(){4 var j = z;5 })()}6 alert(’And the Winner is j:’+j+’ z:’+z);

Note that z is outside the function block!

While many languages provide scope for every block, this is not the case forJavaScript. Here the only scoping block is the function block. So if we needscope inside a looping block for example, we just add an anonymous function andexecute it in place. I am not sure how efficient this is, so do not use it in tightloops.

A simple closures example1 var set;2 var get;3 var j=2;4 (function(){ // the magic javascript scope trick5 var j;6 set = function(x){j=x};7 get = function(){alert(’Hidden j is ’+j)};8 })(); // end of scope9 set(33);

10 get();11 alert(’Outside j is still ’+j);

Everything as expected.

6

Page 8: LISA Qooxdoo Tutorial Handouts

With closures, several functions can share a common variable defined outside thefunction. Lisp/Scheme has been the first language to implement this concept. To-day many object oriented languages can do closures: There are subtle differencesthough, as I had to discover the hard way, working with JavaScript coming froma Perl/C background.

A broken function factory1 var j = [];2 for (var z = 1;z<10;z++){3 var g = z;4 j[g] = function(a){5 alert(’The value for j[’ + a + ’] is ’+g);6 };7 }8 j[2](2);

JavaScript! Has! No! Scope! For! Loop! Blocks!

A working function factory1 var j = [];2 for (var z = 1;z<10;z++){(function(){ // for a block3 var g = z;4 j[g] = function(a){5 alert(’The value for j[’ + a + ’] is ’+g);6 };7 })()} // end of block8 j[2](2);

Again the anonymous function trick comes to the rescue.

7

Page 9: LISA Qooxdoo Tutorial Handouts

When programming with Qooxdoo its almost entirely about anonymous functionsand closures. It took me almost a year to finally figure out about JavaScript scop-ing, before it was just the odd problem which I could not really fix and spenthours working finding a work-a-round, cursing mysterious JavaScript bugs in theprocess.

fun with this1 var hello = {2 world: ’You’,3 show: function(){4 alert(’Hello ’+this.world+’ this: ’+this); }5 };6 hello.show(); // method call7 var plain = hello.show; // function pointer8 var world = ’Me’; // change this.world9 plain(); // method call

this refers tothe hello object.

this refers to the browsersbase Window object.

Be careful when using this in an anonymous function since it always points tothe current parent. Call-back functions are especially ’vulnerable’ to this problem.

8

Page 10: LISA Qooxdoo Tutorial Handouts

4 The Qooxdoo OO Features

Class definition

In its most basic form, a Qooxdoo class is very simple.1 qx.Class.define(’my.first.Class’);

In reality you would use something like this1 qx.Class.define("my.cool.Class", {2 // declare constructor, members, ...3 });

A regular class can then be instantiated1 var myClass = new my.cool.Class;

Class inheritance

The map contains the meat of the class.1 qx.Class.define("my.cool.Class",2 {3 extend : my.great.SuperClass,4 construct : function() { ... },5 destruct : function() { ... }6 });

Embrace and extend.

Static members

Static member names are in the statics key. They use UPPERCASE names byconvention.

1 qx.Class.define("my.cool.Class", {2 statics : {3 FOO : VALUE,4 BAR : function() { ... }5 }6 });

Static members are accessed with their full name:1 my.cool.Class.FOO = 3.141;2 my.cool.Class.BAR();

9

Page 11: LISA Qooxdoo Tutorial Handouts

Instance Members

Instance members reside in the members map.1 qx.Class.define("my.cool.Class", {2 members: {3 foo : VALUE,4 bar : function() { ... }5 }6 });

Use new to create an instance.1 var myClass1 = new my.cool.Class;2 myClass1.foo = 3.141;3 myClass1.bar();

Calling the Superclass1 qx.Class.define("my.cool.Class",2 {3 extend : my.great.SuperClass,4 construct : function(x) {5 this.base(arguments, x); // superclass constructor6 }7 members : {8 foo : function(x) {9 this.base(arguments, x);10 }11 }12 });

The this.base construct works for both constructor and member functions.

The arguments object/map used in this example is a native JavaScript feature.Inside a function call it contains all the information about how the function wascalled: a list of the arguments passed to the function as well as pointers to thefunction itself.

10

Page 12: LISA Qooxdoo Tutorial Handouts

Generic access to static members1 qx.Class.define("my.cool.Class",2 {3 extend : qx.core.Object,4 construct : function(x){this.base(arguments,x)},5 statics : {6 PI : 3.1417 }8 members : {9 circumference : function(radius) {10 return 2 * this.self(arguments).PI * radius;11 }12 }13 });

this.self only works for subclasses of qx.core.Object

mixins1 qx.Mixin.define(’my.cool.MMixin’,{2 // code and variables like in a class3 });

Like classes, but without inheritance. By convention Mixin names start with ’M’.1 qx.Class.define(’my.cool.Class’, {2 include : [my.cool.MMixin, my.other.cool.MMixin]3 ...4 });

Include mixins when creating new classes.1 qx.Class.include(qx.ui.core.Widget, qx.MWidgetFeatures);

Or even inject them into an existing class.

A mixin may contain anything a normal class can contain, but it does not getinstantiated or inherited from directly. Is included into new and existing classes toprovide additional features through its methods and properties.

class access controlThere is the following naming convention for class members.

1 publicMember2 _protectedMember3 __privateMember

In the Qooxdoo build process it is optionally possible to randomize the names ofprivate members to protect access.

11

Page 13: LISA Qooxdoo Tutorial Handouts

static, abstract and singleton classes1 qx.Class.define(’my.static.Class’, {2 type : ’static’3 statics: { ... };4 });

Neither members nor constructors are allowed in static classes.1 qx.Class.define(’my.abstract.Class’, {2 type : ’abstract’3 });

Abstract classes must be sub-classed for use.1 qx.Class.define(’my.singleton.Class’, {2 type : ’singleton’3 });4 var instance = my.singleton.Class.getIntance()

There is only one instance which gets created on the first call.

Browser specific code

Normally Qooxdoo takes care of all browser differences, but if you must intervene. . .

1 members: {2 foo: qx.core.Variant.select(3 ’qx.bom.client.Engine.NAME’, {4 ’mshtml|opera’: function() {5 // Internet Explorer or Opera6 },7 ’default’: function() {8 // All other browsers9 }10 }11 )12 }

12

Page 14: LISA Qooxdoo Tutorial Handouts

5 Working with Qooxdoo

The demo Browser

1 $ cd $QX/frontend/application/demobrowser/2 $ ./generate.py build3 $ gnome-open build/index.html

Or surf to http://demo.qooxdoo.org/current/demobrowser

For me the demobrowser is the quickest way of seeing how to write Qooxdoo.Select a widget on the tree at the left and activate the JS Code toggle. Now youcan see both the running program as well as the JavaScript code. The rest is mostlycut and paste.

13

Page 15: LISA Qooxdoo Tutorial Handouts

The API Documentation

1 $ cd $QX/frontend/framework2 $ ./generate.py api3 $ gnome-open api/index.html

Or surf to http://demo.qooxdoo.org/current/apiviewer

The Qooxdoo API documentation is generated directly from embedded javadocin the Qooxdoo JS source files. You can apply the same process to your ownQooxdoo application to get a api viewer for your own code.

The Qooxdoo generatorPython is the sole dependency

• generator.py is the tool

• it gets called by generate.py

The generator has many functions

• source - prep development code

• build - prep code for deployment

• api - build api doc

• lint - check your code for beauty

• pretty - fix the code layout

• . . .

14

Page 16: LISA Qooxdoo Tutorial Handouts

Running your Qooxdoo program in source

Use source code during development1 $ cd hello2 $ ./generate.py source3 $ gnome-open source/index.html

As long as you do not use any new classes, press reload in the browser to seechanges.

To run a Qooxdoo application, the code for each class you used must be loaded.This can easily be 30 or more files. When calling the generator with the optionsource it will create an JavaScript file in source/script/hello.js whichtakes care of loading these class files. While developing you may want to try thelint option as well, to catch some frequent mistakes.

Deploying your Qooxdoo program1 $ cd hello2 $ ./generate.py build3 $ cp -rp build ~/public_html/hello

• only two js files

• code gets optimized and compressed

• no external dependencies

The Qooxdoo generator builds a fully custom js file containing all the Qooxdooclasses required to run your application. It also compresses and optimizes thecode in this step. You will notice that the first source and build run will take quitesome time. This is because Qooxdoo creates cache files of all classes involved. Ifyou run the build for a second time things will run much quicker.

15

Page 17: LISA Qooxdoo Tutorial Handouts

6 Programming with Qooxdoo

Button, TextField and some Action1 // Create a textfield2 var tf1 = new qx.ui.form.TextField(’Demo Text’);3 // Add button to root4 root.add(tf1, {column: 0, row: 0});5 // Create a button6 var bt1 = new qx.ui.form.Button(7 ’Open Alert’, ’lisa08/test.png’);8 // Add button to root9 root.add(bt1, {column: 1, row: 0});10 // Add an event listener11 bt1.addListener(’execute’, function(e) {12 // closure !!13 this.info(’TextField: ’+tf1.getValue());14 alert(’TextField: ’ + tf1.getValue());15 });

Try F7 to see inline console!

In this first example there is already a closure. The variable tf1 is used insidethe event handler. The function is passed as a reference and takes the access to theTextField object with it.

The Layout Manager

• Qooxdoo Widgets can contain other widgets.

• Layout manager positions child widgets.

• qx.ui.container.Composite basic

• qx.ui.container.Scroll draws scroll bars

• qx.ui.window.Window directs children to an inner composite pane.

• Layout manager set at construction time

• Modified with setLayout method.

16

Page 18: LISA Qooxdoo Tutorial Handouts

Container and Layout1 // a container with horizontal layouyt manager2 var hbox = new qx.ui.layout.HBox();3 hbox.setSpacing(4); // set property4

5 // assign layout6 var ctr1 = new qx.ui.container.Composite(hbox);7 ctr1.setWidth(600); ctr1.setHeight(40);8 // layout properties: position9 root.add(ctr1,{column: 0, row: 1, colSpan: 2});

10

11 var tf2 = new qx.ui.form.TextField(’Some More Text’);12 var bt2 = new qx.ui.form.ToggleButton(’AllowGrowY’);13 bt2.addListener(’changeChecked’, function(e) {14 // modify widget property15 tf2.setAllowGrowY(e.getData());16 this.info(’New Value for AllowGrowY: ’+e.getData());17 });18 ctr1.add(tf2);ctr1.add(bt2);

The container widget together with an associated layout object can arrange wid-gets on screen giving the user high level control over the operation. Here the tog-gle button lets us choose if the text field should grow vertically to fill the availablespace or not.

17

Page 19: LISA Qooxdoo Tutorial Handouts

Grid Layout

• qx.ui.layout.Grid

• fully dynamic

• ideal for dialogs

• one widget per cell

• row and column spans

• minimal and maximal column and row sizes

• fixed row and column sizes

The grid widget has all the flexibility of a html table plus a great deal more. It isthe ideal basis for laying out dialog boxes or complex screen setups.

About the Qooxdoo Layout Widgets

• A container widget needs a layout manager to place its children.

• The layout manager object has properties.

• Every widget has basic properties like: alignment, growability, shrinkabil-ity, stretchability, margins, padding, width and height.

• Each widget can have layout-specific properties.

• Layout properties get checked as the widget is added to a layout.

Lets play with the layout demos!

For me the best way to understand how layouts work was to first try them out inthe demo browser and then use them in a little program of my own.

18

Page 20: LISA Qooxdoo Tutorial Handouts

Localized Applications1 var lmgr = qx.locale.Manager.getInstance();2 var bt3 = new qx.ui.form.ToggleButton(3 this.tr(’Translate!’)4 );5 root.add(bt3, {column: 1, row: 3});6 bt3.addListener(’changeChecked’, function(e) {7 var lang = e.getData() ? ’de’ : ’en’;8 lmgr.setLocale( lang );9 this.info(’Language set to: ’+lang);10 });

• add locale to config.json

• ./generate.py translation

• translate de.po

• ./generate.py source

Qooxdoo locale files are normal .po files. You can use any of the existing kde/g-nome tools for updating your translations. Qooxdoo will automatically pick thelanguage that best matches the locale settings in your browser.

calling code on the server

• JSON RPC for transport

• various language bindings

• often minimal server code

• async with callbacks

• qx.io.Rpc

19

Page 21: LISA Qooxdoo Tutorial Handouts

An RPC Example: Client1 var rpc = new qx.io.remote.Rpc(’jsonrpc.cgi’,’myclass’);2 var bt4 = new qx.ui.form.Button(this.tr(’Call RPC’));3 root.add(bt4, {column: 1, row: 4});4 var that = this; // we want this ’this’!5 var callback = function(result, ex, id) {6 that.RpcRunning = null; // free the reference7 if (ex == null) {8 alert(’The RPC call returned: ’+result);9 that.info(’RPC call returned: ’+result);10 } else {11 alert(’Async(’ + id + ’) exception: ’ + ex);12 that.error(’Async(’ + id + ’) exception: ’ + ex);13 }14 };15 bt4.addListener(’execute’, function(e) {16 that.RpcRunning = rpc.callAsync(17 callback,’mymethod’,’Hello’);18 });

The example only works on a cgi enabled webserver.

An RPC Example: Server CGI

source/jsonrpc.cgi:

1 #!/usr/bin/perl -w2 use strict;3 use lib qw(perl);4

5 use CGI;6 # use CGI::Fast;7 use CGI::Session;8 use Qooxdoo::JSONRPC;9

10 # $Qooxdoo::JSONRPC::debug=1;11 my $cgi = new CGI;12 # while (my $cgi = new CGI::Fast){13 my $session = new CGI::Session;14 Qooxdoo::JSONRPC::handle_request ($cgi, $session);15 # }

For best performance use CGI::Fast and configure CGI::Session to keepthe session data in a database.

20

Page 22: LISA Qooxdoo Tutorial Handouts

An RPC Example: the myclass service

source/perl/Qooxdoo/Services/myclass.pm:

1 package Qooxdoo::Services::myclass;2 use strict;3

4 # for now let everyone access the methods5 sub GetAccessibility { ’public’ };6

7 sub method_mymethod {8 my $error = shift;9 my $arg = shift;10 return ’The argument was: ’.$arg;11 }12

13 1;

The myclass module gets loaded dynamically.

An RPC Example: Getting it to work

• Install language bindings from Qooxdoo website.

• ln -s /var/www/lisa08 build

• ./generate.py build

• cp -rp source/jsonrpc.cgi source/perl build

• Open the application in the browser.

• Make sure the cgis are actually executed

• Watch the Apache error log while testing.

Debugging client/server AJAX applications can be pretty daunting. I find that Ihave the best success when I keep my eyes on the Apache error log and add debug-ging output liberally. For the Perl bindings, setting $Qooxdoo::JSONRPC::debugto 1 gives also valuable debugging output from the Qooxdoo Perl modules pointof view.

21

Page 23: LISA Qooxdoo Tutorial Handouts

Organizing the code into multiple classes

• Object orientation “by the book”.

• One file per class.

• Java’s file name based approach.

• Supported by the generator.

• Ideal for code re-use.

• Use Inline Docs!

• ./generate.py api

The textclick class1 /**2 * textclick combines a textfield and a button.3 */4 qx.Class.define("lisa08.ui.textclick",5 {6 extend : qx.ui.container.Composite,7 /**8 * @param button_text {String} button text.9 */10 construct : function(button_text) {11 this.base( arguments,12 new qx.ui.layout.HBox().set({spacing: 4})13 );14 this.__tf = new qx.ui.form.TextField();15 this.__bt = new qx.ui.form.Button(button_text);16 this.add(this.__tf);17 this.add(this.__bt);18 },19

20

21 members :22 {23 /**24 * Get a handle to the Button widget.25 */26 getButton: function() { return this.__bt },27 /**28 * Get a handle to the TextField widget.29 */30 getTextField: function() { return this.__tf },31 __bt: null,32 __tf: null33 }34 });

22

Page 24: LISA Qooxdoo Tutorial Handouts

Using the textclick class1 var mywi =new lisa08.ui.textclick(2 this.tr(’Copy Text from Example 1’));3

4 mywi.getButton().addListener(’execute’, function(e) {5 mywi.getTextField().setValue(tf1.getValue());6 this.info(’Set textfield to ’+tf1.getValue());7 });8

9 root.add(mywi,{column: 0, row: 5, colSpan: 2});

By splitting your application into different classes, your code becomes simpler tounderstand and to test, you can even re-use it in other projects.

The generator tool will merge your own classes with the relevant Qooxdoo Classesinto optimized, monolithic JavaScript files, ready for deployment.

The Message Bus

• Communication in “large” applications.

• Singleton message bus class.

• Careful with references: memory leaks!

1 var mywi2 =new lisa08.ui.textclick(2 this.tr(’Send Hello to Textfield’));3 var bus = qx.event.message.Bus.getInstance();4 mywi2.getButton().addListener(’execute’, function(e) {5 bus.dispatch(’mybus.1’,’Hello World’);6 this.info(’Sent Hello World on this bus’);7 },this); // context provided for console8 bus.subscribe(’mybus.1’,function(m){9 mywi2.getTextField().setValue(m.getData());10 this.info(’Got ’+m.getData()+’ from the bus’);11 },this);12

13 root.add(mywi2,{column: 0, row: 6, colSpan: 2});

While a message bus can provide a nice way to get inner-application communica-tion organized, you may soon end up in a maze of different messages being sentback and forth to no end. A better way to organize an applications logic may beprovided by the qx.util.fsm class which lets you describe the logic in formof an finite state machine which then takes care of how your gui works. There isa nice example in the Qooxdoo api viewer.

Tobias Oetiker <[email protected]>

23