Skip to content
This repository was archived by the owner on Apr 12, 2024. It is now read-only.
This repository was archived by the owner on Apr 12, 2024. It is now read-only.

Prototype Accessors Called Before Constructor/Injection. #12865

Closed
@rcollette

Description

@rcollette

I'm seeing a behavior that seems to be both undocumented, and leads to an "impedance mismatch" with TypeScript, which I think the Angular team is generally interested in.

In AngularJs, prototype accessors of a directive's controller are called before the controller's constructor. I am not sure where this is documented or why it happens this way. If an accessor needs to use an injected service, you have to abandon that first set call. I don't know if returning from a setter and doing nothing has ramifications in a real world setting, but in a simple example it seems to solve the problem. "Prototype accessors" (my terminology may be incorrect) are what are generated by TypeScript and I believe are what ES6 get/set accessors do.

  var TestController1 = (function() {
    function TestController1(testService) {
      console.log("TestController1 constructor");
      this.testService = testService;
    }
    //This is how TypeScript generates properties.
    Object.defineProperty(TestController1.prototype, "aprop", {
      get: function() {
        console.log("TestController1 Get Value:",this._aProperty);
        return this._aProperty;
      },
      set: function(value) {
        console.log("TestController1 setter called with value: ", value);
        if(!this.testService){
          //in order to make this work I have to exit on the first call.
          console.log("TestController1: exiting, no service yet");
          return;
        }
        this._aProperty = this.testService.doSomething(value);
      },
      enumerable: true,
      configurable: true
    });
    return TestController1;
  })();

To make the "first" (which may not actually be the first) set call work, I can define a property on _this captured in the constructor. In this case the accessor has access to the injected service it needs. But now I'm back to the bloated defineProperty(ies) JS syntax rather than being able to use TypeScript's get/set syntax.

  var TestController2 = (function() {
    function TestController2(testService) {
      console.log("TestController2 constructor");
      var _this = this;
      this.testService = testService;

      //This is how I would typically create properties with angular
      Object.defineProperty(_this, "aprop", {
        get: function() {
          console.log("TestController2 Get Value:",this._aProperty);
          return this._aProperty;
          return _this._aProperty;
        },
        set: function(value) {
          console.log("TestController2 setter called with value: ", value);
          this._aProperty = _this.testService.doSomething(value);
        },
        enumerable: true,
        configurable: true
      });
    }
    return TestController2;
  })();

Console output:

script.js:61 TestController2 constructor
script.js:28 TestController1 setter called with value:  x
script.js:31 TestController1: exiting, no service yet
script.js:18 TestController1 constructor
script.js:72 TestController2 setter called with value:  y
script.js:8 service called with value:  y
script.js:28 TestController1 setter called with value:  x
script.js:8 service called with value:  x
script.js:68 TestController2 Get Value: Hello y
script.js:68 TestController2 Get Value: Hello y
script.js:24 TestController1 Get Value: Hello x
script.js:24 TestController1 Get Value: Hello x
script.js:68 TestController2 Get Value: Hello y
script.js:24 TestController1 Get Value: Hello x

See:
http://plnkr.co/edit/LDiKSzIJe2dQ7X3jS7V8?p=preview

Is this documented and intended behavior, or should TypeScript classes with getters/setters be able to use injected services straight away?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions