34
JavaScript Practices & Design Patterns Salcescu Cristian V0.5.2

JavaScript Practices & Design Patterns

Embed Size (px)

Citation preview

Page 1: JavaScript Practices & Design Patterns

JavaScript Practices & Design Patterns

Salcescu CristianV0.5.2

Page 2: JavaScript Practices & Design Patterns

Function Overloadingfunction log(a,b){ if(arguments.length === 1){ if(typeof(a) === "number") $("#log").append("<br />number:").append(a); else $("#log").append("<br />string:").append(a); } else $("#log").append("<br />2 arguments:").append(a + " " + b); }

log(1);log("Hello!");log(1,2);

Page 3: JavaScript Practices & Design Patterns

Method chaining

• a common syntax for calling multiple functions on the same object

$('#my-div').css('background', 'blue').height(100).slideUp(2000).slideDown(2000);

Page 4: JavaScript Practices & Design Patterns

Timer Patterns

• setInterval/clearInterval• setTimeout/clearTimeout

- all JavaScript in a browser executes on a single thread. JavaScript is single threaded.

- Exception : Web Workers, but they cannot access the DOM.

- Two functions can't run at the same time.

Page 5: JavaScript Practices & Design Patterns

Issues with setInterval

Page 6: JavaScript Practices & Design Patterns

Recursive setTimeout Patternvar i = 1;function func() {

alert(i++);timer = setTimeout(func, 2000);

} var timer = setTimeout(func, 2000);

Page 7: JavaScript Practices & Design Patterns

Throttle

- new function that will execute no more than once every delay milliseconds

_.throttle(fn, delay);

Page 8: JavaScript Practices & Design Patterns

Debounce

- a function that will execute only once, for a group of sequential calls

_.debounce(fn, delay);

Page 9: JavaScript Practices & Design Patterns

Template engine• Separates JS business logic from the html views• Libraries : Handlebars, Hogan

<div id="editContainer">Edit Loading...</div><script type="text/template" id="editTemplate"> <form> FName : <input type="text" value={{fname}} name="fname" /><br/> LName : <input type="text" value={{lname}} name="lname" /><br/> <button type="button" class="save">Save</button> </form></script>

function render(){ var person = {fname: "Mihai", lname: "Ionescu"}; var template = Handlebars.compile($('#editTemplate').html()); var html = template(person); $el.html(html); }

Page 10: JavaScript Practices & Design Patterns

Publish–Subscribe Pattern

• publish–subscribe is a messaging pattern • Senders of messages are called publishers• Receivers of the messages are called subscribers• publishers do not program the messages to be sent

directly to specific receivers, they instead, publish messages without knowledge of what, if any, subscribers there may be.

• subscribers express interest in one or more message types, and only receive messages that are of interest, without knowledge of what, if any, publishers there are.

Page 11: JavaScript Practices & Design Patterns

Publish–Subscribe Pattern

• jQuery$.event.trigger("itemSaved", data);$(document).on("itemSaved", onItemSaved);

- Amplifyamplify.publish( string topic, ... )amplify.subscribe( string topic, function callback )

- Angular$rootScope.$broadcast("itemSaved", data);$scope.$on("itemSaved", function(event, data) {});

Page 12: JavaScript Practices & Design Patterns

Publish–Subscribe Patternvar EVENTS = { buttonClicked : "buttonClicked" };

(function(){ $(function(){ $("#btn").click(function() { amplify.publish( EVENTS.buttonClicked, { message: "Hi!" } ); }); }); }());

(function(){ amplify.subscribe( EVENTS.buttonClicked, function( data ) { alert( data.message ); });}());

Page 13: JavaScript Practices & Design Patterns

Promises- A reference to an async call

function log(text){ $("#container").append(text).append("<br />");}function asynCall(time, text){ var deferred = $.Deferred(); setTimeout(function(){ log(text); deferred.resolve(); }, time); return deferred.promise();}

function asynCall1(){ return asynCall(1000, "1"); };function asynCall2(){ return asynCall(2000, "2"); };function asynCall3(){ return asynCall(3000, "3"); };

var promise1 = asynCall1();var promise2 = asynCall2();var promise3 = asynCall3();

promise1.done(function() { log("1 is done");});$.when(promise1, promise2, promise3).done(function() { log("all done");});

Page 14: JavaScript Practices & Design Patterns

async Libraryasync.parallel([ function(callback){ setTimeout(function(){ callback(null, 'one'); }, 3000); }, function(callback){ setTimeout(function(){ callback(null, 'two'); }, 1000); }], function(err, results){ log(results[0]); log(results[1]); });

function log(text) { $("#log").append(text).append("<br />"); }

Page 15: JavaScript Practices & Design Patterns

MV* Patterns

• MVC• MVVM

Page 16: JavaScript Practices & Design Patterns

MVC

• Model – data, information to be displayed• View – the presentation, HTML template• Controller – manages the communication

between View and Model, encapsulates the business logic

Page 17: JavaScript Practices & Design Patterns

MVVM

• Model – data, information to be displayed• View – the presentation, HTML template• The ViewModel can be considered a

specialized Controller that acts as a data converter. It changes Model information into View information, passing commands from the View to the Model. Behaviour/Business Logic is encapsulated here.

Page 18: JavaScript Practices & Design Patterns

Frameworks

- Backbone- Knockout- Angular

- MVVM separation- Two-way Data Binding- Dependency Injection- Pub/Sub utility

Page 19: JavaScript Practices & Design Patterns

Backbone - View<div id="listContainer">List Loading...</div><div id="editContainer">Edit Loading...</div><script type="text/template" id="editTemplate"> <form> FName : <input type="text" value={{fname}} name="fname" /><br/> LName : <input type="text" value={{lname}} name="lname" /><br/> <button type="button" class="save">Save</button> </form></script>

Page 20: JavaScript Practices & Design Patterns

Backbone - ViewModelvar app = new Backbone.Marionette.Application();

var itemService = { save : function(data) { alert("APIService save : " + data); }}

app.module("editModule", function(editModule, app, Backbone, Marionette, $, _, itemService){ var person = {fname: "Mihai", lname: "Ionescu"}; var EditView = Backbone.View.extend({ el: '#editContainer', initialize: function(){ this.render(); }, render: function(){ var template = Handlebars.compile($('#editTemplate').html()); var html = template(person); this.$el.html(html); }, events: { "click .save" : "save" }, save: function() { var data = $("form", this.$el).serialize(); itemService.save(data); Backbone.trigger('itemSaved', data); } });

var editView = new EditView();}, itemService);

Page 21: JavaScript Practices & Design Patterns

Backbone – Two-way data binding

var app = new Backbone.Marionette.Application();

var itemService = { save : function(data) { alert("APIService save : " + data); }}

app.module("editModule", function(editModule, app, Backbone, Marionette, $, _, itemService){ var personModel = new Backbone.Model({ fname: "Mihai", lname: "Ionescu" }); var EditView = Backbone.View.extend({ el: '#editContainer', model: personModel, bindings: { '[name=fname]': 'fname', '[name=lname]': 'lname' }, initialize: function(){ this.render(); }, render: function(){ this.stickit(); }, events: { "click .save" : "save" }, save: function() { var data = this.model.get("fname"); itemService.save(data); Backbone.trigger('itemSaved', data); } });

var editView = new EditView();}, itemService);

Page 22: JavaScript Practices & Design Patterns

Knockout - View<div id="listContainer"><span data-bind="text: message">List Loading...</span></div><div id="editContainer"> <form> FName : <input type="text" name="fname" data-bind="value: fname" /><br/> LName : <input type="text" name="lname" data-bind="value: lname" /><br/> Full Name : <span data-bind="text: fullName"></span> <br /> <button type="button" class="save" data-bind='click: save' >Save</button> </form></div>

Page 23: JavaScript Practices & Design Patterns

Knockout - ViewModelvar itemService = { save : function(data) { alert("APIService save : " + data); }}

var EditViewModel = function(person) { this.fname = ko.observable(person.fname); this.lname = ko.observable(person.lname); this.fullName = ko.computed(function() { return this.fname() + " " + this.lname(); }, this); this.save = function() { var data = this.fname(); itemService.save(data); ko.postbox.publish("itemSaved", data); }; };

Page 24: JavaScript Practices & Design Patterns

