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

Idea $this in directive locals calls #13247

Closed
jpolo opened this issue Nov 4, 2015 · 16 comments
Closed

Idea $this in directive locals calls #13247

jpolo opened this issue Nov 4, 2015 · 16 comments

Comments

@jpolo
Copy link

jpolo commented Nov 4, 2015

It would be nice to have a normalized reference to the locals for directive expression.
I've proposed $this but other candidates could be better ($locals, etc...)

Example

angular.directive("fooBar", function () {

   return {
     scope: {
       ngCallback:"&"
     },
     compile: function ($scope) {
       var ctx = {
       foo: "bar"
       };
       ctx.$this = ctx;// Keep a reference to the locals

       $scope.ngCallback(ctx);
     }
   };
})

Allows to easily dump available locals

<foo-bar ng-callback="log($this)">
@Narretz
Copy link
Contributor

Narretz commented Nov 6, 2015

Can you give a more complete example? I've a hard time figuring out the use case.

@jpolo
Copy link
Author

jpolo commented Nov 6, 2015

The problem when passing just locals to the context is that there is no way to inspect all locals at once.
If log l is just a simple forward to console.log then log($this) would dump something like

{
  foo: "bar"
}

That helps a lot when using debugging/using light documented directives.

@petebacondarwin
Copy link
Contributor

I think I see what you mean.

You want to have access to a single property on the scope that references the locals that were passed to through to the expression.
I.E. when we execute an expression we can provide some locals that are merged with the scope:

var someExpressionFn = $parse('someExpression');
someExpressionFn($scope, locals);

You would like us to automatically attach an additional property to the locals object that refers to the locals object itself.

@jpolo
Copy link
Author

jpolo commented Nov 18, 2015

Exactly sir! Sorry if I wasn't clear before...

@petebacondarwin
Copy link
Contributor

I see this as a rare requirement that can be easily implemented on a case by case basis as you show in your example @jpolo. I don't think it is something that we would want to add to all cases, especially as we would have to pollute the scope with yet another special property.

@petebacondarwin
Copy link
Contributor

In general, if I have a load of things that I want to pass via locals that can be handled as a single thing by the client then I would set it up so:

$scope.ngCallback({ $event: ctx });

This (interestingly) is similar to the form that seems to be appearing in Angular 2 event handling.

@jpolo
Copy link
Author

jpolo commented Nov 18, 2015

In my user need, I am the developer that uses a third party directive fooBar.
In your example, there is no programmatic way to know that the context local is named "$event" because I didn't write this code. My only solution is to read either the code or the doc if present but that is not always the case...

@petebacondarwin
Copy link
Contributor

Sure, but the third party should document their directive, no?

@petebacondarwin
Copy link
Contributor

Just to be clear, here is a plnkr: http://plnkr.co/edit/Hm0nWaybsJ8PxKsVQzIl?p=preview

@petebacondarwin
Copy link
Contributor

What you are saying is that there is no programmatic way of enumerating the {a: 1, b: 2, c: 3} data.

@petebacondarwin
Copy link
Contributor

I wonder if what we should say is that inside such an expression the this should refer to the merged "scope+locals" rather than just the scope? @lgalfaso what do you think?

@jpolo
Copy link
Author

jpolo commented Nov 18, 2015

You're right everybody should document. But sometimes it is not the case or if there is an error between doc and code.
About the plunkr, that's what you described : there is no programmatic way of enumerating the {a: 1, b: 2, c: 3} data. (At the moment this = $scope, which is pretty logic).
Maybe the best name would be $local to avoid any confusion with the real javascript this. I've updated the plunkr accordingly.

@lgalfaso
Copy link
Contributor

@petebacondarwin I do not like the idea to merge scope and local when running an expression and it would have performance implications and there would be no way to actually forcing something to be loaded from the scope (as locals has priority when this is not specified). I lean towards #13247 (comment) idea of creating a $local special symbol that would always point to locals. There will be two small breaking changes, these being:

  • if scope.$local exists, the way to reference will change to this.$local
  • if locals has a property $local then the way to reference will change to $local.$local

Anyhow, I find these breaking changes to be small and the benefits reasonable

lgalfaso added a commit to lgalfaso/angular.js that referenced this issue Dec 6, 2015
Extends the built-in identifiers definitions by adding `$local`. This is a
non-assignable reference to the locals object.

Closes: angular#13247
lgalfaso added a commit to lgalfaso/angular.js that referenced this issue Dec 7, 2015
Extends the built-in identifiers definitions by adding `$local`. This is a
non-assignable reference to the locals object.

Closes: angular#13247
lgalfaso added a commit to lgalfaso/angular.js that referenced this issue Dec 7, 2015
Extends the built-in identifiers definitions by adding `$local`. This is a
non-assignable reference to the locals object.

Closes: angular#13247
@petebacondarwin
Copy link
Contributor

@lgalfaso can we make sure that the breaking change description in this PR gets into the changelog as it didn't make it into the commit.

@zakdances
Copy link

After reading this thread, the migration guide, and several SO posts, I still have no clue what $locals is. It's always undefined for me. The docs are really opaque about it. For example,

Due to 0ea5350, a new special property, $locals, will be available for accessing the locals from an expression.

What is "locals from an expression"? What expression is this referring to?

This is a breaking change, only if a $locals property does already exist (and needs to be referenced) either on the scope or on the locals object.

What is $locals a property of? What is the "locals object"? Are they different? Are they the same? Am I supposed to be creating this stuff myself somewhere? Why would I want a "locals object" to reference "$locals"?

I'm using Angular 1.5.3 and everywhere I've tried to reference "$locals" or "locals", it NEVER exists. Can someone offer an alternate explanation of this...thing called $locals because I feel like I'm going loco.

@petebacondarwin
Copy link
Contributor

When you evaluate an expression on a scope, you can add in an additional object containing "locals", which are merged into the context (i.e. the scope) for the evaluation of that expression.

var $scope = {};
var fn = $parse('a + b');
expect(fn($scope)).toEqual(undefined);
expect(fn($scope, {a: 1, b: 2}).toEqual(3);

This PR should also add the $locals property to the context, which allows direct access to these locals:

var $scope = {};
var fn = $parse('$locals');
expect(fn($scope, {a: 1, b: 2}).toEqual({a: 1, b: 2});

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

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

5 participants