SlideShare una empresa de Scribd logo
1 de 128
Descargar para leer sin conexión
< w e b / F>
Better Angular.js
Misnomer, myth and Angular.js goliath
< w e b / F><web/F>
TDD
Test driven development
< w e b / F><web/F>
Test driven development
TDD is a game of chess
A best practice could be:
“Never lose a piece without winning a piece of equal or
greater value.”
< w e b / F><web/F>
Test driven development
• In reality, there are no rules/best practices
• Thereare always situations, responses to those situations and
observations recordedfor future reference.
< w e b / F><web/F>
TDD
Does Test Driven Developmentmeans – write tests first and then code
later to pass those tests?
Probably not…
< w e b / F><web/F>
Idea of TDD
• TDD is about testability.
• TDD is about writing testable code.
• It is not about writing unit test first.
< w e b / F><web/F>
Testable code
• Probably the most important aspect of TDD is readability.
• We spend more time on reading a code than writing it. And the goal
of TDD is to generate a testable code that is easy to read while not
sacrificing its performance.
• Can we call readable code as maintainablecode?
< w e b / F><web/F>
Testable code
• On the surface, testable code is
• Efficient
• As simple as possible
• DRY – Doesn’t repeat
• Modular
< w e b / F><web/F>
Testable code
• Inside, testable code has
• Encapsulation
• Loose coupling
• Separation of concern
• Zero to fewer global objects
• Optimized design patterns
< w e b / F><web/F>
TDD on front-end
Does TDD ideology holds true for front-end
< w e b / F><web/F>
What happens when we move to front-end?
< w e b / F><web/F>
But on front-end
• Testability is problematic,
• Because, though HTML, CSS and JS are three pieces of same stack,
each of them is a stack in itself.
< w e b / F><web/F>
On front-end
• The problem is
• We mix HTML, CSS and JS at will
• Concept of encapsulation is very vague
< w e b / F><web/F>
function implementSomeBusinessLogic() {
// Some business logic
var data = getLatestData();
var $div = $("<div></div>");
// Mixing HTML with JavaScript
$div.text("Data: " + data);
// Mixing CSS with JavaScript
$div.css("color", "red");
// DOM Manipulation mixed with Business Logic
$(body).append($div);
}
Mixing
Presentation, DOM
& Business logic at
will.
< w e b / F><web/F>
function UserList(items) {
this._items = items;
}
UserList.prototype.render = function () {
var $list = $("#userList");
this._items.forEach(function (item) {
$("<li>").text(item.name).appendTo(list);
});
}
UserList.prototype.search = function () { }
Is this a good
encapsulation?
< w e b / F><web/F>
Angular to rescue
Core design and philosophy of Angular.js
< w e b / F><web/F>
Here comes the Angular
• That is exactly whereAngular ratifies & embraces these problems
• Threepillars of Angular
< w e b / F><web/F>
Three Pillars of Angular
• Updating view (HTML/DOM)
should be very trivial
• And that is the first pillar of
Angular.js– Data Binding
DRY – Do not RepeatYourself
DB – Two wayData Binding
< w e b / F><web/F>
Three Pillars of Angular
• There should be proper separation
of concern and for that you need
structure.
• That is the second pillar of
Angular.js – Directives
• Then there are controllers, services,
routers, etc.
Structure
Directives
< w e b / F><web/F>
Three Pillars of Angular
• It is not enough to use
framework which is thoroughly
tested. Code on top of it should
also be readily testable.
• That is the third pillar –
Dependency Injection
< w e b / F><web/F>
TDD & Angular
How does Angular helps write testable code?
< w e b / F><web/F>
Angular is solving many trivial front-end
problems.
< w e b / F><web/F>
Problem 1
Modules
Testable design is always modular. There is no exception.
< w e b / F><web/F>
Code organization - Modules
• Any large scale apps needs modules to organize and group related
components.
• Modules help create logical boundarieswithin system.
• Angular.jsmodules existed long before ES6 or Common.js modules
< w e b / F><web/F>
Organizing angular app into modules
< w e b / F><web/F>
But angular modules are yin and yang
There is beast and then there is beauty.
< w e b / F><web/F>
Angular.js Modules – beasts
• No support for namespace
• Probably most criticized aspect of Angular.js modules
• No support for lazy loading
• Practically useless for any other libraries
• Not very intuitiveto encapsulateCSS
< w e b / F><web/F>
The idea is to use modules for what they are
supposed to be used for.
Angular.js module is like a bag that holds one or more recipes.
< w e b / F><web/F>
Angular.js Modules – Beauty
• Logical separation
• Provide better abstractions
• Lazy instantiation
• Module is not instantiateduntil required
• Dependencyorganization
• Small modules makes for a bigger modules
< w e b / F><web/F>
Angular.js Modules – Why
• Code reuse
• Module can be replaced and developed in parallel.
• Better separation of concern
• Config and Run blocks
• Config and run blocks respect dependency tree.
• Code decoration
< w e b / F><web/F>
Two controllers with same name
(function () {
var app = angular.module("HigherModule", []);
app.controller("ControllerSameName", function () {
this.title = "Defined once (1)";
});
app.controller("ControllerSameName", function () {
this.title = "Defined twice (2)";
});
})();
< w e b / F><web/F>
Two controllers with same name in different modules
(function () {
var lowerModule = angular.module("LowerModule", []);
var app = angular.module("HigherModule", ["LowerModule"]);
app.controller("ControllerSameName", function () {
this.title = "Defined at Higher Module (2)";
});
lowerModule.controller("ControllerSameName", function () {
this.title = "Defined at Lower Module (1)";
});
})();
< w e b / F><web/F>
Modules – name spacing
• Often misused approach
• Dot notation approach
• Typically observed when backend programmer do Angular without JavaScript
understanding
< w e b / F><web/F>
Two controllers with same name in different modules
var lowerModule = angular.module("LowerModule", []);
var app = angular.module("HigherModule", ["LowerModule"]);
app.controller("HM.ControllerSameName", function () {});
lowerModule.filter("LM.customFilter", function () {});
< w e b / F><web/F>
Ideal name spacing technique for handling collisions
var lowerModule = angular.module("LowerModule", []);
var app = angular.module("HigherModule", ["LowerModule"]);
app.controller("hmControllerSameName", function () { });
lowerModule.filter("lmCustomFilter", function () { });
< w e b / F><web/F>
Problem 2
Separation of Concern
(Directives, controllers and services)
Testable design adheres to high cohesion along with well defined separation of concern.
< w e b / F><web/F>
Separation of concern – Directives
• Directive are heart of Angular.js
• Separation of concern
• DOM manipulations are abstracted away inside directives.
• Services abstract away server side communication.
• Promoting ideal component encapsulation
< w e b / F><web/F>
What is directivein Angular.js?
Directive is something that extends the meaning
of HTML.
< w e b / F><web/F>
More than one directive with same name?
(function () {
var app = angular.module("HigherModule", []);
app.directive("mySimpleDir", function () {
return { /* DDO */};
});
app.directive("mySimpleDir", function () {
return { /* DDO */};
});
})();
< w e b / F><web/F>
Idea of extending HTML
• Extension in software programming roughly translates to
• Inheritance
• New meaning
• Overriding existing meaning
• Polymorphism
• Extending the meaning of existing items
< w e b / F><web/F>
Directives – Idea of extension
Thus directives are allowed to extend the meaning… Period.
< w e b / F><web/F>
Overriding default browser behavior
(function () {
var app = angular.module("HigherModule", []);
// Extending browser autofocus attribute
app.directive("autofocus", function () {
return { /* DDO */};
});
})();
< w e b / F><web/F>
Problem 3
Loose Coupling
(Dependency Injection and Angular Bootstrapping)
Software design without loose coupling is never testable.
< w e b / F><web/F>
Loose coupling
• Loose coupling– IoC (Inversion of Control)
• Don’t call me; I will call you
• It is always Angular that initiates the call
• Example,
• Angular decides when to instantiatecontroller
• Angular is responsible for loading templates
< w e b / F><web/F>
Idea of IoC – Inversion of Control
Main
Traditional jQuery style
< w e b / F><web/F>
Idea of IoC – Inversion of Control
Main Main Dependency
Injector
Traditional jQuery style
This is how Angular does it
< w e b / F><web/F>
Dependency injection
• Angular implementsIoC using DI
• DI internallyuses Service Locator pattern
• To understandDI, we have to understand Angular bootstrapping
< w e b / F><web/F>
Angular bootstrap process
Diagram does not illustratethe broader
picture. So let’stry to drill down further…
< w e b / F><web/F>
Angular composition
This is whatAngularis madeup of
< w e b / F><web/F>
Angular composition
Theseboxes areobjects communicatingwith each other. That’s theneed forDI.
< w e b / F><web/F>
Angular composition
Theseobjects areangularcomponents
Filters
Controllers
Services
Constants
ProvidersDirectives
Factories
Values
< w e b / F><web/F>
Angular composition
Special Objects
Controllers
Directives
Filters
Animations
Custom objects
Services
The world of Angularcan be categorized into
The only catch is they
should be singleton.
Angularcalls these
customer objects as
services.
< w e b / F><web/F>
Creating various angular objects
• To create Angular.js objects (custom objects or special objects),
use providers:
• $controllerProvider
• $compileProvider
• $filterProvider
• $provide (service Provider)
• Beware of Internet/Google/Angular.js docs
https://docs.angularjs.org/api/auto/service/$provide
< w e b / F><web/F>
Angular bootstrap process
ANGULAR TWO DISTINCT PHASES
Configuration Phase Run Phase
< w e b / F><web/F>
Angular bootstrap process
Use different providers toregister your
components/classes/objects with Angular
modules. Angular calls it recipes for
object creation;
Configuration Phase
module.config();
< w e b / F><web/F>
Angular bootstrap process
Instantiate objects from the
components/classes registered during
configuration phase. Run Phase
module.run();
< w e b / F><web/F>
Angular bootstrap process
BrowserHTML page
Wait for DOM ready
Search ng-app=“module”
Initialize $injector
.config()
.run()
Start playingwith DOM
IoC – Inversion of Control
< w e b / F><web/F>
Code sample with config block
(function () {
var app = angular.module("HigherModule", []);
app.config(function ($controllerProvider) {
$controllerProvider.register("MyCtrl", function () {
var vm = this;
vm.title = "My Title";
});
});
})();
< w e b / F><web/F>
Reduced boilerplate code
app.controller("MyCtrl", function () {
var vm = this;
vm.title = "My Title";
});
< w e b / F><web/F>
Creating a directive
app.config(function ($compileProvider) {
$compileProvider.directive("mySimpleDir", function () {
// DDO - Directive Definition Object
});
});
app.directive("mySimpleDir", function () {
// DDO - Directive Definition Object
});
< w e b / F><web/F>
Built in objects in Angular
• Angular understands:
• Controllers
• Filters
• Directives
• Animation
Built in object Correspondingproviders
Controller $controllerProvider
Filter $filterProvider
Directive $compileProvider
Animation $animationProvider
< w e b / F><web/F>
Can we call providers as Classes in plain old
JavaScript?
< w e b / F><web/F>
Creating custom objects
• Remember, Customer objects are called services in Angular
• To create any object in Angular you need provider
• To create custom object, custom provider is necessary
• For that purpose, we need a provider that can create new provider
which is sort of meta provider
< w e b / F><web/F>
About custom objects
• One condition by Angular.js
• Custom Objects (services) should be Singletons
• It is enforced at Framework level by Angular
• One assumption by Angular.js
• Custom Objects should hold your data
< w e b / F><web/F>
Comes $provide
app.config(function ($provide) {
$provide.provider("calculator", function () {
this.$get = function () {
return {
add: function () { },
subtract: function () { }
};
};
});
});
< w e b / F><web/F>
Using custom object
app.controller("MyCtrl", function (calculator) {
calculator.add();
calculator.subtract();
});
< w e b / F><web/F>
Reducing boilerplate – Why so verbose?
app.provider("calculator", function () {
this.$get = function () {
return {
add: function () { },
subtract: function () { }
};
};
});
< w e b / F><web/F>
Providers are still verbose
&
weird way to create objects
< w e b / F><web/F>
How we do it in plain old JavaScript?
(function () {
function Calculator() {
this.add = function () { };
this.substract = function () { };
}
var calculator = new Calculator();
})();
< w e b / F><web/F>
Comparing plain JS and Angular
(function () {
function Calculator() {
this.add = function () { };
this.substract = function () { };
}
var calculator = new Calculator();
})();
app.provider("calculator", function
() {
this.$get = function () {
return {
add: function () { },
subtract: function () { }
};
};
});
< w e b / F><web/F>
So Angular is creating syntactic sugar
app.service("calculator", function () {
this.add = function () { };
this.substract = function () { };
});
< w e b / F><web/F>
Comparing two syntax
app.service("calculator", function () {
this.add = function () { };
this.substract = function () { };
});
app.provider("calculator", function
() {
this.$get = function () {
return {
add: function () { },
subtract: function () { }
};
};
});
< w e b / F><web/F>
What angular is doing internally
app.service("calculator", function () {
this.add = function () { };
this.substract = function () { };
});
app.service = function (name, Class) {
app.provider(name, function () {
this.$get = function ($injector) {
return
$injector.instantiate(Class);
};
});
}
< w e b / F><web/F>
Some are not comfortable with new
function calculatorFactory() {
var obj = {};
obj.add = function () { };
obj.substract = function () { };
return obj;
}
var calculator = calculatorFactory();
Enter the
factory pattern
< w e b / F><web/F>
Angular has solution for that
function calculatorFactory() {
var obj = {};
obj.add = function () { };
obj.substract = function () { };
return obj;
}
var calculator = calculatorFactory();
app.factory("calculator", function ()
{
return {
add: function () { },
substract: function () { }
};
});
< w e b / F><web/F>
Angular factory pattern
app.factory("calculator", function ()
{
return {
add: function () { },
substract: function () { }
};
});
app.factory = function (name, factory) {
app.provide(name, function () {
this.$get = function ($injector) {
return $injector.invoke(factory);
};
});
}
< w e b / F><web/F>
Then there are other recipes
app.constant("STATES", {
DASHBOARD: "dashboard",
LIST: "projectList"
});
app.value("calculator", function
() { });
app.value("PI", 3.1422);
app.value("welcome", "Hi,
Harsh");
< w e b / F><web/F>
But, yes providers are powerful
app.provider("greeting", function () {
var text = "Hello, ";
this.setText = function (value) {
text = value;
};
this.$get = function () {
return function (name) {
alert(text + name);
};
}; });
app.config(function (greetingProvider) {
greetingProvider
.setText("Howdy there, ");
});
app.run(function (greeting) {
greeting("Harsh Patil");
});
< w e b / F><web/F>
Angular core - $injector
• So far, we have just seen how to bootstrap angular. But how does
Angular executes in run phase?
< w e b / F><web/F>
$injector internals
app.value("val", "");
app.constant("CON", 123);
app.controller("MyCtrl",
function () { });
app.factory(“myFactory",
function () { });
Instance
Factory
Instance
Cache
Service Locator
$injector
$injector.get("myFactory");
< w e b / F><web/F>
Problem 4
Using optimized design patterns
Testable software design implements well thought design patterns
< w e b / F><web/F>
Typical Ajax Request example
var http = new XMLHttpRequest(),
url = "/example/new",
params = encodeURIComponent(data);
http.open("POST", url, true);
< w e b / F><web/F>
Typical Ajax Request example
http.setRequestHeader("Content-type",
"application/x-www-form-urlencoded");
http.setRequestHeader("Content-length", params.length);
http.setRequestHeader("Connection", "close");
< w e b / F><web/F>
Typical Ajax Request example
http.onreadystatechange = function () {
if (http.readyState == 4 && http.status == 200) {
alert(http.responseText);
}
}
http.send(params);
< w e b / F><web/F>
Same thing with Angular
$http({
method: "POST",
url: "/example/new",
data: data
})
.then(function (response) { alert(response); });
< w e b / F><web/F>
Problem 5
Data Binding
DOM updates should be very trivial. This is the magic of angular.
< w e b / F><web/F>
Plain JS code
// HTML
<button id="myButton"></button>
// JavaScript
var button = document.getElementById("myButton");
button.addEventListener("click", function () {
// Do something
}, false);
< w e b / F><web/F>
How we do it in Angular
// HTML
<button ng-click="doSomething()"></button>
// JavaScript
app.controller("MyCtrl", function ($scope) {
$scope.doSomething = function () {
// Do something
};
});
< w e b / F><web/F>
Creating function in JavaScript
// Technique 1
function test() {
// Do something
}
// Technique 2
var test = function () { };
< w e b / F><web/F>
There is something more to JS
// Technique 3
var test = new Function(arguments, body);
Functions in JavaScriptare objects.
They can be createdjust like any other objects.
< w e b / F><web/F>
What angular is doing internally
// HTML
<button ng-click="doSomething()"></button>
This is a directive
< w e b / F><web/F>
What angular is doing internally
link: function ($scope, iElement, iAttr) {
iElement.addEventListener("click", function () {
var ngClick = iAttr.ngClick;
var func = new Function([], ngClick);
// Setup Angular watcher ...
func();
// Execute watchers & bindings ...
// Run $scope.digest();
}, false);
}
< w e b / F><web/F>
Angular digest function
$scope.prototype.$digest = function () {
var self = this;
_.forEach(this.$$watchers, function (watch) {
var newValue = watch.watchFn(self);
var oldValue = watch.last;
if (newValue !== oldValue) {
watch.listenerFn(newValue, oldValue, self);
watch.last = newValue;
}
});
};
< w e b / F><web/F>
Modern Enterprise Web
Apps
< w e b / F><web/F>
Modern enterprise web application
< w e b / F><web/F>
Then they evolve
< w e b / F><web/F>
• Thereis nothing permanent except change.
< w e b / F><web/F>
Then it becomes all together different
< w e b / F><web/F>
Modern enterprise applications
• API
• Third party integrations
• Packages
• Libraries
• Modules
• Legacies
• Versioning
• Limited eye site for individual
developer & tester
• Nearly impossible to test everything
through leaky abstractions
Abstractions leak when you have to understand the lower level concept
to use the higher level concept.
< w e b / F><web/F>
So how do you write testable code?
• One sure shot way is to write Unit Tests.
< w e b / F><web/F>
When you write unit tests
• When you start for unit testing, you disintegrateor more accurately
isolate different components of the system.
• That is where you can see system as a set of interconnected
components.
• And so goes for the couplingsbetween components.
< w e b / F><web/F>
But most unit testing is not proper
• If you do any of this
• Database
• Any kind of server
• File/network I/O or file system
• Another application
• Console
• Logging
• Other classes
< w e b / F><web/F>
So when do you write unit tests?
• Never do it. Let QA do it
• After some amount of code
• After writing all the code
• Before any code* (Test first)
• At the same time as I write the code
< w e b / F><web/F>
Unit testing in day to day life
1. Describe the behavior of function
in plain English in some readme file.
2. Create a test that is
mostly just a copy/paste
from the readme code.
3. Write the actual function
until it passes the first test.
4. Write a few more tests
for any edge cases I can
think of.
5. Get the function to pass
again
6. Encounter another
edge case, go to 4.
< w e b / F><web/F>
But I find writing unit tests hard
• Because the reality check says unit testing is hard
< w e b / F><web/F>
To simplify
Categorize unit tests as:
• Level 1 – Isolated classes
• Level 2 – State management
• Level 3 – Internal dependency
• Level 4 – State management, internal dependency & DOM
< w e b / F><web/F>
Level 1
Isolated Classes/Components
< w e b / F><web/F>
app.factory("utilsFactory", function () {
return {
constructUrl: function (url, urlParams) {
urlParams = urlParams || {};
Object.keys(urlParams).forEach(function (param) {
url = url.replace(":" + param, urlParams[param]);
});
return url;
}
};
});
No Dependency
No state management
< w e b / F><web/F>
it("utilsFactory should construct url correctly", function () {
var url, params, url;
// Setup data
url = "/api/projects/:projectId"; params = { projectId: 1 };
// Exercise SUT
url = utilsFactory.constructUrl(url, params);
// Verify result (behavior)
expect(url).to.equal("/api/projects/1");
});
< w e b / F><web/F>
We need supporting code as well
beforeEach(function () {
module("myModule");
inject(function (_utilsFactory_) {
utilsFactory = _utilsFactory_;
});
});
< w e b / F><web/F>
Level 2
State Management
< w e b / F><web/F>
app.factory("utilsFactory", function () {
return {
_count: 0,
constructUrl: function (url, urlParams) {
urlParams = urlParams || {};
// ...
this._count++;
return url;
},
};
});
< w e b / F><web/F>
it("utilsFactory should update _count", function () {
var url, params, url;
// Setup data
url = "/api/projects/:projectId";
params = { projectId: 1 };
// Exercise SUT
url = utilsFactory.constructUrl(url, params);
url = utilsFactory.constructUrl(url, params);
// Verify result (state)
expect(utilsFactory._count).to.equal(2);
});
< w e b / F><web/F>
Level 3
Internal dependency
< w e b / F><web/F>
app.factory("projectFactory", function projectFactory($http, urlFactory) {
function _getProjects() {
var request = angular.copy({
method: "GET",
url: urlFactory.get("projects")
});
return $http(request).then(function (response) {
return response.data;
});
}
return { getProjects: _getProjects };
});
< w e b / F><web/F>
it("projectFactory should return promise", function () {
var promise;
// Setup data
// Exercise SUT
promise = projectFactory.getProject();
// Verify result (state)
expect(promise).to.have.key("then");
});
< w e b / F><web/F>
it("projectFactory should make call to urlFactory", function () {
var promise;
// Setup data
// Exercise SUT
promise = projectFactory.getProjects();
// Verify result (state)
expect(urlFactory.get).to.have.been.called.once();
});
< w e b / F><web/F>
it("projectFactory should make call to $http", function () {
var url, promise;
// Setup data
// Setup expectation
$httpBackend.expectGET("/api/users/").respond(200, {});
// Exercise SUT
promise = projectFactory.getProjects();
// Verify result (behavior)
$httpBackend.verifyNoOutstandingExpectation();
$httpBackend.verifyNoOutstandingRequest();
});
< w e b / F><web/F>
beforeEach(function () {
module("myModule");
inject(function (_projectFactory_, _$httpBackend_, _urlFactory_) {
projectFactory = _projectFactory_;
$httpBackend = _$httpBackend_;
urlFactory = _urlFactory_;
// Mocking
urlMock = sinon.mock(urlFactory);
urlMock.stub("get").when("/api/project").returns("/api/projects");
});
});
< w e b / F><web/F>
it(“description", function () {
// Setup data
// Setup expectation
// Exercise SUT
// Verify result (behavior)
// Teardown
});
< w e b / F><web/F>
What do I need to write unit tests?
• Test runner
• Runtime
• Testing suite
• Assertion library
• Mocking library
< w e b / F><web/F>
Quality of unit tests
A management stake (code coverage)
“I expect a high level of coverage. Sometimes managers
require one. There's a subtle difference.”
“you can't go into production with less than 80% coverage.”
< w e b / F><web/F>
True use of code coverage
Just like this 
< w e b / F><web/F>
True use of code coverage
< w e b / F><web/F>
True use of code coverage
80%, 90% is good but 100%
“It would smell of someone writing tests to make the coverage
numbers happy, but not thinking about what they are doing.”
< w e b / F><web/F>
There are lot many things
• URL design
• Quality matrix
• Verification strategies
• Test doubles
< w e b / F><web/F>
Thank You
Any more questions
< w e b / F><web/F>
By
Harshal Patil
@mistyharsh

Más contenido relacionado

La actualidad más candente

WordCamp Bournemouth 2014 - Designing with data in WordPress
WordCamp Bournemouth 2014 - Designing with data in WordPressWordCamp Bournemouth 2014 - Designing with data in WordPress
WordCamp Bournemouth 2014 - Designing with data in WordPressJonny Allbut
 
Untangling spring week5
Untangling spring week5Untangling spring week5
Untangling spring week5Derek Jacoby
 
jQuery Chicago 2014 - Next-generation JavaScript Testing
jQuery Chicago 2014 - Next-generation JavaScript TestingjQuery Chicago 2014 - Next-generation JavaScript Testing
jQuery Chicago 2014 - Next-generation JavaScript TestingVlad Filippov
 
Rapid WordPress theme development
Rapid WordPress theme developmentRapid WordPress theme development
Rapid WordPress theme developmentJonny Allbut
 
Optimizing Your Site for Holiday Traffic
Optimizing Your Site for Holiday TrafficOptimizing Your Site for Holiday Traffic
Optimizing Your Site for Holiday TrafficWP Engine UK
 
Untangling spring week4
Untangling spring week4Untangling spring week4
Untangling spring week4Derek Jacoby
 
State of jQuery June 2013 - Portland
State of jQuery June 2013 - PortlandState of jQuery June 2013 - Portland
State of jQuery June 2013 - Portlanddmethvin
 
