-
Notifications
You must be signed in to change notification settings - Fork 26.2k
Support assigning expression result to local variable within templates #2451
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
Victor added @Directive({
selector: 'assign-local'
exportAs: 'assign',
properties: ['value: assignLocal']
})
class AssignLocal {
value: any;
} <div [assign-local]="people | async" #unwrapped-people="assign.value">
...
</div> |
I was under the impression that change detectors would coalesce multiple reads to the same expressions, so having To be self consistent with the rest of the templates new variables can only be declared on templates hence |
Local variable assignment could be also very useful in a combination with Firebase. I've implemented LVA and it works. Here is the plunk - http://plnkr.co/edit/MWrKGahELHsq2kfu03Gh?p=preview. But it doesn't play nicely with a The question is: how it could be done without wrapping viz. refactor
to
Another point is that when I use
|
I could still access the referenced example here, but was having trouble getting that to work in the latest version (error |
+1 |
@tbosch: could you comment on whether something like that Edit to elaborate; I tried implementing a version of the earlier
In a simple use-case, this just writes expressions to the context -- that is, the component: However, trying this same approach within From what I can see, the context written to in the directive here becomes an Forgive me if storing within loops is deemed out of scope of the current topic -- we closed #6947 because this seemed relevant enough. Anyway, this naive implementation may already cover simpler use-cases. |
I found a need for about the same thing as this issue asks for; I met it by create a variant of "ngFor" and "ngIf" which doesn't add the DOM until the data is available (like IF), but which also capture the value (like FOR). I called it ngWhen, based on the name of a similar feature in some other languages. I got it up and running as part of an early, pre-angularfire2 A2-firebase library I working on. You can see it in action here: https://github.com/OasisDigital/angular2-firebase-demo/blob/master/app/weather/weatherPanel.ts#L17 I wonder if something similar (perhaps with better naming etc) ought to be "in the box". |
Link to that ngWhen implementation. |
Right, I'm not proposing my ngWhen as an answer to all questions... just as an example of one thing someone could do to work around the lack of any obvious built-in way to capture the result of blah|async. |
I agree some kind of variable assignment would be very nice. One work around I have come across is this:
|
@vicb any consideration for a version akin to the |
No, not yet. |
Thanks for your quick response. :) |
Any update on this? |
@vicb Could this be reopened? As it seems mainly to be about a general solution rather than tying it down to ngIf and ngFor etc. It would appear to solve an issue I'm having with binding ngFor to a function result. The only alternative seems to be to create a separate component simply for the sake of storing the value of one expression. It would also be one way to solve this issue I believe: #2449 |
This would be really handy for dealing with async expressions without needing an <div *ngIf="userList | async as users; else loading">
<user-profile *ngFor="let user of users; count as count; index as i" [user]="user">
User {{i}} of {{count}}
</user-profile>
</div>
<ng-template #loading>Loading...</ng-template> What if I need to reference Possible solutions currently available:
In summary, the most obvious option - subscriptions - is also the worst for a number of reasons. The component-wrapper option is a pain to implement, and the I imagine a syntax like the following would be sufficient. I call the directive "ngLet" rather than "assignLocal" as it seems to me more in keeping with Angular template terminology. <div *ngLet="userList | async as users">
<user-profile *ngFor="let user of users; count as count; index as i" [user]="user">
User {{i}} of {{count}}
</user-profile>
</div> |
Yeah, as an aside, I've been using
since I want it to display even when the item is false. I'm working around by using ngElse directives (with the ion-icon fully expanded) instead of the ternary operator. I mean, ultimately I could just throw a ton of | asyncs on the page, but it just feels a bit like ngLet or whatever would be a nice addition. |
Couldn't you do:
??? |
That works for inline stuff, but not when I'm doing something like toggling a value. How would I do
I can either do an ngIf like this:
effectively hardcoding the cu
8000
rrent state into the html element. This works okay, but is kind of brittle - if I need to do other I can instead write some stuff in my controller:
This is in fact what I end up doing. There's some extra cruft in that last bit - I usually use a .takeUntil in my in-class subscriptions, and then in ngOnDestroy() I emit on a But this is still a fair bit of boilerplate. The nice thing about the async pipe is that it handles the boilerplate of subscribes and unsubscribes as appropriate. There does not seem to be any generally-useful workaround here. Maybe if I dig really far into directives I could write some local binding directive somehow, but this feels like it has a lot of parallels with the ng-model stuff from angular1, which I always felt was rather overused and abused. For the time being, this is primarily a quality-of-life thing for me. In most scenarios, *ngIf works okay. If I can change the incoming data (from, say, a value to something like The alternative, something like
would sure be rad, saving a ton of boilerplate. Alternatively, putting pipe template expansion in the parameter list of event handler functions would help, but I feel like that's even harder microsyntax. EDIT to add: Something simple like this
is a start. It's not a complete solution - you've got to manually look up the path (which seems to be an out-of-spec thing someone's adding to the MouseEvent) to find where the value was set, and if there are multiple values set, the inner one overrides the outer, and you can only pass strings around (since it gets stringified). I feel like I'm pushing up against the "hey, this is what we designed ngModel for" frontier here. |
I wrote up a stripped down version of ngIf which I've been successfully using in a large enterprise app. I'm using the "ng" prefix in the example, but obviously don't do that in your app if you decide to use it. import {Directive, Input, TemplateRef, ViewContainerRef} from '@angular/core';
interface LetContext<T> {
ngLet: T;
}
@Directive({
selector: '[ngLet]'
})
export class LetDirective<T> {
private _context: LetContext<T> = {ngLet: null};
constructor(_viewContainer: ViewContainerRef, _templateRef: TemplateRef<LetContext<T>>) {
_viewContainer.createEmbeddedView(_templateRef, this._context);
}
@Input()
set ngLet(value: T) {
this._context.ngLet = value;
}
} <ng-container *ngLet="selectedUsers$ | async as selectedUsers">
<app-user-details [users]="selectedUsers"></app-user-details>
</ng-container> |
@AustinMatherne awesome! consider doing a mini-pull request to Angular core to fix #15280 and see what happens? :D |
This is an awesome idea! Is the feature implemented yet, if so can someone please link to the docs? |
This issue has been automatically locked due to inactivity. Read more about our automatic conversation locking policy. This action has been performed automatically by a bot. |
Particularly for use cases where piped output is bound to in many places within a template, it's beneficial to have a single piped expression. For example, this observable that's piped into a template:
This would cause three subscriptions to be created for the people observable, which may re-do the work of getting the observable value if the observable is not a multicast observable. This is also problematic if inside of the ng-if that evaluates to true when the observable has emitted a value, there are expressions that assume people | async is not null, such as:
{{people | async.length }}
. This would raise an exception since the first value returned from the async pipe would benull
.@vsavkin and I put together a prototype directive, which I'm currently using in the
http
example that supports assigning the result of a piped expression to a local variable within the template:This creates a single subscription and simplifies the rest of the template. Here is the implementation: https://github.com/angular/angular/blob/master/modules/examples/src/http/assign_local_directive.ts
I'd like to add this directive as a first-class directive in the angular2 module.
@vsavkin, @mhevery, @tbosch do you have any feedback on the implementation?
The text was updated successfully, but these errors were encountered: