Ionic 2 Grid


#1

Hi all,

I am trying to create a grid of items, with 3 columns and as many rows as needed. I have a list of items to iterate through as you can see below, but instead of repeating the same item on each row for all 3 columns, how can I display the next item?

Thank you

  <ion-list>
    <ion-item *ngFor="let item of items">
      <ion-row>
        <ion-col width-33>
          <p>{{item.name}}</p>
        </ion-col>
        <ion-col width-33>
          <p>{{item.name}}</p>
        </ion-col>
        <ion-col width-33>
          <p>{{item.name}}</p>
        </ion-col>
      </ion-row>
    </ion-item>
  </ion-list>

For example:

| item1 | item2 | item3 |
| item4 | item5 | item6 |
| item7 |

p.s. I don’t know how big the list of items will be (i.e. it is dynamic).


#2

This SO question looks exactly like your situation.


#3

Thanks rapropos, that looks exactly what I am looking for too.

Except, how do you fix the typscript errors for?

this.rows = Array.from(Array(Math.ceil(this.subCategoryModels.length / 3)).keys());

errors:

[ts] 
Property 'from' does not exist on type 'ArrayConstructor'.
any

and

[ts] 
Property 'keys' does not exist on type 'any[]'.
any

Is Array not an ArrayConstructor?


#4

That sounds like an ES5/ES6 problem. I get no typescript errors dropping that line into a stock Ionic scratch project.


#5

Yes, I think it is an ES6 issue.

I try the following, which gives no errors:

this.rows = Object.keys(Array(Math.ceil(this.subCategoryModels.length / 3)));

However, this.rows is empty, even though subCategoryModels.length = 3.

Looks like my javascript is incorrect. What should this.rows contain?


#6
public users = [
    { name: 'Jilles', age: 21 },
    { name: 'Todd', age: 24 },
    { name: 'Lisa', age: 18 }
  ];

let rows = Array.from(Array(Math.ceil(this.users.length / 2)).keys());

> rows;
[0, 1]

#7

rapropos, are you using Ionic 2? Does that mean you have ES6? I am using Ionic 2, and would have thought it used ES 6.


#8

Yes, that’s an Ionic 2 project using the standard beta 10 browserify build. Normally I use webpack, but for testing stuff like this I just have a scratch tabs project. I do see typings for es6-shim; that may be what is making Array.from work. I don’t recall adding them manually, so I expect the project generator did that.


#9

I can get the following to work with ES5. It is not as as elegant, but will use it for now until I can find a better solution.

for (var index = 0; index < Math.ceil(this.subCategoryModels.length / 3); index++) {
this.rows.push(index);
}


#10

Hi All,

Angular’s ngFor is not the same as JavaScript’s for, and when it’s treated as a simple iterator, code will get m-e-s-s-y!

Try this code instead.

HTML code

<ion-grid>
    <ion-row *ngFor="let trio of getTriples()">
        <ion-col *ngFor="let item of trio">
            <ion-item>{{ item }}</ion-item>
        </ion-col>
    </ion-row>
</ion-grid>

TS code

export class HomePage {
myList: any[];

constructor(private navCtrl: NavController) {
    this.myList = [];

    for (let i=0; i<20; i++) {
        this.myList.push(i);
    }
}

getTriples() {
    let triples = [];
    let length = this.myList.length;
    for (let i = 0; i < length; i += 3) {
        let trio = [];
        trio.push(this.myList[i]);
        if (i + 1 < length) {
            trio.push(this.myList[i + 1]);
        }
        if (i + 2 < length) {
            trio.push(this.myList[i + 2]);
        }

        triples.push(trio);
    }
    return triples;
}

}

Here are some benefits of coding like this:

  1. this maintains a clear and necessary break between code for the layout and code for the app logic
  2. this is worlds easier to understand with very little extra memory / run-time (Will you remember what you meant to do with that math statement when you glance at it 2 months from now?)
  3. easier to edit & change in the future
  4. unit testing is not as easy in the coding style of the SO link above
  5. you do not need to build messy logic into the HTML file
  6. you do not need to change the internal structure of your list of items in order to achieve the layout you’re looking for

I hope this helps!

Ryan


#11

Can anyone please tell me how to fix this?

When I run this code, how can I pass item to the function:

  <ion-row *ngFor="let i of rows">
    <ion-col *ngFor="let item of categoryModels | slice:(i*3):(i+1)*3" (click)="itemTapped(item)" width-33>
      <div class="row responsive-md">
        <img src="data:image/png;base64,{{item.icon}}" height="75" width="75" />
        <p>{{item.name}}</p>
      </div>
    </ion-col>
  </ion-row>

and

  itemTapped(event: Event, categoryModel: CategoryModel) {
console.log('itemTapped: '+categoryModel);    
    this.nav.push(SubCategoryPage, {
      categoryModel: categoryModel
    });
  }

outputs:

itemTapped: undefined

I also try the following with the same result:

<img src="data:image/png;base64,{{item.icon}}" height="75" width="75" (click)="itemTapped(item)"/>

I also try with ryanslogsdon’s example with the same result:

  <ion-row *ngFor="let trio of getTriples()">
    <ion-col *ngFor="let item of trio">
      <div class="row responsive-md">
        {{ formatCategories(item) }}
        <img src="data:image/png;base64,{{item.icon}}" height="75" width="75" (click)="itemTapped(item)" />
        <p>{{item.name}}</p>
      </div>
    </ion-col>
  </ion-row>

#12

SOLVED: silly mistake on my part. itemTapped signiture was incorrect

  itemTapped(categoryModel: CategoryModel) {
    console.log('itemTapped: ' + categoryModel);
    this.nav.push(SubCategoryPage, {
      categoryModel: categoryModel
    });
  }

#13

Thanks Ryan! It works! But i need a little edit in

      <ion-col style="width:33%">