Extension developer secrets - How to make money with Joomla
Extension developer secrets - How to make money with JoomlaExtension developer secrets - How to make money with Joomla
Extension developer secrets - How to make money with JoomlaTim Plummer
 
Bootstrap and XPages (DanNotes 2013)
Bootstrap and XPages (DanNotes 2013)Bootstrap and XPages (DanNotes 2013)
Bootstrap and XPages (DanNotes 2013)Mark Leusink
 
Theming in WordPress - Where do I Start?
Theming in WordPress - Where do I Start?Theming in WordPress - Where do I Start?
Theming in WordPress - Where do I Start?Edmund Turbin
 
Dreamweaver CS6, jQuery, PhoneGap, mobile design
Dreamweaver CS6, jQuery, PhoneGap, mobile designDreamweaver CS6, jQuery, PhoneGap, mobile design
Dreamweaver CS6, jQuery, PhoneGap, mobile designDee Sadler
 
WordPress APIs
WordPress APIsWordPress APIs
WordPress APIsmdawaffe
 
Razor into the Razor'verse
Razor into the Razor'verseRazor into the Razor'verse
Razor into the Razor'verseEd Charbeneau
 
State of play for Joomla - Nov 2014
State of play for Joomla - Nov 2014State of play for Joomla - Nov 2014
State of play for Joomla - Nov 2014Tim Plummer
 
What a Back-end Java Developer Doesn't Know About the Modern Web Stack-final
What a Back-end Java Developer Doesn't Know About the Modern Web Stack-finalWhat a Back-end Java Developer Doesn't Know About the Modern Web Stack-final
What a Back-end Java Developer Doesn't Know About the Modern Web Stack-finalRikard Thulin
 
Getting started with WordPress development
Getting started with WordPress developmentGetting started with WordPress development
Getting started with WordPress developmentSteve Mortiboy
 
Drawing the Line with Browser Compatibility
Drawing the Line with Browser CompatibilityDrawing the Line with Browser Compatibility
Drawing the Line with Browser Compatibilityjsmith92
 
Responsive Theme Workshop - WordCamp Columbus 2015
Responsive Theme Workshop - WordCamp Columbus 2015Responsive Theme Workshop - WordCamp Columbus 2015
Responsive Theme Workshop - WordCamp Columbus 2015Joe Querin
 

