3

I'm trying to conditionally wrap my bootstrap col-xs-4's three to a row. I have my col-xs-4's rendering fine but i can't figure out how to get them condtionally wrapped in a row. Here is my curren't NgFor:

 <div class="col-xs-4" *ngFor='let linkGroup of linkGroups | siteMapText: siteMapTextFilter; let i=index'>
        <ul class="list-unstyled">
            <li class="heading">
                <div class="clearfix">
                    <span [className]="linkGroup.ContainerCssClass">
                        <span [className]="linkGroup.CssClass"></span>
                    </span>
                    <a href="#">
                        <strong class="heading-text">{{linkGroup.Title}}</strong>
                    </a>
                </div>
            </li>
            <li *ngFor='let link of linkGroup.Links'>
                <a href="#" class="link">
                    <span [className]="link.CssClass"></span>
                    <span class="item">{{link.Title}}</span>
                </a>
            </li>
        </ul>
    </div>

What I would like to do is something like this, but i know its wrong, so what is the right way to do this:

 <div *ngIf='i % 3== 0' class="row">
        <div class="col-xs-4" *ngFor='let linkGroup of linkGroups | siteMapText: siteMapTextFilter; let i=index'>
            <ul class="list-unstyled">
                <li class="heading">
                    <div class="clearfix">
                        <span [className]="linkGroup.ContainerCssClass">
                            <span [className]="linkGroup.CssClass"></span>
                        </span>
                        <a href="#">
                            <strong class="heading-text">{{linkGroup.Title}}</strong>
                        </a>
                    </div>
                </li>
                <li *ngFor='let link of linkGroup.Links'>
                    <a href="#" class="link">
                        <span [className]="link.CssClass"></span>
                        <span class="item">{{link.Title}}</span>
                    </a>
                </li>
            </ul>
        </div>
    </div>

EDIT

The answer was as Gunter suggested, to reorganize the data to be grouped into rows. Then I could organize the template to this:

<div class="row" *ngFor='let linkRow of linkRows| siteMapRowText: siteMapTextFilter'>
    <div class="col-xs-4" *ngFor='let rowCol of linkRow.RowGroups'>
        <ul class="list-unstyled">
            <li class="heading">
                <div class="clearfix">
                    <span [className]="rowCol.ContainerCssClass">
                        <span [className]="rowCol.CssClass"></span>
                    </span>
                    <a href="#">
                        <strong class="heading-text">{{rowCol.Title}}</strong>
                    </a>
                </div>
            </li>
            <li *ngFor='let link of rowCol.Links'>
                <a href="#" class="link">
                    <span [className]="link.CssClass"></span>
                    <span class="item">{{link.Title}}</span>
                </a>
            </li>
        </ul>
    </div>
</div>
Günter Zöchbauer
  • 623,577
  • 216
  • 2,003
  • 1,567
cobolstinks
  • 6,801
  • 16
  • 68
  • 97
  • Not sure if my answer is what you actually looking for. Maybe you want the `
    ` wrap 4 `linkGroup`s instead of only the first of each group of 4. This would need to reorganize my code a bit.
    – Günter Zöchbauer Jul 04 '16 at 06:26
  • The `ngif` tag is about .Net and not about angular. You could use `angular-ng-if`. – 030 Dec 08 '16 at 14:23

1 Answers1

4

Update

You could use a pipe that creates a new array that groups 4 items into an array so you get an array of arrays and then use nested ngFor

@Pipe({ name: 'cols' })
export class ColsPipe implements PipeTransform {
    transform(value: any[], cols: number) {
        var result: any[] = [];
        while(value.length) {
            result.push(value.splice(0, cols));
        }
        return result;
    }
};

See also How to split a long array into smaller arrays, with JavaScript

then use it like

@NgModule({
  declarations: [ColsPipe],
  exports: [ColsPipe]
})
class MySharedModule()
@NgModule({
  imports: [MySharedModule],
  ...
})
class ModuleWhereColsPipeIsUsed {}
@Component({
  selector: '...',
  template: `
  <div class="row" *ngFor='let linkGroupRow of linkGroups | siteMapText: siteMapTextFilter | cols:4; let i=index'>
    <div class="col-xs-4" *ngFor='let linkGroup of linkGroupRow'>...</div>
  </div>
  `
})    

