6

I'm pretty new in angular2 and I'm trying to make a small angular component called "grid" that simply rearranges its content using transclusion.

Its template

grid component template (grid.component.ts)

<div class="grid"> <div class="row"> <div class="col-xs-4"> <ng-content select="[grid-item-index=0]"></ng-content> </div> <div class="col-xs-4"> <ng-content select="[grid-item-index=1]"></ng-content> </div> <div class="col-xs-4"> <ng-content select="[grid-item-index=2]"></ng-content> </div> </div> <div class="row"> <div class="col-xs-4"> <ng-content select="[grid-item-index=3]"></ng-content> </div> <div class="col-xs-4"> <ng-content select="[grid-item-index=4]"></ng-content> </div> <div class="col-xs-4"> <ng-content select="[grid-item-index=5]"></ng-content> </div> </div> </div> 

And this is part of the parent component that uses it.

parent template

<grid> <div *ngFor="let item of items; index as i" [attr.grid-item-index]="i"> <span>{{item}}</span> </div> </grid> 

Here's a Plunker.

But the result displays no content. But using...

<grid> <div grid-item-index="0">item 0</div> <div grid-item-index="1">item 1</div> <div grid-item-index="2">item 2</div> <div grid-item-index="3">item 3</div> <div grid-item-index="4">item 4</div> <div grid-item-index="5">item 5</div> </grid> 

it works fine and the result was as I expected.

A Plunker of this last working case.

Can achieve this result using a ngfor or similar.

I've tried using nth-child css pseudo class to avoid using the index but it doesn't work too.

UPDATE

I've made some progress based on @yurzui (Thanks!!) answer. It allows to map content with an grid-item-index value to the view container with the same grid-item-index value.

parent.component.html

<grid> <ng-template *ngFor="let item of items; let i=index" [grid-item-index]="(items.length-1)-i"> <span >{{item}}</span> </ng-template> </grid> 

grid-item-index directive

@Directive({ selector: '[grid-item-index]' }) export class GridItemIndexDirective { @Input('grid-item-index') index: any; constructor(public vcRef: ViewContainerRef, public tRef: TemplateRef) {} } 

grid.component.ts

@ContentChildren(GridItemIndexDirective) sources: QueryList<GridItemIndexDirective>; @ViewChildren(GridItemIndexDirective) containers: QueryList<GridItemIndexDirective>; constructor( private cdRef:ChangeDetectorRef ) {} ngAfterViewInit() { const len = this.sources.length; for (var i = 0; i < len; i++) { const destinationContainer = this.containers.find(x => x.index == i); const source = this.sources.find(x => x.index == i); if (destinationContainer) { destinationContainer.vcRef.createEmbeddedView(source.tRef); this.cdRef.detectChanges(); // this solves ExpressionChangedAfterItHasBeenCheckedError } } } 

Check this Plunker

12

1 Answer 1

3

You can use ngTemplateOutlet to achieve this but there is another approach that uses low-level API:

parent.component.html

<grid> <div *ngFor="let item of items"> <span>{{item}}</span> </div> </grid> 

Additional directive that will help us to recognize destination index.

@Directive({ selector: '[grid-item-index]' }) export class GridItemIndexDirective { @Input('grid-item-index') index: any; constructor(public vcRef: ViewContainerRef) {} } 

grid.component.html

<div class="grid"> <div class="row"> <div class="col-xs-4"> <ng-template grid-item-index="1"></ng-template> </div> <div class="col-xs-4"> <ng-template grid-item-index="2"></ng-template> </div> <div class="col-xs-4"> <ng-template grid-item-index="3"></ng-template> </div> </div> <div class="row"> <div class="col-xs-4"> <ng-template grid-item-index="4"></ng-template> </div> <div class="col-xs-4"> <ng-template grid-item-index="5"></ng-template> </div> <div class="col-xs-4"> <ng-template grid-item-index="6"></ng-template> </div> </div> </div> 

grid.component.ts

@ContentChild(TemplateRef, { read: ViewContainerRef }) vcRef: ViewContainerRef; @ViewChildren(GridItemIndexDirective) containers: QueryList<GridItemIndexDirective>; ngAfterViewInit() { const len = this.vcRef.length; for( var i = 1; i <= len; i++) { const destinationContainer = this.containers.find(x => x.index == i); if(destinationContainer) { const view = this.vcRef.detach(0); destinationContainer.vcRef.insert(view); } } } 

Plunker Example

Sign up to request clarification or add additional context in comments.

10 Comments

Would it be possible to add the directive (item-grid-index) to the content in order to map its content to the same item-grid-index in the view? This way you can alter the order they will be added and even discard content not having the directive.
Do you mean like in your origin question <div *ngFor="let item of items; index as i" [attr.grid-item-index]="i">?
You might also be interested in this link stackoverflow.com/questions/44595942/… He is building table as well.
Yes, that was what i mean. Matching the content item with a grid-item-index value to the view item with the same grid-item-index value. I'll take a look to the question mentioned. Thanks.
|

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.