La actualidad más candente (20)

WordCamp Bournemouth 2014 - Designing with data in WordPress
WordCamp Bournemouth 2014 - Designing with data in WordPressWordCamp Bournemouth 2014 - Designing with data in WordPress
WordCamp Bournemouth 2014 - Designing with data in WordPress
 
Untangling spring week5
Untangling spring week5Untangling spring week5
Untangling spring week5
 
jQuery Chicago 2014 - Next-generation JavaScript Testing
jQuery Chicago 2014 - Next-generation JavaScript TestingjQuery Chicago 2014 - Next-generation JavaScript Testing
jQuery Chicago 2014 - Next-generation JavaScript Testing
 
Rapid WordPress theme development
Rapid WordPress theme developmentRapid WordPress theme development
Rapid WordPress theme development
 
Optimizing Your Site for Holiday Traffic
Optimizing Your Site for Holiday TrafficOptimizing Your Site for Holiday Traffic
Optimizing Your Site for Holiday Traffic
 
Html5
Html5Html5
Html5
 
Untangling spring week4
Untangling spring week4Untangling spring week4
Untangling spring week4
 
State of jQuery June 2013 - Portland
State of jQuery June 2013 - PortlandState of jQuery June 2013 - Portland
State of jQuery June 2013 - Portland
 
Extension developer secrets - How to make money with Joomla
Extension developer secrets - How to make money with JoomlaExtension developer secrets - How to make money with Joomla
Extension developer secrets - How to make money with Joomla
 
Bootstrap and XPages (DanNotes 2013)
Bootstrap and XPages (DanNotes 2013)Bootstrap and XPages (DanNotes 2013)
Bootstrap and XPages (DanNotes 2013)
 
Theming in WordPress - Where do I Start?
Theming in WordPress - Where do I Start?Theming in WordPress - Where do I Start?
Theming in WordPress - Where do I Start?
 
Blazor Full-Stack
Blazor Full-StackBlazor Full-Stack
Blazor Full-Stack
 
Dreamweaver CS6, jQuery, PhoneGap, mobile design
Dreamweaver CS6, jQuery, PhoneGap, mobile designDreamweaver CS6, jQuery, PhoneGap, mobile design
Dreamweaver CS6, jQuery, PhoneGap, mobile design
 
WordPress APIs
WordPress APIsWordPress APIs
WordPress APIs
 
Razor into the Razor'verse
Razor into the Razor'verseRazor into the Razor'verse
Razor into the Razor'verse
 
State of play for Joomla - Nov 2014
State of play for Joomla - Nov 2014State of play for Joomla - Nov 2014
State of play for Joomla - Nov 2014
 
What a Back-end Java Developer Doesn't Know About the Modern Web Stack-final
What a Back-end Java Developer Doesn't Know About the Modern Web Stack-finalWhat a Back-end Java Developer Doesn't Know About the Modern Web Stack-final
What a Back-end Java Developer Doesn't Know About the Modern Web Stack-final
 
Getting started with WordPress development
Getting started with WordPress developmentGetting started with WordPress development
Getting started with WordPress development
 
Drawing the Line with Browser Compatibility
Drawing the Line with Browser CompatibilityDrawing the Line with Browser Compatibility
Drawing the Line with Browser Compatibility
 
Responsive Theme Workshop - WordCamp Columbus 2015
Responsive Theme Workshop - WordCamp Columbus 2015Responsive Theme Workshop - WordCamp Columbus 2015
Responsive Theme Workshop - WordCamp Columbus 2015
 

Destacado

II - Build Automation
II - Build AutomationII - Build Automation
II - Build AutomationWebF
 
Services Factory Provider Value Constant - AngularJS
Services Factory Provider Value Constant - AngularJSServices Factory Provider Value Constant - AngularJS
Services Factory Provider Value Constant - AngularJSSumanth krishna
 
AngularJS Beginners Workshop
AngularJS Beginners WorkshopAngularJS Beginners Workshop
AngularJS Beginners WorkshopSathish VJ
 
REST Easy with AngularJS - ng-grid CRUD EXAMPLE
REST Easy with AngularJS - ng-grid CRUD EXAMPLEREST Easy with AngularJS - ng-grid CRUD EXAMPLE
REST Easy with AngularJS - ng-grid CRUD EXAMPLEreneechemel
 
AngularJS - Architecture decisions in a large project 
AngularJS - Architecture decisionsin a large project AngularJS - Architecture decisionsin a large project 
AngularJS - Architecture decisions in a large project Elad Hirsch
 
Step by Step - AngularJS
Step by Step - AngularJSStep by Step - AngularJS
Step by Step - AngularJSInfragistics
 
AngularJS application architecture
AngularJS application architectureAngularJS application architecture
AngularJS application architectureGabriele Falace
 
Single Page Application (SPA) using AngularJS
Single Page Application (SPA) using AngularJSSingle Page Application (SPA) using AngularJS
Single Page Application (SPA) using AngularJSM R Rony
 
AngularJS Architecture
AngularJS ArchitectureAngularJS Architecture
AngularJS ArchitectureEyal Vardi
 

Destacado (10)

II - Build Automation
II - Build AutomationII - Build Automation
II - Build Automation
 
Services Factory Provider Value Constant - AngularJS
Services Factory Provider Value Constant - AngularJSServices Factory Provider Value Constant - AngularJS
Services Factory Provider Value Constant - AngularJS
 
RESTEasy
RESTEasyRESTEasy
RESTEasy
 
AngularJS Beginners Workshop
AngularJS Beginners WorkshopAngularJS Beginners Workshop
AngularJS Beginners Workshop
 
REST Easy with AngularJS - ng-grid CRUD EXAMPLE
REST Easy with AngularJS - ng-grid CRUD EXAMPLEREST Easy with AngularJS - ng-grid CRUD EXAMPLE
REST Easy with AngularJS - ng-grid CRUD EXAMPLE
 
AngularJS - Architecture decisions in a large project 
AngularJS - Architecture decisionsin a large project AngularJS - Architecture decisionsin a large project 
AngularJS - Architecture decisions in a large project 
 
Step by Step - AngularJS
Step by Step - AngularJSStep by Step - AngularJS
Step by Step - AngularJS
 
AngularJS application architecture
AngularJS application architectureAngularJS application architecture
AngularJS application architecture
 
Single Page Application (SPA) using AngularJS
Single Page Application (SPA) using AngularJSSingle Page Application (SPA) using AngularJS
Single Page Application (SPA) using AngularJS
 
AngularJS Architecture
AngularJS ArchitectureAngularJS Architecture
AngularJS Architecture
 

Similar a III - Better angularjs

Handlebars and Require.js
Handlebars and Require.jsHandlebars and Require.js
Handlebars and Require.jsIvano Malavolta
 
Ionic framework one day training
Ionic framework one day trainingIonic framework one day training
Ionic framework one day trainingTroy Miles
 
WebNet Conference 2012 - Designing complex applications using html5 and knock...
WebNet Conference 2012 - Designing complex applications using html5 and knock...WebNet Conference 2012 - Designing complex applications using html5 and knock...
WebNet Conference 2012 - Designing complex applications using html5 and knock...Fabio Franzini
 
From Backbone to Ember and Back(bone) Again
From Backbone to Ember and Back(bone) AgainFrom Backbone to Ember and Back(bone) Again
From Backbone to Ember and Back(bone) Againjonknapp
 
ME vs WEB - AngularJS Fundamentals
ME vs WEB - AngularJS FundamentalsME vs WEB - AngularJS Fundamentals
ME vs WEB - AngularJS FundamentalsAviran Cohen
 
Developing Java Web Applications
Developing Java Web ApplicationsDeveloping Java Web Applications
Developing Java Web Applicationshchen1
 
Angular or Backbone: Go Mobile!
Angular or Backbone: Go Mobile!Angular or Backbone: Go Mobile!
Angular or Backbone: Go Mobile!Doris Chen
 
gDayX 2013 - Advanced AngularJS - Nicolas Embleton
gDayX 2013 - Advanced AngularJS - Nicolas EmbletongDayX 2013 - Advanced AngularJS - Nicolas Embleton
gDayX 2013 - Advanced AngularJS - Nicolas EmbletonGeorge Nguyen
 
Introduction to web application development with Vue (for absolute beginners)...
Introduction to web application development with Vue (for absolute beginners)...Introduction to web application development with Vue (for absolute beginners)...
Introduction to web application development with Vue (for absolute beginners)...Lucas Jellema
 
Seven Versions of One Web Application
Seven Versions of One Web ApplicationSeven Versions of One Web Application
Seven Versions of One Web ApplicationYakov Fain
 
JavaScript Modules Done Right
JavaScript Modules Done RightJavaScript Modules Done Right
JavaScript Modules Done RightMariusz Nowak
 
Javascript-heavy Salesforce Applications
Javascript-heavy Salesforce ApplicationsJavascript-heavy Salesforce Applications
Javascript-heavy Salesforce ApplicationsSalesforce Developers
 
Javascript first-class citizenery
Javascript first-class citizeneryJavascript first-class citizenery
Javascript first-class citizenerytoddbr
 
The State of Front-end At CrowdTwist
The State of Front-end At CrowdTwistThe State of Front-end At CrowdTwist
The State of Front-end At CrowdTwistMark Fayngersh
 
