Finding Closure from jquery

Starting to use Closure Tools with:

plovr - PlastronJS - the G

Who is this guy?

Rhys Brett-Bowen

Closure Compiler

Advanced Optimizations

  • Aggressive renaming
  • Dead code removal
  • Function inlining
  • sourcemaps

What does this mean?

  • More code - less bytes
  • We can be verbose - the code won't be
  • We can use longer name(space)s
  • Unused code is removed
  • Code will run faster

It's awesome!

Prove it!

What's the catch

What's the catch?

Code must be AO compatible
  • foo['bar'] !== foo.bar
  • {foo: 'bar'} becomes {a: 'bar'}
  • but {'foo': 'bar'} stays {'foo': 'bar'}
Use property when internal only and strings when value is external. Read more

What's the catch?

Code must be AO compatible
var Class = function() {};
Class.number = 1;
  • must provide Class.number to use externally (enums)
  • goog.provide('Class.Number')
    /** @enum {number} */
    Class.Number = {
        ONE: 1,
        TWO: 2
    };
  • provide access through prototype
  • put under another namespace

What about using libraries that aren't compatible...

Closure Library

Built for the compiler
  • Classical inheritance
  • Type annotations
  • Dependency management
  • goog.ui.Component

Classical inheritance

don't be afraid
goog.provide('Class');
goog.require('Super');
/**
 * @constructor
 * @extends {Super}
 */
Class = function() {
    goog.base(this);
};
goog.inherits(Class, Super);

/**
 * @inheritDoc
 */
Class.prototype.method = function(arg) {
    return goog.base(this, 'method', arg);
};

Classical inheritance

Looks long, but autocomplete!
  • Code organization
  • Easy override functions
  • Well understood
  • Performance!

Type annotations

  • Annotating
  • Optional... mostly
  • Don't need to be explicit
We'll see some in the code

Dependency management

goog.provide('namespace');
goog.require('othernamespace');
uncompiled
  • goog.provide will create the objects needed for the namespace if they don't exist
  • goog.require calls in files from goog.addDependency if not already done
  • Need the goog.addDependecy file made using depswriter.py... or let plovr do it
compiled
  • compiler will include all dependencies in a single file in the correct order
want Dependency Injection? try Loader

WeatherDemo

Plovr

no egrets

Setup with Plovr

  • bundles compiler, templates and library
  • Simple configuration
  • Test runner
  • Documentation generation
  • Modules

WeatherDemo config

{
    "id": "weatherdemo",
    "paths": ["js/", "lib/", "templates/"],
    "inputs": "js/main.js",
    "mode": "ADVANCED",
    "level": "VERBOSE",
    "output_file": "compiled.js"
}

add in to project

add in to index.html
<!-- or mode=compile -->
<script src="http://localhost:9810/compile?id=weatherdemo&mode=raw"></script>
run from the command line
java -jar plovr.jar serve weatherdemo.json
Done!
... or create the compiled js file
java -jar plovr.jar build weatherdemo.json

Compiling might take some time...

but it's worth it, so use your time wisely

Setup a main function

main.js
goog.provide('WeatherDemo.main');
WeatherDemo.main = function() {};
// stops the name from compiling
goog.exportSymbol('WeatherDemo.main', WeatherDemo.main);
index.html
<script>WeatherDemo.main();</script>

Back to jQuery

jQuery has a great interface

$('.class').children().addClass('classChilren');
            

Closure Library...

  • Namespacing great for discoverability
  • Functional instead of OO
but...

goog.array.forEach(
    goog.dom.getChildren(goog.dom.getElementByClass('class')),
        function(child) {
            goog.dom.classes.add(child, 'classChildren');
        });
            

Learn a new library?

or just use an interface...

$('.class').children().addClass('classChilren');
            
The G!

Using the G

goog.require('G');
  • Just like jQuery - almost
  • use $$.method instead of $.method
  • $$.setSelectorEngine(engine) for better selector support
  • Even some (simple) jQuery plugins may work - just add to $$.fn

PlastronJS

PlastronJS

MVC for Closure Library
  • Define schemas on models
  • Controls built on goog.ui.Component
  • Control level event system
  • Convenient model bindings
  • Mediator
  • Store
  • also router and syncs

Schema for models

