angularjsimmutabilityangularjs-provider

How to create any kind of immutable providers in AngularJS


Consider the following example:

angular.module('demo')
    .service('MyService', function () {
        this.fn = function () {
            console.log('MyService:fn');
        };
    })
    .factory('MyFactory', function () {
        function fn() {
            console.log('MyFactory:fn');
        }
        return { fn: fn };
    })
    .value('MyValue', {
        fn: function () {
            console.log('MyValue:fn');
        }
    })
    .constant('MyConstant', {
        fn: function () {
            console.log('MyConstant:fn');
        }
    })
    .run(function (MyService, MyFactory, MyValue, MyConstant) {
        MyService.fn();
        MyFactory.fn();
        MyValue.fn();
        MyConstant.fn();

        MyService.fn = undefined;
        MyFactory.fn = undefined;
        MyValue.fn = undefined;
        MyConstant.fn = undefined;
    })
    .run(function (MyService, MyFactory, MyValue, MyConstant) {
        MyService.fn();
        MyFactory.fn();
        MyValue.fn();
        MyConstant.fn();
    });

When the first run() is executed, all 4 console logs will be executed and print something on the console. Then I set each of the providers fn function to undefined for simplification purposes, say someone rewrote this function somewhere (which is something I want to prevent).

On the second run() block, everything is undefined and errors will be thrown. I'm confused by this... Shouldn't at least some of them (constant is the first to come to mind) be immutable objects?

Is this the expected behavior or am I doing something wrong?


Solution

  • Why is this a surprise. Angular is a framework running on top of Javascript, and Javascript is a dynamic language. Your question is really about the language construct.

    First, recognize that all the calls are, at the end of the day, registering a provider that would return an object to be injected. .service, .factory, and .value are just short hands for .provider (.constant is a bit different).

    Having established that there is no difference between them once the object is injected, all you then need to concern yourself with is how to make that object immutable.

    Javascript provides Object.freeze function, so for example, you could do:

    var immutable = {
            fn: function () {
                console.log('MyConstant:fn');
            }
        };
    Object.freeze(immutable);
    
    app.constant("MyConstant", immutable);