Angular JS, A dive to concepts
Angular JS, A dive to conceptsAngular JS, A dive to concepts
Angular JS, A dive to conceptsAbhishek Sur
 
Crash Course in AngularJS + Ionic (Deep dive)
Crash Course in AngularJS + Ionic (Deep dive)Crash Course in AngularJS + Ionic (Deep dive)
Crash Course in AngularJS + Ionic (Deep dive)ColdFusionConference
 
Yeoman AngularJS and D3 - A solid stack for web apps
Yeoman AngularJS and D3 - A solid stack for web appsYeoman AngularJS and D3 - A solid stack for web apps
Yeoman AngularJS and D3 - A solid stack for web appsclimboid
 
CodeFest 2014. Пухальский И. — Отзывчивые кроссплатформенные веб-приложения
CodeFest 2014. Пухальский И. — Отзывчивые кроссплатформенные веб-приложенияCodeFest 2014. Пухальский И. — Отзывчивые кроссплатформенные веб-приложения
CodeFest 2014. Пухальский И. — Отзывчивые кроссплатформенные веб-приложенияCodeFest
 
Awesome html with ujs, jQuery and coffeescript
Awesome html with ujs, jQuery and coffeescriptAwesome html with ujs, jQuery and coffeescript
Awesome html with ujs, jQuery and coffeescriptAmir Barylko
 

Similar a III - Better angularjs (20)

Handlebars and Require.js
Handlebars and Require.jsHandlebars and Require.js
Handlebars and Require.js
 
Ionic framework one day training
Ionic framework one day trainingIonic framework one day training
Ionic framework one day training
 
WebNet Conference 2012 - Designing complex applications using html5 and knock...
WebNet Conference 2012 - Designing complex applications using html5 and knock...WebNet Conference 2012 - Designing complex applications using html5 and knock...
WebNet Conference 2012 - Designing complex applications using html5 and knock...
 
From Backbone to Ember and Back(bone) Again
From Backbone to Ember and Back(bone) AgainFrom Backbone to Ember and Back(bone) Again
From Backbone to Ember and Back(bone) Again
 
ME vs WEB - AngularJS Fundamentals
ME vs WEB - AngularJS FundamentalsME vs WEB - AngularJS Fundamentals
ME vs WEB - AngularJS Fundamentals
 
Developing Java Web Applications
Developing Java Web ApplicationsDeveloping Java Web Applications
Developing Java Web Applications
 
Angular or Backbone: Go Mobile!
Angular or Backbone: Go Mobile!Angular or Backbone: Go Mobile!
Angular or Backbone: Go Mobile!
 
Handlebars & Require JS
Handlebars  & Require JSHandlebars  & Require JS
Handlebars & Require JS
 
gDayX 2013 - Advanced AngularJS - Nicolas Embleton
gDayX 2013 - Advanced AngularJS - Nicolas EmbletongDayX 2013 - Advanced AngularJS - Nicolas Embleton
gDayX 2013 - Advanced AngularJS - Nicolas Embleton
 
Introduction to web application development with Vue (for absolute beginners)...
Introduction to web application development with Vue (for absolute beginners)...Introduction to web application development with Vue (for absolute beginners)...
Introduction to web application development with Vue (for absolute beginners)...
 
Seven Versions of One Web Application
Seven Versions of One Web ApplicationSeven Versions of One Web Application
Seven Versions of One Web Application
 
JavaScript Modules Done Right
JavaScript Modules Done RightJavaScript Modules Done Right
JavaScript Modules Done Right
 
Javascript-heavy Salesforce Applications
Javascript-heavy Salesforce ApplicationsJavascript-heavy Salesforce Applications
Javascript-heavy Salesforce Applications
 
Javascript first-class citizenery
Javascript first-class citizeneryJavascript first-class citizenery
Javascript first-class citizenery
 
The State of Front-end At CrowdTwist
The State of Front-end At CrowdTwistThe State of Front-end At CrowdTwist
The State of Front-end At CrowdTwist
 
Angular JS, A dive to concepts
Angular JS, A dive to conceptsAngular JS, A dive to concepts
Angular JS, A dive to concepts
 
Crash Course in AngularJS + Ionic (Deep dive)
Crash Course in AngularJS + Ionic (Deep dive)Crash Course in AngularJS + Ionic (Deep dive)
Crash Course in AngularJS + Ionic (Deep dive)
 
Yeoman AngularJS and D3 - A solid stack for web apps
Yeoman AngularJS and D3 - A solid stack for web appsYeoman AngularJS and D3 - A solid stack for web apps
Yeoman AngularJS and D3 - A solid stack for web apps
 
CodeFest 2014. Пухальский И. — Отзывчивые кроссплатформенные веб-приложения
CodeFest 2014. Пухальский И. — Отзывчивые кроссплатформенные веб-приложенияCodeFest 2014. Пухальский И. — Отзывчивые кроссплатформенные веб-приложения
CodeFest 2014. Пухальский И. — Отзывчивые кроссплатформенные веб-приложения
 
Awesome html with ujs, jQuery and coffeescript
Awesome html with ujs, jQuery and coffeescriptAwesome html with ujs, jQuery and coffeescript
Awesome html with ujs, jQuery and coffeescript
 

Último

presentation ICT roal in 21st century education
presentation ICT roal in 21st century educationpresentation ICT roal in 21st century education
presentation ICT roal in 21st century educationjfdjdjcjdnsjd
 
Polkadot JAM Slides - Token2049 - By Dr. Gavin Wood
Polkadot JAM Slides - Token2049 - By Dr. Gavin WoodPolkadot JAM Slides - Token2049 - By Dr. Gavin Wood
Polkadot JAM Slides - Token2049 - By Dr. Gavin WoodJuan lago vázquez
 
[BuildWithAI] Introduction to Gemini.pdf
[BuildWithAI] Introduction to Gemini.pdf[BuildWithAI] Introduction to Gemini.pdf
[BuildWithAI] Introduction to Gemini.pdfSandro Moreira
 
Why Teams call analytics are critical to your entire business
Why Teams call analytics are critical to your entire businessWhy Teams call analytics are critical to your entire business
Why Teams call analytics are critical to your entire businesspanagenda
 
MS Copilot expands with MS Graph connectors
MS Copilot expands with MS Graph connectorsMS Copilot expands with MS Graph connectors
MS Copilot expands with MS Graph connectorsNanddeep Nachan
 
Vector Search -An Introduction in Oracle Database 23ai.pptx
Vector Search -An Introduction in Oracle Database 23ai.pptxVector Search -An Introduction in Oracle Database 23ai.pptx
Vector Search -An Introduction in Oracle Database 23ai.pptxRemote DBA Services
 
DBX First Quarter 2024 Investor Presentation
DBX First Quarter 2024 Investor PresentationDBX First Quarter 2024 Investor Presentation
DBX First Quarter 2024 Investor PresentationDropbox
 
ICT role in 21st century education and its challenges
ICT role in 21st century education and its challengesICT role in 21st century education and its challenges
ICT role in 21st century education and its challengesrafiqahmad00786416
 
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers:  A Deep Dive into Serverless Spatial Data and FMECloud Frontiers:  A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FMESafe Software
 
Architecting Cloud Native Applications
Architecting Cloud Native ApplicationsArchitecting Cloud Native Applications
Architecting Cloud Native ApplicationsWSO2
 
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers:  A Deep Dive into Serverless Spatial Data and FMECloud Frontiers:  A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FMESafe Software
 
How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerThousandEyes
 
CNIC Information System with Pakdata Cf In Pakistan
CNIC Information System with Pakdata Cf In PakistanCNIC Information System with Pakdata Cf In Pakistan
CNIC Information System with Pakdata Cf In Pakistandanishmna97
 
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemkeProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemkeProduct Anonymous
 
Apidays New York 2024 - APIs in 2030: The Risk of Technological Sleepwalk by ...
Apidays New York 2024 - APIs in 2030: The Risk of Technological Sleepwalk by ...Apidays New York 2024 - APIs in 2030: The Risk of Technological Sleepwalk by ...
Apidays New York 2024 - APIs in 2030: The Risk of Technological Sleepwalk by ...apidays
 
Navigating the Deluge_ Dubai Floods and the Resilience of Dubai International...
Navigating the Deluge_ Dubai Floods and the Resilience of Dubai International...Navigating the Deluge_ Dubai Floods and the Resilience of Dubai International...
Navigating the Deluge_ Dubai Floods and the Resilience of Dubai International...Orbitshub
 
Platformless Horizons for Digital Adaptability
Platformless Horizons for Digital AdaptabilityPlatformless Horizons for Digital Adaptability
Platformless Horizons for Digital AdaptabilityWSO2
 
Exploring Multimodal Embeddings with Milvus
Exploring Multimodal Embeddings with MilvusExploring Multimodal Embeddings with Milvus
Exploring Multimodal Embeddings with MilvusZilliz
 
Rising Above_ Dubai Floods and the Fortitude of Dubai International Airport.pdf
Rising Above_ Dubai Floods and the Fortitude of Dubai International Airport.pdfRising Above_ Dubai Floods and the Fortitude of Dubai International Airport.pdf
Rising Above_ Dubai Floods and the Fortitude of Dubai International Airport.pdfOrbitshub
 