WeatherDemo.model.City.Schema = {
  'celcius': {
    get: function(temp) {
      return 5 / 9 * (temp - 32);
    },
    /** @this {mvc.Model} */
    set: function(num, opt_silent) {
      this.set('temp', num * 9 / 5 + 32, opt_silent);
    },
    require: ['temp']
  }
};
or convenience functions
this.alias('farenheit', 'temp');
read documentation for more

Mediator

  • app wide messaging
  • namespace messages
  • can register objects to broadcast, then will decorate with broadcast method
  • can initialize and dispose based on available message
  • message matching with customizable wildcards
WeatherDemo.Mediator.register(this, 'chooseCity');
// then
this.broadcast('chooseCity', this.getModel().get('id'));
WeatherDemo.Mediator.on('chooseCity', function(id) {...});

Store

  • single store for retrieval of models
  • can create models given a type
  • will retrieve models given an id (and update new models id when given)
WeatherDemo.Store = new mvc.Store(WeatherDemo.model.City);
WeatherDemo.Store.get(city['id']);

Let's talk about components

  • mvc.Layout built on goog.ui.Component
    • Component level events
    • Handy selectors
    • More to come
  • mvc.Control built on mvc.Layout
    • bindings to a model
    • can handle models and collections

2 types of component

// render to create a component DOM structure
myComponent.render(el);

// this will fire:
myComponent.createDom(); // create the DOM structure, and set this.element_;
// decorate to use existing DOM structure
myComponent.decorate(el);

// this will fire:
myComponent.canDecorate(el);// return if DOM structure is eligible
myComponent.decorateInternal(el); // any transforms on the DOOM needed
Then enterDocument() will fire.
Component Lifecycle

Control events

  • All control events use event delegation (to root element)
  • Handlers are fired based on priority first (then order)
  • returns an object that can .fire() the handler and .off()
this.on(eventType, fn, opt_selector, opt_handler, opt_priority).fire();

Convenient model bindings

  • bind methods bind to model (and return bound event objects)
    • this.bind - mvc.Model
    • this.bindAll - mvc.Model
    • this.modelChange - mvc.Collection
    • this.anyModelChange - mvc.Collection
  • automatically clears bindings when disposed
  • setModel() will change bindings to the new model
    single.setModel(WeatherDemo.Store.get(id));

model bindings

  this.autobind('.city', 'City: {$city}');
  this.autobind('.temp', '{$farenheit} °F');
  this.bind('temp', function(temp) {
    $(this.getElement()).css('left', temp * 10 - 300);
  }).fire();

autobind does it all!

this.autobind('', {
    reqs: ['celcius'],
    reqClass: ['hot', 'mild'],
    chooseClass: function(celcius) {
      if (celcius > 30)
        return 'hot';
      if (celcius < 20)
        return 'mild';
    }
  });

even two way binding

  this.autobind('.city', '{$city}');
  this.autobind('.temp', '{$temp}');
  this.autobind('.celcius', '{$celcius}');
  
  // used to blur on enter so autobind will apply
  this.on(goog.events.EventType.KEYUP, function(e) {
    if (e.keyCode === goog.events.KeyCodes.ENTER) {
      e.target.blur();
    }
  }, 'input');

autolist

this.autolist(WeatherDemo.control.Simple, content);
or override placement:
WeatherDemo.control.List.prototype.placeChild_ = function(
    contentEl, child, index) {
  $(child.getElement()).css({
    'top': index * 45
  });
};

Let's see some code

More tools!

  • templates
  • linting
  • stylesheets
  • class minification
  • testing

Closure Templates

  • Compiled
  • Extensible functions
  • Client and Server-side
  • Translatable strings

Closure Linter

Linting

gjslint -r js

...
Found 17 errors, including 1 new errors, in 7 files (0 files OK).

... The script can be run by executing: fixjsstyle -r js 
fixjsstyle -r js

gjslint -r js
...
Found 8 errors, including 0 new errors, in 5 files (2 files OK).

Closure Stylesheets

  • Variables
  • Extensible functions
  • Mixins
  • Conditionals
  • Minification (including class name)
  • Linting

Class minification

  • Use Closure Stylesheets (coming soon in plovr)
  • '-' separator
  • goog.getCssName()
  • {css className} in templates
// renaming separated by -

goog.getCssName('class-subclass') ==
    goog.getCssName('class') + '-' + goog.getCssName('sublcass');

// select by class in The G and PlastronJS

$(goog.getCssName('-class'));

What's next?

Buy this book!

Questions?