Skip to content
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

[Feature] @ContentChildren() option to traverse just ng-container and ng-template #24858

Open
IlCallo opened this issue Jul 12, 2018 · 15 comments
Assignees
Labels
area: core Issues related to the framework runtime core: queries feature: insufficient votes Label to add when the not a sufficient number of votes or comments from unique authors feature: votes required Feature request which is currently still in the voting phase feature Issue that requests a new feature
Milestone

Comments

@IlCallo
Copy link

IlCallo commented Jul 12, 2018

I'm submitting a...


[ ] Regression (a behavior that used to work and stopped working in a new release)
[ ] Bug report  
[ ] Performance issue
[x] Feature request
[ ] Documentation issue or request
[ ] Support request => Please do not submit support request here, instead see https://github.com/angular/angular/blob/master/CONTRIBUTING.md#question
[ ] Other... Please describe:

Current behavior

@ContentChild() matches only direct descendants or all descendands with the {descendants: true} option

Expected behavior

Another option which allow to query direct descendands together with all and only descendands inside ng-template or ng-container tags.
Or some kind of reference/option which, put on nested elements, would make them discoverable by @ContentChildren even if nested.
The second could be preferrable because the decision is left to who is writing the template and not to who wrote the component.

What is the motivation / use case for changing the behavior?

See issue with matPrefix for mat-form-field in angular/material2.

In some cases being able to manually flag ng-container and ng-template as "traversable" could easily prevent the creation of a new component or the duplication of all common code which can be a lot.
In the provided use case, only the input element had to change while all other mat-form-field pieces are always the same: here's an abstract of the use case with more context that the stackblitz provided into the linked issue.

<mat-form-field *ngFor="let fields of fields">
    <mat-label>{{ field.name }}</mat-label>

    <input *ngIf="field.status == 'disabled'; else switchConditions" matInput disabled>

    <ng-template #switchConditions>
        <ng-container [ngSwitch]="field.type">
            <ng-container *ngSwitchCase="'date'">
                <!-- matPrefix here won't work because @ContentChildren do not traverse ng-template and ng.container -->
                <mat-datepicker-toggle matPrefix [for]="datePicker"></mat-datepicker-toggle>
                <input matInput [(ngModel)]="field.value" [matDatepicker]="datePicker">
                <mat-datepicker #datePicker></mat-datepicker>
            </ng-container>

            <ng-container *ngSwitchCase="'select'">
                <mat-select [(ngModel)]="field.value">
                    <mat-option *ngFor="let option of field.options" [value]="option"> {{ option }} </mat-option>
                </mat-select>
            </ng-container>

            <!-- type text or number -->
            <input *ngSwitchDefault matInput [(ngModel)]="field.value">
        </ng-container>
    </ng-template>

    <button mat-icon-button matSuffix (click)="/* remove field */"><mat-icon>cancel</mat-icon></button>
</mat-form-field>

Environment


Angular version: 6.0.9

Related

#20810 and many others

@mhevery mhevery added the area: core Issues related to the framework runtime label Jul 12, 2018
@ngbot ngbot bot added this to the needsTriage milestone Jul 12, 2018
@IlCallo
Copy link
Author

IlCallo commented Jul 20, 2018

Another use case, still related to Angular Material, is dinamically displaying errors as seen here

In this case it's possible to apply a workaround to make it right (using an empty mat-error element for *ngFor), but I guess it could create problems in future and it's not replicable with other problems.

@mhevery mhevery added the feature Issue that requests a new feature label Jul 31, 2018
@ngbot ngbot bot modified the milestones: needsTriage, Backlog Jul 31, 2018
@kmjungersen
Copy link

@IlCallo I'm experiencing this issue as well, for the exact same use case (a data-driven form with a material date picker). Have you found a workaround?

@IlCallo
Copy link
Author

IlCallo commented Oct 3, 2018

See the issue from which this one has spawned, which is linked in the first message, for the workaround I used: angular/components#12060 (comment)
It's nothing more than always adding the mat-picker-toggle and mat-datepicker and hiding them when not really needed.

I know it's an horrible workaround.
The "fixed by Ivy" label on this issue brings a sparkle of hope in my hearth ❤️

@kmjungersen
Copy link

@IlCallo awesome - thank you! Somehow I missed your reply until now, but I ended up doing something similar to what you suggested. Hacky, but functional! 😃

@etay2000
Copy link

This is still blocking the ability to create custom material components containing a matPrefix/matSuffix. Not sure what the point of adding the "Fixed by Ivy" label was. It certainly isn't fixed, this issue has been open and lingering since July. I realize there are roughly 2300 open issues, so perhaps I need to adjust my expectations accordingly, but on average how long do issues with the "Fixed by Ivy" tag usually take to actually get resolved?

@IlCallo
Copy link
Author

IlCallo commented Feb 14, 2019

It means they will start looking at this issue (and all others with the same label) when ivy will be rolled out as default. Which is in Angular 9, probably this fall. Don't expect a fix before 2020 🙃

@etay2000
Copy link

Thanks for the heads up. I think Angular is awesome and I'm glad for their constant innovation, but this is all the motivation I needed to finally check out React.

@kylewhitaker
Copy link

This is a good feature request. I stumbled across this limitation in my own development. I naturally expected <ng-container> and <ng-template> to work with <ng-content select="...">, and I think it should.