Último (20)

presentation ICT roal in 21st century education
presentation ICT roal in 21st century educationpresentation ICT roal in 21st century education
presentation ICT roal in 21st century education
 
Polkadot JAM Slides - Token2049 - By Dr. Gavin Wood
Polkadot JAM Slides - Token2049 - By Dr. Gavin WoodPolkadot JAM Slides - Token2049 - By Dr. Gavin Wood
Polkadot JAM Slides - Token2049 - By Dr. Gavin Wood
 
[BuildWithAI] Introduction to Gemini.pdf
[BuildWithAI] Introduction to Gemini.pdf[BuildWithAI] Introduction to Gemini.pdf
[BuildWithAI] Introduction to Gemini.pdf
 
Why Teams call analytics are critical to your entire business
Why Teams call analytics are critical to your entire businessWhy Teams call analytics are critical to your entire business
Why Teams call analytics are critical to your entire business
 
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
 
MS Copilot expands with MS Graph connectors
MS Copilot expands with MS Graph connectorsMS Copilot expands with MS Graph connectors
MS Copilot expands with MS Graph connectors
 
Vector Search -An Introduction in Oracle Database 23ai.pptx
Vector Search -An Introduction in Oracle Database 23ai.pptxVector Search -An Introduction in Oracle Database 23ai.pptx
Vector Search -An Introduction in Oracle Database 23ai.pptx
 
DBX First Quarter 2024 Investor Presentation
DBX First Quarter 2024 Investor PresentationDBX First Quarter 2024 Investor Presentation
DBX First Quarter 2024 Investor Presentation
 
ICT role in 21st century education and its challenges
ICT role in 21st century education and its challengesICT role in 21st century education and its challenges
ICT role in 21st century education and its challenges
 
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers:  A Deep Dive into Serverless Spatial Data and FMECloud Frontiers:  A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
 
Architecting Cloud Native Applications
Architecting Cloud Native ApplicationsArchitecting Cloud Native Applications
Architecting Cloud Native Applications
 
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers:  A Deep Dive into Serverless Spatial Data and FMECloud Frontiers:  A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
 
How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected Worker
 
CNIC Information System with Pakdata Cf In Pakistan
CNIC Information System with Pakdata Cf In PakistanCNIC Information System with Pakdata Cf In Pakistan
CNIC Information System with Pakdata Cf In Pakistan
 
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemkeProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
 
Apidays New York 2024 - APIs in 2030: The Risk of Technological Sleepwalk by ...
Apidays New York 2024 - APIs in 2030: The Risk of Technological Sleepwalk by ...Apidays New York 2024 - APIs in 2030: The Risk of Technological Sleepwalk by ...
Apidays New York 2024 - APIs in 2030: The Risk of Technological Sleepwalk by ...
 
Navigating the Deluge_ Dubai Floods and the Resilience of Dubai International...
Navigating the Deluge_ Dubai Floods and the Resilience of Dubai International...Navigating the Deluge_ Dubai Floods and the Resilience of Dubai International...
Navigating the Deluge_ Dubai Floods and the Resilience of Dubai International...
 
Platformless Horizons for Digital Adaptability
Platformless Horizons for Digital AdaptabilityPlatformless Horizons for Digital Adaptability
Platformless Horizons for Digital Adaptability
 
Exploring Multimodal Embeddings with Milvus
Exploring Multimodal Embeddings with MilvusExploring Multimodal Embeddings with Milvus
Exploring Multimodal Embeddings with Milvus
 
Rising Above_ Dubai Floods and the Fortitude of Dubai International Airport.pdf
Rising Above_ Dubai Floods and the Fortitude of Dubai International Airport.pdfRising Above_ Dubai Floods and the Fortitude of Dubai International Airport.pdf
Rising Above_ Dubai Floods and the Fortitude of Dubai International Airport.pdf
 

