Angular 1.3 – Creating a controller

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 way are not functioning … totally

Since this is not a common scenario I didn't bather looking for an answer using public knowledge like forums and others.

Instead, I jumped into Angular source code. Maybe I can figure up this myself.

Under Angular 1.2 the code that instantiates a controller looks like

function instantiate(Type, locals) {
    var Constructor = function () { }, instance, returnedValue;

    Constructor.prototype = (isArray(Type)? Type[Type.length-1] : Type).prototype;
    instance = new Constructor();

    returnedValue = invoke(Type, instance, locals);

    return isObject(returnedValue) ||
                           isFunction(returnedValue) ? returnedValue : instance;
}

Type is the controller constructor to be instantiated.

However, Angular does not instantiate the Type itself but rather creates a new function named Constructor and then instantiate it.

You probably wonder why Angular uses another constructor. The answer resides inside line 2 of the method

Constructor.prototype = (isArray(Type)? Type[Type.length-1] : Type).prototype;

Angular chains the Constructor.prototype to Type.prototype. This is a classical manipulation for simulating inheritance in JavaScript. Now, Angular can instantiate the Constructor but without actually invoking the Type constructor. The Type constructor will be invoked later using Angular dependency injection mechanism.

returnedValue = invoke(Type, instance, locals);

invoke method is smart enough to invoke the specified function (Type, in our case) and apply the correct context (this) for the function invocation while resolving all dependencies annotated by the Type constructor. This is the regular Angular pattern for supporting dependency injection so I am not getting into to much details.

The important thing to notice is that Angular ignores the instance variable it just created and instead saves the return value of the invoke function and uses it as the controller object.

This means that we can return any object from our controller constructor and it will be considered as the controller.

So, what about Angular 1.3 (I removed some code to improve readability) ?

function instantiate(expression, locals) {
    var instance, constructor;

    var Constructor = function() {};
    Constructor.prototype = (isArray(expression) ?
        expression[expression.length - 1] : expression).prototype;
    instance = new Constructor();

    return extend(function() {
        $injector.invoke(expression, instance, locals, constructor);
        return instance;
    }, {
        instance: instance,
        identifier: identifier
    });
};

As before, Angular creates a new Constructor function and instantiate it, then it uses $injector.invoke to invoke the controller's constructor and then …. ooops

Did you notice? Angular does not save the return value as it did on version 1.2. This means that it totally ignores our controller object and rather stick to the object it creates by itself.

Is this a bug? A feature? I am not sure. What do you think ?

Add comment
facebook linkedin twitter email

Leave a Reply

Your email address will not be published.

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

*