There is a workaround, but the documentation is limited or nonexistent. ngProjectAs can be used to disguise <ng-container> or <ng-template> elements to match content projection with specific selections.

Child component with select content projection

<ng-content select="[jabroni]">

Parent component

<app-child>
  <div jabroni>This will display!</div>
  <ng-container>
    <div jabroni>This will NOT display!</div>
  <ng-container>
  <ng-container *ngProjectAs="[jabroni]">
    <div jabroni>This will display!</div>
  <ng-container>
</app-child>

However, ngProjectAs virtually nullifies the protection offered by select="..." of ng-content since any element can be disguised to satisfy the selection restriction. See ng-content: The hidden docs for more info on ngProjectAs.

@dannymcgee
Copy link

Does the "Fixed by Ivy" tag here indicate that this issue is fixed with the Ivy renderer, or will be fixed?

My use case is that I'm developing a component library for internal use in all of our Angular applications. We have a button that can serve as a dropdown trigger, with an API like this:

<lib-button
  icon="SomeIconName"
  libDropdownTrigger
>
  Button label
  <lib-dropdown>
    <lib-menu>
      <lib-button>Menu Item label</lib-button>
      <lib-button>Menu Item label</lib-button>
      <lib-button>Menu Item label</lib-button>
    </lib-menu>
  </lib-dropdown>
</lib-button>

In the MenuComponent, I'm using CDK's FocusKeyManager to handle accessible keyboard navigation by querying for ContentChildren(ButtonComponent) and passing the keyboard events from those buttons to the FocusKeyManager.

I'm now trying to create a split button with an API kind of like this:

<lib-split-button>
  Main button label
  <lib-split-button-menu icon="ChevronDown">
    <lib-button>Menu Item label</lib-button>
    <lib-button>Menu Item label</lib-button>
    <lib-button>Menu Item label</lib-button>
  </lib-split-button-menu>
</lib-split-button>

The SplitButtonMenuComponent's template looks kind of like this:

<lib-button
  [icon]="icon"
  libDropdownTrigger
>
  <lib-dropdown>
    <lib-menu>
      <ng-content></ng-content>
    </lib-menu>
  </lib-dropdown>
</lib-button>

It's just an abstraction to give consumers a nicer API for building these split buttons. The problem is that this abstraction component takes an ng-content and passes it to MenuComponent's ng-content, which is doing the actual ContentChildren query. Because ContentChildren doesn't query deeply/recursively, this does not work.

I'd be just as happy using an ng-template in place of the SplitButtonMenuComponent abstraction, but right now literally nothing will allow me to handle keyboard navigation according to ARIA specs because I can't build a QueryList of those buttons. Dropping accessibility (or making the split button's API so verbose that it's pointless) is not really an acceptable option for me in the long-term, but we are planning to upgrade to Angular 9 once it's released, so that is a solution I can deal with.

@etay2000
Copy link

I think the "Fixed by Ivy" label just means that now they can ignore this issue indefinitely.

@literalpie
Copy link
Contributor

I would love to see this. The current behavior of ignoring anything inside a ng-container means that all components that want to support flexible situations (with ngIf, ngFor, etc.) need to add the descendants: true option pretty much everywhere. Notice that in the angular/components library, most instances of ContentChildren have the value set as true.

@literalpie
Copy link
Contributor

Sorry, I was conflating this issue with this other one

@amitbeck
Copy link

amitbeck commented Mar 16, 2021

Was this fixed? Or am I doing something wrong?
I'm trying to do something like this:

<app-child>
  <ng-container ngProjectAs="content" *ngTemplateOutlet="someTemplate">
  </ng-container>
</app-child>

And in app-child.component.ts:

@ContentChild('content') contentChild: any;

ngAfterContentInit(): void {
  console.log(this.contentChild); // prints `undefined`
}

I also tried @ContentChild('content', { read: ElementRef }) contentChild: ElementRef but to no avail.

@angular-robot angular-robot bot added the feature: votes required Feature request which is currently still in the voting phase label Jun 4, 2021
@angular-robot
Copy link
Contributor

angular-robot bot commented Jun 4, 2021

Just a heads up that we kicked off a community voting process for your feature request. There are 20 days until the voting process ends.

Find more details about Angular's feature request process in our documentation.

@angular-robot
Copy link
Contributor

angular-robot bot commented Jun 24, 2021

Thank you for submitting your feature request! Looks like during the polling process it didn't collect a sufficient number of votes to move to the next stage.

We want to keep Angular rich and ergonomic and at the same time be mindful about its scope and learning journey. If you think your request could live outside Angular's scope, we'd encourage you to collaborate with the community on publishing it as an open source package.

You can find more details about the feature request process in our documentation.

@angular-robot angular-robot bot added the feature: insufficient votes Label to add when the not a sufficient number of votes or comments from unique authors label Jun 24, 2021
@pkozlowski-opensource pkozlowski-opensource self-assigned this Apr 14, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area: core Issues related to the framework runtime core: queries feature: insufficient votes Label to add when the not a sufficient number of votes or comments from unique authors feature: votes required Feature request which is currently still in the voting phase feature Issue that requests a new feature
Projects
None yet
Development

No branches or pull requests

9 participants