Twitter Meta: @kvangork #OOJS Object-Oriented Javascript order from chaos (we hope)
function bfIsAlphaNumeric( cfield ) { cfield.value = TRIM2(cfield.value); for ( i = 0 ; i < cfield.value.length ; i++) { var n = cfield.value.substr(i,1); if ( n != 'a' && n != 'b' && n != 'c' && n != 'd' && n != 'e' && n != 'f' && n != 'g' && n != 'h' //... && n != '8' && n != '9' && n != '_' && n != '@' && n != '-' && n != '.' ) { window.alert("Only Alphanumeric are allowed.nPlease re-enter the value."); cfield.value = ''; cfield.focus(); } cfield.value = cfield.value.toUpperCase(); } return; }
everything is an Object even functions http://www.flickr.com/photos/sanchtv/4192677571
every Object has a Prototype http://www.flickr.com/photos/macwalsh/4403701509
Tools //Helper function. Copy all properties of props into obj function mixin (obj, props) { if(obj && props && typeof props == 'object') { for(var property in props) { obj[property] = props[property]; } } }
Tools //Pseudo-Classical Extension Function //Preserves: inheritance // superclass function extend (superclass, overrides) { //...complicated, dragon-filled, etc... //get the well-commented code from http://gist.github.com/339766 //Returns new class, descended from superclass, with overrides applied. }
Basic Container Class var Container = function(){ this.items = []; //empty array to hold items }; Container.prototype.addItem = function(item) { this.items.push(item); } Container.prototype.getCount = function() { return this.items.length; }
var Container = function(){ this.items = []; //empty array to hold items }; Container.prototype.addItem = function(item) { this.items.push(item); } Container.prototype.getCount = function() { return this.items.length; } var LimitedContainer = extend(Container, { constructor: function(maxItems) { LimitedContainer.superclass.constructor.call(this); this.maxItems = maxItems; }, addItem: function(item) { if(this.getCount() < this.maxItems) { LimitedContainer.superclass.addItem.call(this, item); } } });
var Container = function(){ this.items = []; //empty array to hold items }; Container.prototype.addItem = function(item) { this.items.push(item); } Container.prototype.getCount = function() { return this.items.length; } var LimitedContainer = extend(Container, { constructor: function(maxItems) { LimitedContainer.superclass.constructor.call(this); this.maxItems = maxItems; }, addItem: function(item) { if(this.getCount() < this.maxItems) { LimitedContainer.superclass.addItem.call(this, item); } } });
http://www.flickr.com/photos/thomasroche/2481517741
Bearded Men of the 21st Century (1939)
Config Objects function LimitedContainer (maxItems) { this.maxItems = maxItems; } new LimitedContainer(3);
Config Objects function LimitedContainer(config) { mixin(this, config); //who knows what you'll need this.maxItems = config.maxItems || 3; } new LimitedContainer({maxItems: 3});
Model View
Example
Example View
Example Model View
var DS = {}; //Empty constructor, this is just a stub DS.QueryControllerView = extend(Object, { //No constructor required, use Object's default //Required Delegate methods: QueryCompleted: function (resultSet) { alert("Your query finished with " + resultSet.features.length + " features returned."); }, QueryFailed: function (error) { alert("Sorry, your query failed. Here are its last words: " + error); } });
DS.QueryController = extend(Object, { constructor: function (config) { //copy all config parameters mixin(this, config); //verify required parameter presence and types if (!(this.view instanceof DS.QueryControllerView)) throw("Incorrect view..."); if (!this.serviceURL) throw("Missing Service URL."); this.queryTask = new esri.tasks.QueryTask(this.serviceURL); }, initiateQuery: function (geometry) { if (!geometry instanceof esri.geometry.Geometry) throw("Invalid geometry parameter..."); var query = new esri.tasks.Query(); query.geometry = geometry; query.returnGeometry = true; this.runningQuery = this.queryTask.execute(query, this.callback, this.errback); },
view = new DS.QueryControllerView(); controller = new DS.QueryController({ view: view, serviceURL: "http://sampleserver1.arcg...mographics/ESRI_Census_USA/MapServer/3" }); //set up click event handler dojo.connect(map, "onClick", function(evt) { controller.initiateQuery(evt.mapPoint); });
Demo
DS.QueryControllerMapView = extend(DS.QueryControllerView, { constructor: function(config) { mixin(this, config); if (!(this.map instanceof esri.Map)) throw("Incorrect map parameter..."); }, QueryCompleted: function(resultSet) { if(resultSet.features.length) { this.map.graphics.clear(); var mySymbol = new esri.symbol.SimpleFillSymbol(...255,0,0.25])); var feature = resultSet.features[0]; feature.setSymbol(mySymbol); this.map.graphics.add(feature); } } });
view = new DS.QueryControllerMapView({map: map}); controller = new DS.QueryController({ view: view, serviceURL: "http://sampleserver1.arcg...mographics/ESRI_Census_USA/MapServer/3" }); //set up click event handler dojo.connect(map, "onClick", function(evt) { controller.initiateQuery(evt.mapPoint); });
Demo
Resources Object-Oriented Javascript by Stoyan Stefanov YUI Theater, especially Douglas Crockford’s videos Read Library Source Code - Dojo, jQuery, YUI Download this deck and code at http://prng.vangorkom.org You should follow me on Twitter: @kvangork

Object-Oriented Javascript

  • 1.
    Twitter Meta: @kvangork #OOJS Object-Oriented Javascript order from chaos (we hope)
  • 2.
    function bfIsAlphaNumeric( cfield) { cfield.value = TRIM2(cfield.value); for ( i = 0 ; i < cfield.value.length ; i++) { var n = cfield.value.substr(i,1); if ( n != 'a' && n != 'b' && n != 'c' && n != 'd' && n != 'e' && n != 'f' && n != 'g' && n != 'h' //... && n != '8' && n != '9' && n != '_' && n != '@' && n != '-' && n != '.' ) { window.alert("Only Alphanumeric are allowed.nPlease re-enter the value."); cfield.value = ''; cfield.focus(); } cfield.value = cfield.value.toUpperCase(); } return; }
  • 6.
    everything is an Object evenfunctions http://www.flickr.com/photos/sanchtv/4192677571
  • 7.
    every Object has a Prototype http://www.flickr.com/photos/macwalsh/4403701509
  • 8.
    Tools //Helper function. Copyall properties of props into obj function mixin (obj, props) { if(obj && props && typeof props == 'object') { for(var property in props) { obj[property] = props[property]; } } }
  • 9.
    Tools //Pseudo-Classical Extension Function //Preserves:inheritance // superclass function extend (superclass, overrides) { //...complicated, dragon-filled, etc... //get the well-commented code from http://gist.github.com/339766 //Returns new class, descended from superclass, with overrides applied. }
  • 11.
    Basic Container Class varContainer = function(){ this.items = []; //empty array to hold items }; Container.prototype.addItem = function(item) { this.items.push(item); } Container.prototype.getCount = function() { return this.items.length; }
  • 12.
    var Container =function(){ this.items = []; //empty array to hold items }; Container.prototype.addItem = function(item) { this.items.push(item); } Container.prototype.getCount = function() { return this.items.length; } var LimitedContainer = extend(Container, { constructor: function(maxItems) { LimitedContainer.superclass.constructor.call(this); this.maxItems = maxItems; }, addItem: function(item) { if(this.getCount() < this.maxItems) { LimitedContainer.superclass.addItem.call(this, item); } } });
  • 13.
    var Container =function(){ this.items = []; //empty array to hold items }; Container.prototype.addItem = function(item) { this.items.push(item); } Container.prototype.getCount = function() { return this.items.length; } var LimitedContainer = extend(Container, { constructor: function(maxItems) { LimitedContainer.superclass.constructor.call(this); this.maxItems = maxItems; }, addItem: function(item) { if(this.getCount() < this.maxItems) { LimitedContainer.superclass.addItem.call(this, item); } } });
  • 14.
  • 15.
    Bearded Men ofthe 21st Century (1939)
  • 16.
    Config Objects function LimitedContainer(maxItems) { this.maxItems = maxItems; } new LimitedContainer(3);
  • 17.
    Config Objects function LimitedContainer(config) { mixin(this, config); //who knows what you'll need this.maxItems = config.maxItems || 3; } new LimitedContainer({maxItems: 3});
  • 18.
    Model View
  • 19.
  • 20.
  • 21.
    Example Model View
  • 22.
    var DS ={}; //Empty constructor, this is just a stub DS.QueryControllerView = extend(Object, { //No constructor required, use Object's default //Required Delegate methods: QueryCompleted: function (resultSet) { alert("Your query finished with " + resultSet.features.length + " features returned."); }, QueryFailed: function (error) { alert("Sorry, your query failed. Here are its last words: " + error); } });
  • 23.
    DS.QueryController = extend(Object,{ constructor: function (config) { //copy all config parameters mixin(this, config); //verify required parameter presence and types if (!(this.view instanceof DS.QueryControllerView)) throw("Incorrect view..."); if (!this.serviceURL) throw("Missing Service URL."); this.queryTask = new esri.tasks.QueryTask(this.serviceURL); }, initiateQuery: function (geometry) { if (!geometry instanceof esri.geometry.Geometry) throw("Invalid geometry parameter..."); var query = new esri.tasks.Query(); query.geometry = geometry; query.returnGeometry = true; this.runningQuery = this.queryTask.execute(query, this.callback, this.errback); },
  • 24.
    view = newDS.QueryControllerView(); controller = new DS.QueryController({ view: view, serviceURL: "http://sampleserver1.arcg...mographics/ESRI_Census_USA/MapServer/3" }); //set up click event handler dojo.connect(map, "onClick", function(evt) { controller.initiateQuery(evt.mapPoint); });
  • 25.
  • 26.
    DS.QueryControllerMapView = extend(DS.QueryControllerView,{ constructor: function(config) { mixin(this, config); if (!(this.map instanceof esri.Map)) throw("Incorrect map parameter..."); }, QueryCompleted: function(resultSet) { if(resultSet.features.length) { this.map.graphics.clear(); var mySymbol = new esri.symbol.SimpleFillSymbol(...255,0,0.25])); var feature = resultSet.features[0]; feature.setSymbol(mySymbol); this.map.graphics.add(feature); } } });
  • 27.
    view = newDS.QueryControllerMapView({map: map}); controller = new DS.QueryController({ view: view, serviceURL: "http://sampleserver1.arcg...mographics/ESRI_Census_USA/MapServer/3" }); //set up click event handler dojo.connect(map, "onClick", function(evt) { controller.initiateQuery(evt.mapPoint); });
  • 28.
  • 29.
    Resources Object-Oriented Javascript byStoyan Stefanov YUI Theater, especially Douglas Crockford’s videos Read Library Source Code - Dojo, jQuery, YUI Download this deck and code at http://prng.vangorkom.org You should follow me on Twitter: @kvangork

Editor's Notes

  • #19 Model = RESTful data source on server. View = HTML Template Engine in JS, or UI Widgets. Use DI with controller. View should be an abstract class (like an interface) which will throw errors if you fail to implement required members. Controller = Javascript class, takes in View class in constructor, along with any model configuration params.
  • #20 Strap in, it&amp;#x2019;s time for CODE
  • #21 Strap in, it&amp;#x2019;s time for CODE