Building Enterprise HTML5 Application

Brite Views

Brite split the interface in Views and UI elements & components

Views are managed by brite, are fully object oriented, asynchronous, and have full lifecycle (create, init, postDisplay, and destroy).

UI elements and components can be any HTML/CSS, JavaScript such as twitter/bootstrap components or any jQuery friendly plugins.

Views are instantiated and displayed using brite.display

brite.display(viewName,parent) to instantiate and display a new view

// for example, instantiate the ProjectView view and add it to the #someContainerDiv element   
brite.display("MyView","#someContainerDiv");

Can use brite.display(viewName,parent,data,configOverride) for instantiating the view with some data and config override

A View is defined with brite.registerView

brite.registerView(viewName,viewDefinition) to define a View and its APIs.

A View must implement .create which must return a new HTML element (html string, HTMLElement, or jQuery element) for the view (or a $.Deferred Promise that will resolve with the HTML element). .postDisplay is one of the optional view methods, and will be called last, after the view is added to the DOM.

js/MyView.js

// for example: MyView, with a default parent of body and will empty the parent on display would look like
brite.registerView("MyView", 
   // The implementation 
   {
     // required
     create: function(data,config){
       return $("<div>This is MyView <button class="clickme">Click Me</button></div>");
     }, 
     
     // optional
     postDisplay: function(data,config){
       this.$el; // is the jQuery wrapped HTMLElement for this view (the one returned by create). 
       this.id;  // is the unique id of this view instance
       console.log("I can be seen now, I am turning green");
       this.$el.css("background","green");
     }  
   }
);

Can use brite.registerView(viewName,config,viewDefinition) to configure the view with some default configuration. (see brite.registerView API doc)

Views may have events

Defining a events property will bind any number of events on this view using where the property key is the filter as "eventName(s); selector" and the value a function (or method name) which will be be executed with the view as contact.

js/MyView.js

// for example: MyView, with a default parent of body and will empty the parent on display would look like
brite.registerView("MyView", // the name

               // The config (optionals
               {emptyParent: true},
               
               // The implementation 
               {
                 create: function(data,config){
                   return $("<div>This is MyView <button class="clickme">Click Me</button></div>");
                 }
               },
               
               // The view events
               events: {
                 "click; button.clickme": function(event){
                   var view = this;
                   console.log("Someone clicked me, I am turning red");
                   view.$el.css("background","red")
                 }
               }
);

A View has a life (i.e lifecycle)

A View must implement .create and might implement .init, .postDisplay, .destroy interfaces. .create must return the HTML element for the view (or a Promise that will resolve with the HTML element). Views can expose and implement any other public API.

js/MyView.js

// for example:
brite.registerView("MyView",
         {
           create: function(data,config){
             // for example, using jsrender
             var viewHtml = $("#tmpl-MyView").render(data);
             return $(viewHtml);
           },
           
           init: function(data, config){
             // will be called after the create and before the postDisplay.
             // this.$element is the jQuery wrapped HTML Element for this view instance
             // this.id is a unique id for this view instance 
           }
           
           postDisplay: function(data,config){
             // will be called after the init and after 
             // the element has been added to the DOM.
             // Good place to do binding here. 
             // same object, so same properties (i.e. this.$element, this.id).
           },
           
           destroy: function(){
             // will be called when the view will be removed. 
             // Note: make sure to use $.fn.bRemove() or $.fn.bEmpty() to trigger this. 
           },
           
           someCustomPublicMethod: function(any,argument,signature){
             // same object, so same properties (i.e. this.$element, this.id).
           }
         });

// if controllerFactory is a JS Object (as above), then it will be cloned on each 
// brite.display to allow the this.$element and this.id to be unique per instance. 

// controllerFactory can also be a function which is responsible to return a new object instance.                 
                       

A View is usually composed of a HTML and CSS file

The view's html template file looks like this:

tmpl/MyView.tmpl

<!-- using the script tag technic. Can be any templating engine -->  
<dscript id="tmpl-MyView" type="text/html">
  <div class="MyView">
    <!-- any HTML, templating engine -->
    <!-- twitter/boostrap fits very well here -->
  </div>
</script> 

The view's css (or less/sass) file would like like this:

css/MyView.css

.MyView{
  ...
}

/* always prefix View sub elements with View name */
.MyView .subElement{
  ...
}

Brite Views Are Asynchronously Managed

Brite asynchronous view management bring maximum optimization capability. Brite use the jQuery $.Deferred API to provide asynchronous view programming.

// the caller wants to know when the component is fully display (after the postDisplay is called)
brite.display("ProjectView",$someContainer,{projectId:123}).done(function(projectView){
  // here the component is fully displayed, and the caller can all public custom view API
  projectView.refreshTaskList();
});  

The create, init, postDisplay can even return a jquery Promise and brite will wait appropriately to go to next step.

brite.registerView("ContactInfo",...,{
  create: function(data){
    var dfd = $.Deferred();
    var contactId = data.contactId;
    jQuery.getJSON("/userInfo",{contactId:contactId},function(contactInfo){
      // using jsRender to render the string HTML
      var html = $("#tmpl-ContactInfo").render(contactInfo);
      
      // resolve the deferred with the jQuery HTML Element
      dfd.resolve($(html));
    });
    
    return dfd.promise();
  }
});

// since the ContactInfo.create function returns a Deferred promise, brite will wait until it resolve
// to call the component .init and .postDisplay (if they are define)
// .init and .postDisplay can also return Deferred promise  

Developers do not have to use this function or even learn it, but it can become instrumental to optimize user experience. See Async Programming with jQuery.Deferred

Views obey to the convention over configuration mantra

By convention, brite assumes (and strongly recommends):

  • View names are CamelCase and start with a Upper case (simpler to read later)
  • Template file name format as: /tmpl/[ViewName].tmpl
  • Template script tag id as: <script id="tmpl-[ViewName]" ... />
  • The first HTML element of a template output must have class="[ViewName]" (use class over id to allow multiple instantiation)
  • CSS (or less/sass) files should follow the same naming pattern: /css/[ViewName.css]
  • View specific css rules must be prefixed with the view name: .MyView .subElement {... or even .MyView-subElement {...
  • The View's JavaScript file should follow same naming pattern: /js/[ViewName].js and obviously the brite.registerView([ViewName] ... code should match the view name.
  • Only ViewName files and css classes should start with upper case (this makes it very easy to read code later as the project grow)

Also, the best-practice to maximize UI performance is to have the UI Elements and Components in the views as lightweight and HTML/CSS centric as possible. Meaning, component should not require JavaScript for layout and styling and make the use of event delegation when appropriate. Twitter/bootstrap is a great library which follows the HTML/CSS centric component pattern, and good coding pattern can bring you the delgation optimization.

The DAO Model

Ask, learn, share about brite.js

Go to brite.js G+ community