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>
2
  • Not sure if my answer is what you actually looking for. Maybe you want the <div class="row"> wrap 4 linkGroups instead of only the first of each group of 4. This would need to reorganize my code a bit. Commented Jul 4, 2016 at 6:26
  • The ngif tag is about .Net and not about angular. You could use angular-ng-if. Commented Dec 8, 2016 at 14:23

1 Answer 1

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.

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

5 Comments

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.
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
I updated my answer how the pipe example could look like.
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
Thanks for the hint. pipes in @Component() was removed a while ago. I updated my answer.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.