Components: Allow usage of attribute selectors and `<ng-container>` for Angular 2 Material Components

Created on 18 Mar 2017  路  9Comments  路  Source: angular/components

Bug, feature request, or proposal:

proposal

What is the expected behavior?

I am using the Angular2 Material grid list component and I want to split the list and the item in separate components.

Here's my list:

@Component({
  selector: 'app-list',
  template: `
    <md-grid-list cols="12">
        <app-item *ngFor="let item of items"></app-item>
    </md-grid-list>
  `
})
export class ListComponent {
  items: Array<number> = [1, 2]; // dummy data
}

Here is my item component:

@Component({
  selector: 'app-item',
  template: `
    <md-grid-tile [colspan]="6">
      First
    </md-grid-tile>
    <md-grid-tile [colspan]="6">
      Second
    </md-grid-tile>
  `
})
export class ItemComponent implements OnInit { }

The issue is that the child item component gets rendered in the actual DOM inside a wrapper <app-item> custom (invalid) DOM element. And the styles are broken because the Angular2 Material grid list component expects the following structure:

<md-grid-list cols="12">
  <!-- Item 1 -->
  <md-grid-tile [colspan]="6"></md-grid-tile>
  <md-grid-tile [colspan]="6"></md-grid-tile>

  <!-- Item 2 -->
  <md-grid-tile [colspan]="6"></md-grid-tile>
  <md-grid-tile [colspan]="6"></md-grid-tile>
</md-grid-list>

... but the actual DOM structure is:

<md-grid-list cols="12">
  <!-- Item 1 -->
  <app-item> <!-- same issue if I replace this with `div` or `span` -->
    <md-grid-tile [colspan]="6"></md-grid-tile>
    <md-grid-tile [colspan]="6"></md-grid-tile>
  </app-item>

  <!-- Item 2 -->
  <app-item>
    <md-grid-tile [colspan]="6"></md-grid-tile>
    <md-grid-tile [colspan]="6"></md-grid-tile>
  </app-item>
</md-grid-list>

I expected to solve this issue by using attribute selectors and <ng-container>, so my list looks like this:

@Component({
  selector: 'app-list',
  template: `
    <md-grid-list cols="12">
        <ng-container appItem *ngFor="let item of items"></ng-container>
    </md-grid-list>
  `
})
export class ListComponent {
  items: Array<number> = [1, 2]; // dummy data
}

and then, my item:

@Component({
  selector: '[appItem]',
  template: `
    <md-grid-tile [colspan]="6">
      First
    </md-grid-tile>
    <md-grid-tile [colspan]="6">
      Second
    </md-grid-tile>
  `
})
export class ItemComponent implements OnInit { }

What is the current behavior?

I am getting the following error:

Failed to execute 'appendChild' on 'Node': This node type does not support this method.

What are the steps to reproduce?

Plunker: http://plnkr.co/edit/21QTWw

What is the use-case or motivation for changing an existing behavior?

In real case (project) scenario - splitting list and listItem it a common practice. I don't see how I could split them in the current set-up.

Which versions of Angular, Material, OS, browsers are affected?

Angular 2.4, Material 2.0.0-beta.1, tested on Chrome 57 on MacOS Sierra.

Most helpful comment

I don't understand why this issue was closed: actually it is currently not possible to use an attribute selector to create a component on an <ng-container>... Or is there another way to create a component that completely replace the tag that is linked with its selector with the content of its template?
This SO question exactly reflect what I would want but the current solution is not really a clean one...

All 9 comments

same here , any solution ?

@alaa-alshamy I haven't found a workaround yet.

This isn't really how Angular works; you can view the grid-list as a single UI component with a specific API. Placing the grid tiles inside of another component hides them completely from the grid list since each component is effectively a black box.

@jelbourn thank you for clarifying this!

Today I actually needed a component attribute selector on a ng-container and... nope.

I don't understand why this issue was closed: actually it is currently not possible to use an attribute selector to create a component on an <ng-container>... Or is there another way to create a component that completely replace the tag that is linked with its selector with the content of its template?
This SO question exactly reflect what I would want but the current solution is not really a clean one...

Stumbled upon this problem as well, can't find a solution...

same here. The recommended best practice is to make black boxes of components - in my case I want to make a card within a card collection into a component.

Unfortunately the css lib I use has this syntax

.ui.cards > .card {
   display: -webkit-box;
   display: -ms-flexbox;
   display: flex;
   margin: 0.875em 0.5em;
   float: none;
 }

which means that the css is now longer applied.

I _can_ move the internal <div class="card"> into the host and use an attribute selector on the div

<div class="card" my-card> but then the div and class for the card is now defined outside the component which feels wrong

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._

Was this page helpful?
0 / 5 - 0 ratings

Related issues

constantinlucian picture constantinlucian  路  3Comments

julianobrasil picture julianobrasil  路  3Comments

Hiblton picture Hiblton  路  3Comments

vanor89 picture vanor89  路  3Comments

theunreal picture theunreal  路  3Comments