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

Isolate scope properties not bound to controller #15619

Closed
thany opened this issue Jan 18, 2017 · 7 comments
Closed

Isolate scope properties not bound to controller #15619

thany opened this issue Jan 18, 2017 · 7 comments

Comments

@thany
Copy link

thany commented Jan 18, 2017

Do you want to request a feature or report a bug?
Bug.

What is the current behavior?
The properties set in an isolated scope object hash for a directive, are not bound to the controller, even when using bindToControler: true. They only appear to be bound to the template.

Here's a demo: https://jsfiddle.net/z9pgasbt/
It will console.log undefined for a value called name that has been bound via isolate scope to the controller. All documentation and tutorials point to it should work this way.

What is the expected behavior?
The above example should print "Foobar" instead of undefined. Technically speaking, the properties defined in an isolate scope should be bound to the controller, and should be usable as such in the controller - not just in the template.

What is the motivation / use case for changing the behavior?
Using bindToController with controllerAs as it appears to be intended.

Which versions of Angular, and which browser / OS are affected by this issue? Did this work in previous versions of Angular? Please also test with the latest stable and snapshot (https://code.angularjs.org/snapshot/) versions.
Firefox 50, AngularJS 1.6.1.
Tested with snapshot version - problem persists.

@gkalpak
Copy link
Member

gkalpak commented Jan 18, 2017

It is because of this documented breaking change. The best approach is to use the $onInit() lifecycle hook for accessing bindings (and for your initialization logic in general).

@thany
Copy link
Author

thany commented Jan 18, 2017

I'm not quite sure that adding some function to the controller is more elegant than scope properties be bound to the controller before it gets called. Not saying it's bad, just... Odd.

Do you have (a link to) a more detailed explanation of the reasoning behind this decision? Because I honestly don't see the advantage of having a discrete magic function.

@gkalpak
Copy link
Member

gkalpak commented Jan 18, 2017

I'm not quite sure that adding some function to the controller is more elegant than scope properties be bound to the controller before it gets called.
[...]
I honestly don't see the advantage of having a discrete magic function.

It is not a magic function. It is a lifecycle hook 😃

Lifecycle hooks enable developers to hook into certain points in the lifecycle of a component (initialization, removal, input updates etc) and perform relevant operations easily. You can read more about it in the Dev Guide.

The Angular 1 hooks are modelled as close as reasonable after Angular 2+ hooks, so it is easy to migrate. We implemented them because they turned out to be useful - they are not directly related to deprecating bindings pre-assignment. (Indeed the $onInit() hook existed before we disabled bindings pre-assignment.)

There are several drawbacks for pre-assignment, including (but not limited to):

  1. Increased implementation complexity (pre-assigning the bindings requires a lot of creativity 😃)
  2. Incompatibility with ES2015 classes (as a direct result of (1)).
  3. Certain usecases are difficult of impossible (e.g. using getters/setters, default values etc).
  4. The style of writing Angular 1 components was not very close to that of writing Angular 2+ components.

You can find relevant discussions in #14580 (starting at #14580 (comment)) and #14206.

@thany
Copy link
Author

thany commented Jan 27, 2017

Thanks for explaining. It's clearer to me now.

However,

  1. How hard can it be?
    1. create empty object
    2. pass scope variables to object, hook up exactly as you would to $scope
    3. bind controller function to object
    4. execute
    5. profit :)
  2. Angular 1.x doesn't "do" ES6 classes, does it? (if it does, I'd love to see some example code, since I've been itching to use them proper). Everything's a function anyway with Angular's own dependency injection. That instead of classes, all to be compatible with ES5. That's how I understand it. Only ng2 is supposed to do all kinds of newfangled stuff, even stuff that no browser in the next 5 years will support.
  3. I don't see the problem. What getters/setters? We're talking the scope: { ... } from a directive. There are no getters/setters to be had. There might be a challenge with $watchers, but that is a totally different story, as watchers appear to be completely nonexistent anyway when not using $scope in favour of controllerAs.
  4. Different framework, different code style. That's fine.

@gkalpak
Copy link
Member

gkalpak commented Jan 27, 2017

How hard can it be?

I didn't say it is hard. I said it is more complex than it needs to be. Even the oversimplified version...

var instance = Object.create(Constructor.prototype);
assignProperties(instance);
Constructor.apply(instance, args);

...is more complex than...

var instance = new Constructor(...args)

On top of that, there are other subtleties that need to be taken into account. Very briefly:

  • Classes cannot be called with Constructor.apply(...), so we need (a) to detect class constructors vs constructor functions and (b) have different code path for them (involving new (Function.prototype.bind.apply(Constructor, args))()).
  • Because constructor functions can return the instance of the object, the actual instance maybe different from our original instance, so we need to check for that and re-apply the scope bindings.
  • Other framework-specific stuff.

Angular 1.x doesn't "do" ES6 classes, does it?

It pretty much does (now with bindings pre-asignment disabled, it does it even better) 😃
Why wouldn't it?

(if it does, I'd love to see some example code, since I've been itching to use them proper).

.component('classyComponent', {
  template: `
    <div ng-style="{color: $ctrl.color}">
      I am {{ $ctrl.thatMuch }} classy!
    </div>`,
  bindings: {
    color: '@'
  },
  controller: class ClassComponentController {
    $onInit() {
      this.thatMuch = 'very';
    }
  }
})

Everything's a function anyway with Angular's own dependency injection. That instead of classes, all to be compatible with ES5. That's how I understand it.

Not sure I follow. Anywhere you can use a constructor function, you can use a class. .controller(), .service(), .provider(), .constant(), .value() do support classes. Obviously, .factory(), .filter() and .directive() do not support classes, because they expect a callable function, not a constructor function. (The same is true for any framework or construct that expects a function in order to call it, not instantiate it.)

Only ng2 is supposed to do all kinds of newfangled stuff, even stuff that no browser in the next 5 years will support.

No framework can do funky stuff that the browser does not support on its own. What you can do (and this is independent of frameworks, libraries etc) is use some kind of tool to transpile from a different dialect or language (e.g. ES2015, ES2016, ES2017, TypeScript) to a version of JavaScript that is supported by your target browser (typically ES5 nowadays).

You can do this with Angular (2+), you can do this with AngularJS (1.x), you can do this with no framework at all. Both Angular and AngularJS allow you to write applications in ES5, ES2015 (ES6), TypeScript etc.

I don't see the problem. What getters/setters?

The issues I linked to in my previous comment have relevant discussions.

Different framework, different code style. That's fine.

It may be fine for some people, but it is not fine for others 😃
Unless you or your team plan to stay on AngularJS (1.x.) forever or immediately abandon all existing 1.x apps and start with Angular (2+), then being able to slowly change the way you write 1.x apps to be similar to the way you write 2+ apps is a big deal imo.

@thany
Copy link
Author

thany commented Jan 30, 2017

You make a few fair points. It's okay to have differing opinions about this stuff of course, and I'm not sure we should be using this ticket much further to discuss how ng1 is supposed to work. I trust it's been thought out well.

I did learn a bunch though, so thank you very much for taking the time to explain things to me. I love learning :) I hope I didn't seem too defensive towards you - that's just my rusty view on the world.

@gkalpak
Copy link
Member

gkalpak commented Jan 31, 2017

I hope I didn't come across as too defensive (or offensive) either 😃 I was just trying to give my (personal and not necessarily correct) views on every point you raised. I, too, think differing opinions are great.

In any case, thanks for raising your concerns. It is mostly the users' needs and feedback that drive the evolution of AngularJS.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

2 participants