III - Better angularjs

  • 1. < w e b / F> Better Angular.js Misnomer, myth and Angular.js goliath
  • 2. < w e b / F><web/F> TDD Test driven development
  • 3. < w e b / F><web/F> Test driven development TDD is a game of chess A best practice could be: “Never lose a piece without winning a piece of equal or greater value.”
  • 4. < w e b / F><web/F> Test driven development • In reality, there are no rules/best practices • Thereare always situations, responses to those situations and observations recordedfor future reference.
  • 5. < w e b / F><web/F> TDD Does Test Driven Developmentmeans – write tests first and then code later to pass those tests? Probably not…
  • 6. < w e b / F><web/F> Idea of TDD • TDD is about testability. • TDD is about writing testable code. • It is not about writing unit test first.
  • 7. < w e b / F><web/F> Testable code • Probably the most important aspect of TDD is readability. • We spend more time on reading a code than writing it. And the goal of TDD is to generate a testable code that is easy to read while not sacrificing its performance. • Can we call readable code as maintainablecode?
  • 8. < w e b / F><web/F> Testable code • On the surface, testable code is • Efficient • As simple as possible • DRY – Doesn’t repeat • Modular
  • 9. < w e b / F><web/F> Testable code • Inside, testable code has • Encapsulation • Loose coupling • Separation of concern • Zero to fewer global objects • Optimized design patterns
  • 10. < w e b / F><web/F> TDD on front-end Does TDD ideology holds true for front-end
  • 11. < w e b / F><web/F> What happens when we move to front-end?
  • 12. < w e b / F><web/F> But on front-end • Testability is problematic, • Because, though HTML, CSS and JS are three pieces of same stack, each of them is a stack in itself.
  • 13. < w e b / F><web/F> On front-end • The problem is • We mix HTML, CSS and JS at will • Concept of encapsulation is very vague
  • 14. < w e b / F><web/F> function implementSomeBusinessLogic() { // Some business logic var data = getLatestData(); var $div = $("<div></div>"); // Mixing HTML with JavaScript $div.text("Data: " + data); // Mixing CSS with JavaScript $div.css("color", "red"); // DOM Manipulation mixed with Business Logic $(body).append($div); } Mixing Presentation, DOM & Business logic at will.
  • 15. < w e b / F><web/F> function UserList(items) { this._items = items; } UserList.prototype.render = function () { var $list = $("#userList"); this._items.forEach(function (item) { $("<li>").text(item.name).appendTo(list); }); } UserList.prototype.search = function () { } Is this a good encapsulation?
  • 16. < w e b / F><web/F> Angular to rescue Core design and philosophy of Angular.js
  • 17. < w e b / F><web/F> Here comes the Angular • That is exactly whereAngular ratifies & embraces these problems • Threepillars of Angular
  • 18. < w e b / F><web/F> Three Pillars of Angular • Updating view (HTML/DOM) should be very trivial • And that is the first pillar of Angular.js– Data Binding DRY – Do not RepeatYourself DB – Two wayData Binding
  • 19. < w e b / F><web/F> Three Pillars of Angular • There should be proper separation of concern and for that you need structure. • That is the second pillar of Angular.js – Directives • Then there are controllers, services, routers, etc. Structure Directives
  • 20. < w e b / F><web/F> Three Pillars of Angular • It is not enough to use framework which is thoroughly tested. Code on top of it should also be readily testable. • That is the third pillar – Dependency Injection
  • 21. < w e b / F><web/F> TDD & Angular How does Angular helps write testable code?
  • 22. < w e b / F><web/F> Angular is solving many trivial front-end problems.
  • 23. < w e b / F><web/F> Problem 1 Modules Testable design is always modular. There is no exception.
  • 24. < w e b / F><web/F> Code organization - Modules • Any large scale apps needs modules to organize and group related components. • Modules help create logical boundarieswithin system. • Angular.jsmodules existed long before ES6 or Common.js modules
  • 25. < w e b / F><web/F> Organizing angular app into modules
  • 26. < w e b / F><web/F> But angular modules are yin and yang There is beast and then there is beauty.
  • 27. < w e b / F><web/F> Angular.js Modules – beasts • No support for namespace • Probably most criticized aspect of Angular.js modules • No support for lazy loading • Practically useless for any other libraries • Not very intuitiveto encapsulateCSS
  • 28. < w e b / F><web/F> The idea is to use modules for what they are supposed to be used for. Angular.js module is like a bag that holds one or more recipes.
  • 29. < w e b / F><web/F> Angular.js Modules – Beauty • Logical separation • Provide better abstractions • Lazy instantiation • Module is not instantiateduntil required • Dependencyorganization • Small modules makes for a bigger modules
  • 30. < w e b / F><web/F> Angular.js Modules – Why • Code reuse • Module can be replaced and developed in parallel. • Better separation of concern • Config and Run blocks • Config and run blocks respect dependency tree. • Code decoration
  • 31. < w e b / F><web/F> Two controllers with same name (function () { var app = angular.module("HigherModule", []); app.controller("ControllerSameName", function () { this.title = "Defined once (1)"; }); app.controller("ControllerSameName", function () { this.title = "Defined twice (2)"; }); })();
  • 32. < w e b / F><web/F> Two controllers with same name in different modules (function () { var lowerModule = angular.module("LowerModule", []); var app = angular.module("HigherModule", ["LowerModule"]); app.controller("ControllerSameName", function () { this.title = "Defined at Higher Module (2)"; }); lowerModule.controller("ControllerSameName", function () { this.title = "Defined at Lower Module (1)"; }); })();
  • 33. < w e b / F><web/F> Modules – name spacing • Often misused approach • Dot notation approach • Typically observed when backend programmer do Angular without JavaScript understanding
  • 34. < w e b / F><web/F> Two controllers with same name in different modules var lowerModule = angular.module("LowerModule", []); var app = angular.module("HigherModule", ["LowerModule"]); app.controller("HM.ControllerSameName", function () {}); lowerModule.filter("LM.customFilter", function () {});
  • 35. < w e b / F><web/F> Ideal name spacing technique for handling collisions var lowerModule = angular.module("LowerModule", []); var app = angular.module("HigherModule", ["LowerModule"]); app.controller("hmControllerSameName", function () { }); lowerModule.filter("lmCustomFilter", function () { });
  • 36. < w e b / F><web/F> Problem 2 Separation of Concern (Directives, controllers and services) Testable design adheres to high cohesion along with well defined separation of concern.
  • 37. < w e b / F><web/F> Separation of concern – Directives • Directive are heart of Angular.js • Separation of concern • DOM manipulations are abstracted away inside directives. • Services abstract away server side communication. • Promoting ideal component encapsulation
  • 38. < w e b / F><web/F> What is directivein Angular.js? Directive is something that extends the meaning of HTML.
  • 39. < w e b / F><web/F> More than one directive with same name? (function () { var app = angular.module("HigherModule", []); app.directive("mySimpleDir", function () { return { /* DDO */}; }); app.directive("mySimpleDir", function () { return { /* DDO */}; }); })();
  • 40. < w e b / F><web/F> Idea of extending HTML • Extension in software programming roughly translates to • Inheritance • New meaning • Overriding existing meaning • Polymorphism • Extending the meaning of existing items
  • 41. < w e b / F><web/F> Directives – Idea of extension Thus directives are allowed to extend the meaning… Period.
  • 42. < w e b / F><web/F> Overriding default browser behavior (function () { var app = angular.module("HigherModule", []); // Extending browser autofocus attribute app.directive("autofocus", function () { return { /* DDO */}; }); })();
  • 43. < w e b / F><web/F> Problem 3 Loose Coupling (Dependency Injection and Angular Bootstrapping) Software design without loose coupling is never testable.
  • 44. < w e b / F><web/F> Loose coupling • Loose coupling– IoC (Inversion of Control) • Don’t call me; I will call you • It is always Angular that initiates the call • Example, • Angular decides when to instantiatecontroller • Angular is responsible for loading templates
  • 45. < w e b / F><web/F> Idea of IoC – Inversion of Control Main Traditional jQuery style
  • 46. < w e b / F><web/F> Idea of IoC – Inversion of Control Main Main Dependency Injector Traditional jQuery style This is how Angular does it
  • 47. < w e b / F><web/F> Dependency injection • Angular implementsIoC using DI • DI internallyuses Service Locator pattern • To understandDI, we have to understand Angular bootstrapping
  • 48. < w e b / F><web/F> Angular bootstrap process Diagram does not illustratethe broader picture. So let’stry to drill down further…
  • 49. < w e b / F><web/F> Angular composition This is whatAngularis madeup of
  • 50. < w e b / F><web/F> Angular composition Theseboxes areobjects communicatingwith each other. That’s theneed forDI.
  • 51. < w e b / F><web/F> Angular composition Theseobjects areangularcomponents Filters Controllers Services Constants ProvidersDirectives Factories Values
  • 52. < w e b / F><web/F> Angular composition Special Objects Controllers Directives Filters Animations Custom objects Services The world of Angularcan be categorized into The only catch is they should be singleton. Angularcalls these customer objects as services.
  • 53. < w e b / F><web/F> Creating various angular objects • To create Angular.js objects (custom objects or special objects), use providers: • $controllerProvider • $compileProvider • $filterProvider • $provide (service Provider) • Beware of Internet/Google/Angular.js docs https://docs.angularjs.org/api/auto/service/$provide
  • 54. < w e b / F><web/F> Angular bootstrap process ANGULAR TWO DISTINCT PHASES Configuration Phase Run Phase
  • 55. < w e b / F><web/F> Angular bootstrap process Use different providers toregister your components/classes/objects with Angular modules. Angular calls it recipes for object creation; Configuration Phase module.config();
  • 56. < w e b / F><web/F> Angular bootstrap process Instantiate objects from the components/classes registered during configuration phase. Run Phase module.run();
  • 57. < w e b / F><web/F> Angular bootstrap process BrowserHTML page Wait for DOM ready Search ng-app=“module” Initialize $injector .config() .run() Start playingwith DOM IoC – Inversion of Control
  • 58. < w e b / F><web/F> Code sample with config block (function () { var app = angular.module("HigherModule", []); app.config(function ($controllerProvider) { $controllerProvider.register("MyCtrl", function () { var vm = this; vm.title = "My Title"; }); }); })();
  • 59. < w e b / F><web/F> Reduced boilerplate code app.controller("MyCtrl", function () { var vm = this; vm.title = "My Title"; });
  • 60. < w e b / F><web/F> Creating a directive app.config(function ($compileProvider) { $compileProvider.directive("mySimpleDir", function () { // DDO - Directive Definition Object }); }); app.directive("mySimpleDir", function () { // DDO - Directive Definition Object });
  • 61. < w e b / F><web/F> Built in objects in Angular • Angular understands: • Controllers • Filters • Directives • Animation Built in object Correspondingproviders Controller $controllerProvider Filter $filterProvider Directive $compileProvider Animation $animationProvider
  • 62. < w e b / F><web/F> Can we call providers as Classes in plain old JavaScript?
  • 63. < w e b / F><web/F> Creating custom objects • Remember, Customer objects are called services in Angular • To create any object in Angular you need provider • To create custom object, custom provider is necessary • For that purpose, we need a provider that can create new provider which is sort of meta provider
  • 64. < w e b / F><web/F> About custom objects • One condition by Angular.js • Custom Objects (services) should be Singletons • It is enforced at Framework level by Angular • One assumption by Angular.js • Custom Objects should hold your data
  • 65. < w e b / F><web/F> Comes $provide app.config(function ($provide) { $provide.provider("calculator", function () { this.$get = function () { return { add: function () { }, subtract: function () { } }; }; }); });
  • 66. < w e b / F><web/F> Using custom object app.controller("MyCtrl", function (calculator) { calculator.add(); calculator.subtract(); });
  • 67. < w e b / F><web/F> Reducing boilerplate – Why so verbose? app.provider("calculator", function () { this.$get = function () { return { add: function () { }, subtract: function () { } }; }; });
  • 68. < w e b / F><web/F> Providers are still verbose & weird way to create objects
  • 69. < w e b / F><web/F> How we do it in plain old JavaScript? (function () { function Calculator() { this.add = function () { }; this.substract = function () { }; } var calculator = new Calculator(); })();
  • 70. < w e b / F><web/F> Comparing plain JS and Angular (function () { function Calculator() { this.add = function () { }; this.substract = function () { }; } var calculator = new Calculator(); })(); app.provider("calculator", function () { this.$get = function () { return { add: function () { }, subtract: function () { } }; }; });
  • 71. < w e b / F><web/F> So Angular is creating syntactic sugar app.service("calculator", function () { this.add = function () { }; this.substract = function () { }; });
  • 72. < w e b / F><web/F> Comparing two syntax app.service("calculator", function () { this.add = function () { }; this.substract = function () { }; }); app.provider("calculator", function () { this.$get = function () { return { add: function () { }, subtract: function () { } }; }; });
  • 73. < w e b / F><web/F> What angular is doing internally app.service("calculator", function () { this.add = function () { }; this.substract = function () { }; }); app.service = function (name, Class) { app.provider(name, function () { this.$get = function ($injector) { return $injector.instantiate(Class); }; }); }
  • 74. < w e b / F><web/F> Some are not comfortable with new function calculatorFactory() { var obj = {}; obj.add = function () { }; obj.substract = function () { }; return obj; } var calculator = calculatorFactory(); Enter the factory pattern
  • 75. < w e b / F><web/F> Angular has solution for that function calculatorFactory() { var obj = {}; obj.add = function () { }; obj.substract = function () { }; return obj; } var calculator = calculatorFactory(); app.factory("calculator", function () { return { add: function () { }, substract: function () { } }; });
  • 76. < w e b / F><web/F> Angular factory pattern app.factory("calculator", function () { return { add: function () { }, substract: function () { } }; }); app.factory = function (name, factory) { app.provide(name, function () { this.$get = function ($injector) { return $injector.invoke(factory); }; }); }
  • 77. < w e b / F><web/F> Then there are other recipes app.constant("STATES", { DASHBOARD: "dashboard", LIST: "projectList" }); app.value("calculator", function () { }); app.value("PI", 3.1422); app.value("welcome", "Hi, Harsh");
  • 78. < w e b / F><web/F> But, yes providers are powerful app.provider("greeting", function () { var text = "Hello, "; this.setText = function (value) { text = value; }; this.$get = function () { return function (name) { alert(text + name); }; }; }); app.config(function (greetingProvider) { greetingProvider .setText("Howdy there, "); }); app.run(function (greeting) { greeting("Harsh Patil"); });
  • 79. < w e b / F><web/F> Angular core - $injector • So far, we have just seen how to bootstrap angular. But how does Angular executes in run phase?
  • 80. < w e b / F><web/F> $injector internals app.value("val", ""); app.constant("CON", 123); app.controller("MyCtrl", function () { }); app.factory(“myFactory", function () { }); Instance Factory Instance Cache Service Locator $injector $injector.get("myFactory");
  • 81. < w e b / F><web/F> Problem 4 Using optimized design patterns Testable software design implements well thought design patterns
  • 82. < w e b / F><web/F> Typical Ajax Request example var http = new XMLHttpRequest(), url = "/example/new", params = encodeURIComponent(data); http.open("POST", url, true);
  • 83. < w e b / F><web/F> Typical Ajax Request example http.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); http.setRequestHeader("Content-length", params.length); http.setRequestHeader("Connection", "close");
  • 84. < w e b / F><web/F> Typical Ajax Request example http.onreadystatechange = function () { if (http.readyState == 4 && http.status == 200) { alert(http.responseText); } } http.send(params);
  • 85. < w e b / F><web/F> Same thing with Angular $http({ method: "POST", url: "/example/new", data: data }) .then(function (response) { alert(response); });
  • 86. < w e b / F><web/F> Problem 5 Data Binding DOM updates should be very trivial. This is the magic of angular.
  • 87. < w e b / F><web/F> Plain JS code // HTML <button id="myButton"></button> // JavaScript var button = document.getElementById("myButton"); button.addEventListener("click", function () { // Do something }, false);
  • 88. < w e b / F><web/F> How we do it in Angular // HTML <button ng-click="doSomething()"></button> // JavaScript app.controller("MyCtrl", function ($scope) { $scope.doSomething = function () { // Do something }; });
  • 89. < w e b / F><web/F> Creating function in JavaScript // Technique 1 function test() { // Do something } // Technique 2 var test = function () { };
  • 90. < w e b / F><web/F> There is something more to JS // Technique 3 var test = new Function(arguments, body); Functions in JavaScriptare objects. They can be createdjust like any other objects.
  • 91. < w e b / F><web/F> What angular is doing internally // HTML <button ng-click="doSomething()"></button> This is a directive
  • 92. < w e b / F><web/F> What angular is doing internally link: function ($scope, iElement, iAttr) { iElement.addEventListener("click", function () { var ngClick = iAttr.ngClick; var func = new Function([], ngClick); // Setup Angular watcher ... func(); // Execute watchers & bindings ... // Run $scope.digest(); }, false); }
  • 93. < w e b / F><web/F> Angular digest function $scope.prototype.$digest = function () { var self = this; _.forEach(this.$$watchers, function (watch) { var newValue = watch.watchFn(self); var oldValue = watch.last; if (newValue !== oldValue) { watch.listenerFn(newValue, oldValue, self); watch.last = newValue; } }); };
  • 94. < w e b / F><web/F> Modern Enterprise Web Apps
  • 95. < w e b / F><web/F> Modern enterprise web application
  • 96. < w e b / F><web/F> Then they evolve
  • 97. < w e b / F><web/F> • Thereis nothing permanent except change.
  • 98. < w e b / F><web/F> Then it becomes all together different
  • 99. < w e b / F><web/F> Modern enterprise applications • API • Third party integrations • Packages • Libraries • Modules • Legacies • Versioning • Limited eye site for individual developer & tester • Nearly impossible to test everything through leaky abstractions Abstractions leak when you have to understand the lower level concept to use the higher level concept.
  • 100. < w e b / F><web/F> So how do you write testable code? • One sure shot way is to write Unit Tests.
  • 101. < w e b / F><web/F> When you write unit tests • When you start for unit testing, you disintegrateor more accurately isolate different components of the system. • That is where you can see system as a set of interconnected components. • And so goes for the couplingsbetween components.
  • 102. < w e b / F><web/F> But most unit testing is not proper • If you do any of this • Database • Any kind of server • File/network I/O or file system • Another application • Console • Logging • Other classes
  • 103. < w e b / F><web/F> So when do you write unit tests? • Never do it. Let QA do it • After some amount of code • After writing all the code • Before any code* (Test first) • At the same time as I write the code
  • 104. < w e b / F><web/F> Unit testing in day to day life 1. Describe the behavior of function in plain English in some readme file. 2. Create a test that is mostly just a copy/paste from the readme code. 3. Write the actual function until it passes the first test. 4. Write a few more tests for any edge cases I can think of. 5. Get the function to pass again 6. Encounter another edge case, go to 4.
  • 105. < w e b / F><web/F> But I find writing unit tests hard • Because the reality check says unit testing is hard
  • 106. < w e b / F><web/F> To simplify Categorize unit tests as: • Level 1 – Isolated classes • Level 2 – State management • Level 3 – Internal dependency • Level 4 – State management, internal dependency & DOM
  • 107. < w e b / F><web/F> Level 1 Isolated Classes/Components
  • 108. < w e b / F><web/F> app.factory("utilsFactory", function () { return { constructUrl: function (url, urlParams) { urlParams = urlParams || {}; Object.keys(urlParams).forEach(function (param) { url = url.replace(":" + param, urlParams[param]); }); return url; } }; }); No Dependency No state management
  • 109. < w e b / F><web/F> it("utilsFactory should construct url correctly", function () { var url, params, url; // Setup data url = "/api/projects/:projectId"; params = { projectId: 1 }; // Exercise SUT url = utilsFactory.constructUrl(url, params); // Verify result (behavior) expect(url).to.equal("/api/projects/1"); });
  • 110. < w e b / F><web/F> We need supporting code as well beforeEach(function () { module("myModule"); inject(function (_utilsFactory_) { utilsFactory = _utilsFactory_; }); });
  • 111. < w e b / F><web/F> Level 2 State Management
  • 112. < w e b / F><web/F> app.factory("utilsFactory", function () { return { _count: 0, constructUrl: function (url, urlParams) { urlParams = urlParams || {}; // ... this._count++; return url; }, }; });
  • 113. < w e b / F><web/F> it("utilsFactory should update _count", function () { var url, params, url; // Setup data url = "/api/projects/:projectId"; params = { projectId: 1 }; // Exercise SUT url = utilsFactory.constructUrl(url, params); url = utilsFactory.constructUrl(url, params); // Verify result (state) expect(utilsFactory._count).to.equal(2); });
  • 114. < w e b / F><web/F> Level 3 Internal dependency
  • 115. < w e b / F><web/F> app.factory("projectFactory", function projectFactory($http, urlFactory) { function _getProjects() { var request = angular.copy({ method: "GET", url: urlFactory.get("projects") }); return $http(request).then(function (response) { return response.data; }); } return { getProjects: _getProjects }; });
  • 116. < w e b / F><web/F> it("projectFactory should return promise", function () { var promise; // Setup data // Exercise SUT promise = projectFactory.getProject(); // Verify result (state) expect(promise).to.have.key("then"); });
  • 117. < w e b / F><web/F> it("projectFactory should make call to urlFactory", function () { var promise; // Setup data // Exercise SUT promise = projectFactory.getProjects(); // Verify result (state) expect(urlFactory.get).to.have.been.called.once(); });
  • 118. < w e b / F><web/F> it("projectFactory should make call to $http", function () { var url, promise; // Setup data // Setup expectation $httpBackend.expectGET("/api/users/").respond(200, {}); // Exercise SUT promise = projectFactory.getProjects(); // Verify result (behavior) $httpBackend.verifyNoOutstandingExpectation(); $httpBackend.verifyNoOutstandingRequest(); });
  • 119. < w e b / F><web/F> beforeEach(function () { module("myModule"); inject(function (_projectFactory_, _$httpBackend_, _urlFactory_) { projectFactory = _projectFactory_; $httpBackend = _$httpBackend_; urlFactory = _urlFactory_; // Mocking urlMock = sinon.mock(urlFactory); urlMock.stub("get").when("/api/project").returns("/api/projects"); }); });
  • 120. < w e b / F><web/F> it(“description", function () { // Setup data // Setup expectation // Exercise SUT // Verify result (behavior) // Teardown });
  • 121. < w e b / F><web/F> What do I need to write unit tests? • Test runner • Runtime • Testing suite • Assertion library • Mocking library
  • 122. < w e b / F><web/F> Quality of unit tests A management stake (code coverage) “I expect a high level of coverage. Sometimes managers require one. There's a subtle difference.” “you can't go into production with less than 80% coverage.”
  • 123. < w e b / F><web/F> True use of code coverage Just like this 
  • 124. < w e b / F><web/F> True use of code coverage
  • 125. < w e b / F><web/F> True use of code coverage 80%, 90% is good but 100% “It would smell of someone writing tests to make the coverage numbers happy, but not thinking about what they are doing.”
  • 126. < w e b / F><web/F> There are lot many things • URL design • Quality matrix • Verification strategies • Test doubles
  • 127. < w e b / F><web/F> Thank You Any more questions
  • 128. < w e b / F><web/F> By Harshal Patil @mistyharsh