יום שני, ספטמבר 28, 2015
One of the more interesting features of Angular 2 is its improved dependency injection mechanism
Unlike Angular 1.X you don't need to specify the dependencies manually in order to preserve minification capabilities
Angular 2 is smart enough to use the type information of the dependency and inject the correct implementation at run time
Can we do the same with Angular 1.X ? Sure, keep reading ...
First lets describe the way we define a controller
import {MyApp} from '../App';
import {Controller} from '../Common/Decorators';
import {Contact, ContactService} from '../Services/ContactService';
@Controller(MyApp, "HomeCtrl")
class HomeCtrl {
contacts: Contact;
constructor(contactService: ContactService) {
contactService.getAll().then(contacts => {
this.contacts = contacts;
});
}
}
And the service dependency
import {MyApp} from '../App';
import {Service, Inject} from '../Common/Decorators';
@Service(MyApp, "contactService")
export class ContactService {
constructor(@Inject("$http") private $http: ng.IHttpService) {
}
getAll(): ng.IPromise<Contact> {
return this.$http.get("/contacts.json").then(response => response.data);
}
}
export interface Contact {
id: number;
name: string;
}
Second we need to define the Controller and Service decorators (A.K.A attribute/annotation)
export function Controller(module: ng.IModule, name: string) {
return function (target: Function) {
Reflect.defineMetadata("controller", target, { controllerName: name });
module.controller(name, target);
set$inject(target);
}
}
export function Service(module: ng.IModule, name: string) {
return function (target: Function) {
Reflect.defineMetadata("service", { serviceName: name }, target);
module.service(name, target);
set$inject(target);
}
}
For more information on...
יום שלישי, יוני 30, 2015
I just encountered a brilliant post written by Eyal Vardi (Hebrew only)
Eyal talks about how we can use Angular metadata stored inside $inject to automatically inject the controller's dependencies into the controller instance
This way you don't need to manually copy local dependency parameters into the controller instance.
Consider the following Typescript controller + HTML view
class HomeCtrl {
$scope;
$http;
items: any;
constructor($scope, $http) {
this.$scope = $scope;
this.$http = $http;
}
refresh() {
this.$http.get("/api/item").then((items) => {
this.items = items;
});
}
}
angular.module("MyApp").controller("HomeCtrl", );
<div ng-controller="HomeCtrl as ctrl">
<ul>
<li ng-repeat="item in ctrl.items">
<span>{{item.name}}</span>
</li>
</ul>
<div>
<button ng-click="ctrl.refresh()">Refresh</button>
</div>
</div>
As you can see all dependencies specified by angular.controller function are copied manually into the controller instance so we can later use them inside the refresh function.
When writing "plain" Angular controller based on simple function (without prototype) the dependencies are specified as local parameters...
יום שבת, יוני 27, 2015
(I find below bits quite useful so I add them to github. You can get them by using bower install angular1-zones)
One of the cool features of Angular 2 is that there is no need to call $scope.$apply. This is true even if you are using 3rd party asynchronous library which Angular is unaware of.
The magic resides inside Angular 2 library named zone.js
The library overrides all standard browser APIs that are considered asynchronous. zone.js injects its own implementation and uses it to monitor the start and completion of any asynchronous activity.
Consider the following Angular 1.4 source code
function HomeCtrl($scope) {
$scope.change = function () {
setTimeout(function () {
$scope.message = "XXX";
}, 1000);
}
}
This is a plain controller...
יום ראשון, אפריל 5, 2015
Through my previous post I described the challenges when implementing long running operation. Basically, you need to break your long running operation into small steps where each step is isolated by setTimeout(0) from the next step.
At this post I would like to cover a technique for using promise object to manage the long running operation.
Angular's promises offer the common then/catch functions but also offer an additional function named notify
The notify function can be used to send notifications to the client about the progress of the running operation.
For example, suppose a runTask function is responsible for initiating a long running...
יום שבת, מרץ 21, 2015
Suppose I need to implement a long running operation (longer than a few seconds) and during that operation display some user feedback
Well, JavaScript is single threaded. This means that during operation execution the single thread is busy and cannot handle UI events like mouse move and mouse click. From end user perspective the UI is dead.
Modern solution is to use Web Worker. As long as your algorithm is pure (only accesses JavaScript objects) then you are OK. However, if the algorithm needs to access DOM objects you cannot use web workers since they have no access to the DOM
I agree that...
יום שלישי, פברואר 24, 2015
What is wrong with below code ?
angular.module("MyApp", ).config(function (MyProvider) {
});
angular.module("MyApp").provider("My", function () {
this.$get = function () {
var service = {
doSomething: function () {
console.log("Do something ...");
}
};
return service;
}
});
Nothing. But, depends on the version of Angular you are using you might encounter errors.
Running above code with Angular 1.2.9 generates the following error:
Failed to instantiate module MyApp due to unknown provider: MyProvider
Apparently, Angular 1.2.9 queues config callbacks and provider registration requests into the same queue. Lets look at Angular 1.2.9 implementation for a module
function module(name, requires, configFn) {
var invokeQueue = ;
var runBlocks = ;
var moduleInstance = {
provider: invokeLater('$provide', 'provider'),
config: invokeLater('$injector', 'invoke'),
};
return moduleInstance;
function invokeLater(provider, method, im) {
return function() {
invokeQueue();
return moduleInstance;
};
}
});
};
A module instance holds two private queues, invokeQueue and runBlocks. Both provider and config methods are using invokeQueue queue
Later, Angular loads the module by walking the queues and invoking all registered callbacks.
Our problematic sample code first registers a config block
angular.module("MyApp", ).config(function (MyProvider) {
});
And only...
יום חמישי, פברואר 19, 2015
Not every class inside Angular application is a service
For example, consider the following class definition
function Contact($http, name, email) {
this.$http = $http;
this.name = name;
this.email = email;
}
Contact.prototype.dump = function () {
console.log(this.name + ", " + this.email);
}
Contact.prototype.save = function () {
//
// Work against this.$http
//
}
Above class represents a single contact and therefore is instantiated multiple times during application execution. This means, that we cannot register it as an Angular factory/service
However, the Contact class has $http dependency. This means that the caller must supply it manually. For example, assuming the caller is a controller which has a reference to $injector it can do the following
angular.module("MyApp").controller("HomeCtrl", function ($injector) {
var $http = $injector.get("$http");
var contact = new Contact($http, "Ori", "ori.calvo@gmail.com");
contact.dump();
});
However, what if the Contact class has many dependencies that must be resolved. Calling $injector.get for every dependency is quite tedious
Fortunately, Angular offers a...
יום חמישי, ינואר 1, 2015
There are two ways writing controllers in AngularJS
Function based
Class based
For some reason, the first method is more popular (probably, because Angular's tutorials are written that way)
(function (angular) {
angular.module("MyApp").controller("HomeCtrl", function ($scope) {
$scope.sayHello = function () {
alert("Hello");
}
});
})(angular);
And the corresponding template
<div ng-controller="HomeCtrl">
<button ng-click="sayHello()">Say Hello</button>
</div>
The other method is more elegant. You write the controller just as any other JavaScript class
(function (angular) {
function HomeCtrl() {
}
HomeCtrl.prototype.sayHello = function () {
alert("Hello");
}
angular.module("MyApp").controller("HomeCtrl", HomeCtrl);
})(angular);
However, Angular does not support this style out of the box. You need to update the template
<div ng-controller="HomeCtrl as ctrl">
<button ng-click="ctrl.sayHello()">Say Hello</button>
</div>
From my POV the big advantage using the later implementation is that it allows you to use a base controller class
var MyApp = MyApp || {};
MyApp.BaseCtrl = (function () {
function BaseCtrl($scope) {
var me = this;
me.$scope = $scope;
me.$scope.$on("$destroy", function () {
me.onDispose();
});
}
BaseCtrl.prototype.onDispose = function () {
var me = this;
console.log(me.name + ".dtor");
}
return BaseCtrl;
})();
The BaseCtrl is a great place to store common methods required by all controllers. In my sample above I register to...
יום שישי, דצמבר 5, 2014
In one of my Angular projects I needed to control the way a controller is instantiated
By default, the developer specifies the controller name inside the HTML or inside a route configuration and then Angular instantiates the controller.
Suppose I want to the instantiate the controller my self and return it to Angular
Well, this should be quite easy
angular.module("MyApp").controller("HomeCtrl", function () {
function HomeCtrl() {
this.name = "Ori";
}
HomeCtrl.prototype.sayHello = function () {
alert(this.name);
}
return new HomeCtrl();
});
As you can see, I am not registering a JavaScript constructor but rather use anonymous function and then return a new controller object.
Running this code under Angular 1.2 completes without any issue
However, when switching to Angular 1.3 i noticed that all controllers instantiated this...
יום חמישי, אוקטובר 16, 2014
Angular 1.3 was released two days ago. There are some nice small improvements that should be noticed. High level overview can be found here
I think that the most noticeable improvement is the "One time binding" syntax. Angular team was talking a lot at the last two years about "performance issues with Angular". Their main conclusion is that there is no real performance issue. Funny, the first topic to be covered with 1.3 release is about performance. Still, I am happy to see that they are trying to target those issues.
However, at this post I am going to look at a...