Skip to content
This repository was archived by the owner on Jun 25, 2025. It is now read-only.

Component pattern

addyosmani edited this page Dec 4, 2014 · 2 revisions

The following pattern will be used for new components moving forward:

/** * A possible component handler interface using the revealing module design * pattern. * @author Jason Mayes (jmayes@google.com) */varcomponentHandler=function(){'use strict';varregisteredComponents_=[];varcreatedComponents_=[];/** * Searches registered components for a class we are interested in using. * Optionally replaces a match with passed object if specified. * @param {string} name The name of a class we want to use. * @param {object} opt_replace Optional object to replace match with. * @return {Object | false} * @private */functionfindRegisteredClass_(name,opt_replace){for(vari=0;i<registeredComponents_.length;i++){if(registeredComponents_[i].className===name){if(opt_replace!==undefined){registeredComponents_[i]=opt_replace;}returnregisteredComponents_[i];}}returnfalse;}/** * Searches existing DOM for elements of our component type and upgrades them * if they have not already been upgraded. * @param {string} jsClass the programatic name of the element class we need * to create a new instance of. * @param {string} cssClass the name of the CSS class elements of this type * will have. */functionupgradeDomInternal(jsClass,cssClass){if(cssClass===undefined){varregisteredClass=findRegisteredClass_(jsClass);if(registeredClass){cssClass=registeredClass.cssClass;}}varelements=document.querySelectorAll('.'+cssClass);for(varn=0;n<elements.length;n++){upgradeElementInternal(elements[n],jsClass);}}/** * Upgrades a specific element rather than all in the DOM. * @param {HTMLElement} element The element we wish to upgrade. * @param {string} jsClass The name of the class we want to upgrade * the element to. */functionupgradeElementInternal(element,jsClass){// Only upgrade elements that have not already been upgraded.if(element.getAttribute('data-upgraded')===null){// Upgrade element.element.setAttribute('data-upgraded','');varregisteredClass=findRegisteredClass_(jsClass);if(registeredClass){createdComponents_.push(newregisteredClass.classConstructor(element));}else{// If component creator forgot to register, try and see if// it is in global scope.createdComponents_.push(newwindow[jsClass](element));}}}/** * Registers a class for future use and attempts to upgrade existing DOM. * @param {object} config An object containting: * {constructor: Constructor, classAsString: string, cssClass: string} */functionregisterInternal(config){varnewConfig={'classConstructor': config.constructor,'className': config.classAsString,'cssClass': config.cssClass};varfound=findRegisteredClass_(config.classAsString,newConfig);if(!found){registeredComponents_.push(newConfig);}upgradeDomInternal(config.classAsString);}// Now return the functions that should be made public with their publicly// facing names...return{upgradeDom: upgradeDomInternal,upgradeElement: upgradeElementInternal,register: registerInternal};}();/** * An example Class constructor for a WSK component. Note capital camel case. * @param {HTMLElement} element The element that will be upgraded. */functionMaterialComponentClassname(element){'use strict';// Example private variable. Uses underscore notation to denote private var.this.element_=element;// Other private vars can go here as needed...// Initialize instance.this.init();}/** * Store constants in one place so they can be updated easily. * @enum {string} * @private */MaterialComponentClassname.prototype.Constant_={/** * Name should be descriptive so no comment needed. */MEANING_OF_LIFE: '42',SPECIAL_WORD: 'HTML5'};/** * Store strings for class names defined by this component that are used in * JavaScript. This allows us to simply change it in one place should we * decide to modify at a later date. * @enum {string} * @private */MaterialComponentClassname.prototype.CssClasses_={/** * Class names should use camelCase and be prefixed with the word "material" * to minimize conflict with 3rd party systems. */SHOW: 'materialShow',/** * Explain what the class is for. */HIDE: 'materialHidden'};/** * Example of a private function, note the underscore and 2 blank lines * between function defintion and previous lines of code. * @private */MaterialComponentClassname.prototype.privateFunction_=function(){'use strict';// Your code here...console.log(this.Constant_.SPECIAL_WORD+' is cool!');};// Other private functions could be defined here. 2 lines space between each.// Public functions can also be defined here, simply without underscores at// end of funciton name.MaterialComponentClassname.prototype.init=function(){// In this example we will add an event listener to the element.if(this.element_){this.element_.addEventListener('click',this.privateFunction_.bind(this));}};window.addEventListener('load',function(){// On document ready, the component registers itself. It can assume// componentHandler is available in the global scope.componentHandler.register({constructor: MaterialComponentClassname,classAsString: 'MaterialComponentClassname',cssClass: 'MaterialComponentClassname'});});

Clone this wiki locally