8000 feat(common): ngLet directive by ibarsi · Pull Request #25472 · angular/angular · GitHub
[go: up one dir, main page]

Skip to content

feat(common): ngLet directive #25472

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

Closed
wants to merge 6 commits into from
Closed

feat(common): ngLet directive #25472

wants to merge 6 commits into from

Conversation

ibarsi
Copy link
@ibarsi ibarsi commented Aug 14, 2018

ngLet exposes the result of an expression that is stored as a variable to its child view.

Fixes #15280

PR Checklist

Please check if your PR fulfills the following requirements:

PR Type

What kind of change does this PR introduce?

[ ] Bugfix
[x] Feature
[ ] Code style update (formatting, local variables)
[ ] Refactoring (no functional changes, no api changes)
[ ] Build related changes
[ ] CI related changes
[ ] Documentation content changes
[ ] angular.io application / infrastructure changes
[ ] Other... Please describe:

What is the current behavior?

Issue Number: #15280

What is the new behavior?

ngLet evaluates the expression and, if stored in a variable, provides the result to its child view.

Storing falsy result in a variable

ngIf supports the common use case of storing a value in a variable and exposing it to its child view.

This works well for most cases. However, if the value stored is falsy (ie. false, 0, '', etc) the child view will not be rendered. This can be problematic if the stored value is required to be displayed, or provided as an argument to an output or DOM event.

ngLet works around this issue by always rendering the child view, regardless of the result of its expression.

Semantic meaning

Even in the cases where ngIf works, some semantic meaning is lost. Reading the code doesn't communicate whether the auther intended contitional rendering or simply needed to expose the result of the expression to its child view.

ngLet explicitly communicates semantic meaning, illustrating that the intent of the author's code was to expose the result of the expression, not conditional rendering.

Syntax

<div *ngLet="condition as value">{{value}}</div>

Does this PR introduce a breaking change?

[ ] Yes
[x] No

Other information

N/A

`ngLet` expose the result of an expression that is stored as a variable
to its child view.

Fixes #15280
@ibarsi ibarsi changed the title feat(common): Implemented ng-let directive. feat(common): ngLet directive Aug 14, 2018
@sebek64
Copy link
sebek64 commented Aug 20, 2018

This feature is tightly related to #25251, which is in some sense more general, but maybe a bit less readable. For me (as the author of the other pull request), both solutions are quite fine. The fact that these pull requests emerge indicates that this is a real deficiency that should be addressed somehow, by merging either one (or by proposing a completely different solution).

Copy link
@sandangel sandangel left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you should add more tests for some use case like nested *ngLet, usage with async pipe...

}

@Input()
set ngLet(condition: any) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we name it condition?
we can absolutely use something like *ngLet="data$ | async as data" here.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Absolutely! It's not expected to be a "condition" at all. Thanks for pointing that out. Maybe value or data is more appropriate?

set ngLet(condition: any) {
this._context.$implicit = this._context.ngLet = condition;
this._viewContainer.clear();
this._viewContainer.createEmbeddedView(this._templateRef, this._context);
Copy link
@sandangel sandangel Aug 20, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why you put createEmbeddedView here? Is that mean we will createEmbeddedView everytime input change?

I think we just need to createEmbeddedView once in the ngOnInit hook and only changes the value of property in the context object when Input changes.

  @Input()
  set ngLet(value: any) {
    this._context.$implicit = this._context.ngLet = value;
  }

  constructor(private _vcr: ViewContainerRef, private _templateRef: TemplateRef<NgLetContext>) {}

  ngOnInit() {
    this._vcr.createEmbeddedView(this._templateRef, this._context);
  }

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is an excellent point... I didn't even consider that updating context on input changes would be enough 🤔

This makes a lot of sense, will update 👍🏼

});

it('should support binding to variable using let', async(() => {
const template = '<span *ngLet="booleanCondition let v">{{v}}</span>';
Copy link
@sandangel sandangel Aug 20, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct me if I'm wrong but I think the syntax should be *ngLet="booleanCondition; let v" (notice the ;)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's good either way, the semicolon isn't required 🙂

@ibarsi
Copy link
Author
ibarsi commented Aug 21, 2018

@sandangel Updated tests to better assert the functionality we're expecting from this directive, namely aliasing and displaying the result of our expression.

Let me know what you think 😄

@sandangel
Copy link
sandangel commented Aug 21, 2018

Hi @ibarsi , I think you should have a look at *ngIf directive test cases. some tests I think you should have are binding in ng-template, nested *ngLet, update multiple nodes...

});
});

it('should support binding to observable using as', async(() => {
Copy link
Contributor
@jotatoledo jotatoledo Aug 21, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Dont use EventEmitter for the purpose of an Observable instance. Use a Subject or similar.

@kara kara added the area: common Issues related to APIs in the @angular/common package label Oct 11, 2018
@IgorMinar
Copy link
Contributor

hi there. thanks for the PR. I've posted on #15280 (comment) about how we should go about implementing this feature. The implementation in this PR can't be accepted as it would (eventually) result in perf issues. Thanks for exploring this problem space. Your work will inform future implementations.

@IgorMinar IgorMinar closed this Oct 17, 2018
@angular-automatic-lock-bot
Copy link

This issue has been automatically locked due to inactivity.
Please file a new issue if you are encountering a similar or related problem.

Read more about our automatic conversation locking policy.

This action has been performed automatically by a bot.

@angular-automatic-lock-bot angular-automatic-lock-bot bot locked and limited conversation to collaborators Sep 14, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area: common Issues related to APIs in the @angular/common package cla: yes
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Template Local Variables
7 participants
0