Angular - View<div ng-app="app"><div id="listContainer" ng-controller="listCtrl" > <span ng-bind="message" >List Loading...</span></div><div id="editContainer" ng-controller="editCtrl" > <form> FName : <input type="text" name="fname" ng-model="person.fname" /><br/> LName : <input type="text" name="lname" ng-model="person.lname" /><br/> Full Name : <span ng-bind="fullName()" ></span> <br /> <button type="button" class="save" ng-click="save()" >Save</button> </form></div></div>

Page 25: JavaScript Practices & Design Patterns

Angular - Controllervar app = angular.module("app", []);app.controller("editCtrl", ["$scope", "$rootScope", "itemService", function($scope,$rootScope, itemService){ var person = { fname: "Mihai", lname: "Ionescu"}; $scope.person = person; $scope.fullName = function() { return $scope.person.fname + " " + $scope.person.lname; }; $scope.save = function(){ var data = $scope.person.fname; itemService.save(data); $rootScope.$broadcast("itemSaved", data); }}]);

app.factory( 'itemService', function(){ return { save : function(data) { alert("APIService save : " + data) } }});

Page 26: JavaScript Practices & Design Patterns

Modules

- Split the HTML page in UI components (Views)- Create JS Controllers for every View. Follow the

Module Pattern when defining the JS Controller- All DOM- centric code related to a View should

stay in its Controller. – Comunicate between Controllers using the

Publish-Subscribe Pattern– Don’t create or update global objects. All required

service libraries should be passed in

Page 27: JavaScript Practices & Design Patterns

View<div id="listContainer">List Loading...</div><div id="editContainer">Edit Loading...</div><script type="text/template" id="editTemplate"> <form> FName : <input type="text" value={{fname}} name="fname" /><br/> LName : <input type="text" value={{lname}} name="lname" /><br/> <button type="button" class="save">Save</button> </form></script>

Page 28: JavaScript Practices & Design Patterns

Logicvar services = {};services.itemService = (function(){ function save(data) { alert("APIService save : " + data); }; return { save : save }}());

Page 29: JavaScript Practices & Design Patterns

(function($, services){ "use strict"; var $el, el = "#editContainer"; function initElements(){ $el = $(el); } function render(){ var person = {fname: "Mihai", lname: "Ionescu"}; var template = Handlebars.compile($('#editTemplate').html()); var html = template(person); $el.html(html); } function initEvents(){ $(".save", $el).on("click", save); } function save() { var data = $("form", $el).serialize(); services.itemService.save(data); $.event.trigger("itemSaved", data); }

$(function(){ initElements(); render(); initEvents(); }); }(jQuery, services));

Page 30: JavaScript Practices & Design Patterns

Dependency Management• angular• ng-di library

app.controller("editCtrl", ["$scope", "$rootScope", "itemService", function($scope,$rootScope, itemService){

…}]);

app.factory( 'itemService', function(){ return {

… }});

Page 31: JavaScript Practices & Design Patterns

jQuery Plugin- Create reusable UI components

<div class="show-more"> <button class="btn-toggle">Toggle</button> <div class="content"> text text<br /> text text<br /> text text<br /> </div></div><div class="show-more"> <button class="btn-toggle">Toggle</button> <div class="content"> text text<br /> text text<br /> text text<br /> </div></div>

Page 32: JavaScript Practices & Design Patterns

jQuery Plugin$.fn.showMore = function() { var $set = this, $el; function createToggleHendler($el){ return function(){ $(".content", $el).toggle(); } } $set.each(function() { $el = $(this); $(".btn-toggle", $el).on("click", createToggleHendler($el)); }); return $set; };

$(function() { $(".show-more").showMore();});

Page 33: JavaScript Practices & Design Patterns

angular Directive- encapsulate all DOM manipulation and create reusable UI components

var app = angular.module('myapp', []);

app.directive('showMore', function() { function createToggleHendler($el){ return function(){ $(".content", $el).toggle(); } } return { restrict: 'C', link : function($scope, element, attrs) { $(".btn-toggle", element).on("click", createToggleHendler(element)); } };});

Page 34: JavaScript Practices & Design Patterns

ResourcesPluralsight - Large Scale JavaScript on Client and ServerPluralsight – JavaScript Design Patterns