original

This can be achieved using ngTemplateOutlet

At first we create a reusable component:

<template #linkGroupTemplate>
    <div class="col-xs-4">
        <ul class="list-unstyled" let-linkGroup="linkGroup"
                >
            <li class="heading">
                <div class="clearfix">
                    <span [className]="linkGroup.ContainerCssClass">
                        <span [className]="linkGroup.CssClass"></span>
                    </span>
                    <a href="#">
                        <strong class="heading-text">{{linkGroup.Title}}</strong>
                    </a>
                </div>
            </li>
            <li *ngFor='let link of linkGroup.Links'>
                <a href="#" class="link">
                    <span [className]="link.CssClass"></span>
                    <span class="item">{{link.Title}}</span>
                </a>
            </li>
        </ul>
    </div>
</template>

let-linkGroup="linkGroup" declares a context variable linkGroup that refers to the linGroup property of the passed context.

then we use the template inside ngFor and use it wrapped with <div class="row"> on every 4th item, otherwise unwrapped.

<template ngFor let-linkGroup [ngForOf]="linkGroups |siteMapText:siteMapTextFilter" let-i="index">
  <template [ngIf]="i % 3 === 0">
    <div class="row">
     <template [ngTemplateOutlet]="linkGroupTemplate" [ngOutletContext]="{'linkGroup': linkGroup}">
    </div>
  <template>

  <template [ngIf]="i % 3 !== 0">
     <template [ngTemplateOutlet]="linkGroupTemplate" [ngOutletContext]="{'linkGroup': linkGroup}">
  </template>
</template>

<ng-container *ngFor="let linkGroup of linkGroups |siteMapText:siteMapTextFilter" let-i="index">
  <ng-container *ngIf="i % 3 === 0">
    <div class="row">
     <template [ngTemplateOutlet]="linkGroupTemplate" [ngOutletContext]="{'linkGroup': linkGroup}"></template>
    </div>
  <ng-container>

  <ng-container *ngIf="i % 3 !== 0">
     <template [ngTemplateOutlet]="linkGroupTemplate" [ngOutletContext]="{'linkGroup': linkGroup}"></template>
  </ng-container>
</ng-container>

With [ngOutletContext]="{'linkGroup': linkGroup}" we wrap the actual linkGroup value in another object so we can access it as before within the template. Otherwise we would need to declare a variable for each property we want to use

let-CssContainerCssClass="ContainerCssClass" let-CssClass="CssClass" let-Title="Title" let-Links="Links" 

and remove linkGroup. from all bindings.

See also How to repeat a piece of HTML multiple times without ngFor and without another @Component for an example (with Plunker) that uses ngTemplateOutlet and ngOutletContext.

Community
  • 1
  • 1
Günter Zöchbauer
  • 623,577
  • 216
  • 2,003
  • 1,567
  • This is really close thanks for pointing me to routerOutlet. But what its doing is wrapping the first column it is own row then rendering the next two columns outside the row element. I'm trying to get Three columns in side a row div. – cobolstinks Jul 05 '16 at 14:07
  • I see. I got this suspicion already. This is why I posted the comment below your question. You could use a pipe that creates a new array that groups 4 items into an array so you get an array of arrays and then use nested `ngFor` – Günter Zöchbauer Jul 05 '16 at 14:13
  • I updated my answer how the pipe example could look like. – Günter Zöchbauer Jul 05 '16 at 16:33
  • This worked well for me. My version of angular didn't allow "pipes: [ColsPipe]," I had to put ColPipes into my app module's declarations and the pipe was accessible in my template – David Lee Jan 10 '17 at 02:50
  • Thanks for the hint. `pipes` in `@Component()` was removed a while ago. I updated my answer. – Günter Zöchbauer Jan 10 '17 at 06:14