Nativescript-angular: How to display a two-dimensional array in a GridLayout

Created on 3 May 2016  路  7Comments  路  Source: NativeScript/nativescript-angular

I have a two dimensional array I would like to display in my app. It seems like the GridLayout would be a good choice. I was able to put an ngFor on a Label to set the col, but I'm not sure how to nest another ngFor in order to increment the row. If I were to do this in plain Angular I could use a div just to have something to attach the directive to. Is there something similar in NativeScript? If I were to get this working, another potential issue I see is how to set the rows and columns attribute of GridLayout when dealing with a large or dynamic grid. If my grid were 15 x 15, would I need to have 15 values in the rows and columns attribute? e.g. columns = "auto, auto, auto, auto, auto, auto, auto, auto, auto, ........". Or is there some way to set one value and use it for every column?

I see the grid layout can be created programmatically. Is that the recommended approach for what I'm trying to do? If so, are there any examples of how to do this with NativeScript + Angular? The examples I have seen are only using NativeScript and I'm not sure how the grid layout variable I create would actually get incorporated into the template.

Thanks!

Most helpful comment

Thanks for your solutions. Based on that I've created a Pipe:

import { Pipe, PipeTransform } from '@angular/core';

@Pipe({
    name: 'autogrid'
})
export class AutogridPipe implements PipeTransform {
    transform(length: number, spec: string): any {
        var itemSpec = [];
        for (var i=0; i < length; i++) {
            itemSpec.push(spec);
        }
        return itemSpec.join(",");
    }
}

usage:
<GridLayout columns="auto, *, auto" [rows]="courses.length | autogrid:'auto'">

All 7 comments

You can use the exanded ngFor syntax to nest 2 ngFor-s one inside another and put those inside a <GridLayout>. You would have to use different local variables for the index of the row and the column.
About the row and column - there is no shorthand syntax to define 15 columns. If the row/column count is really dynamic you might have to write some code to add the rows and columns definitions after the component is initialised.
One other approach you can consider is to use WrapLayout with itemWidth and itemHeight properties set to achieve grid-like layout or just use AbsoluteLayout and position elements manually. In both cases you will probably need the sreen size to do some calculations - you can get it from the platform module - its is inside screen.mainScreen.widthDIPs/heightDIPs.

Sounds like a fun thing to play about with... (dynamic rows/cols in the next post)

5x5 grid ... columns="*,*,*,*,*" rows="*,*,*,*,*" but static...

import { Component} from "angular2/core";
import { GridLayout } from "ui/layouts/grid-layout";
import { Observable, Subscription, Subject } from 'rxjs/Rx';
@Component({
  selector: "my-app",
  template: `
    <StackLayout>

      <TextField [(ngModel)]="rowInput" hint="Enter rows"></TextField>
      <TextField [(ngModel)]="colsInput" hint="Enter cols"></TextField>

      <GridLayout columns="*,*,*,*,*" rows="*,*,*,*,*">
          <template ngFor #row [ngForOf]="rows | async" #y="index">
            <template ngFor #col [ngForOf]="cols | async" #x="index"> 
              <Label [row]="row" [col]="col" [text]="x + ',' + y"></Label>
            </template>
          </template>
      </GridLayout>
    </StackLayout>
  `,
  directives: [NgFor]
})
export class AppComponent {

  public rows;
  public cols; 

  //5x5
  public rowInput = 5; 
  public colsInput = 5;



  constructor(){
      this.rows = Observable.range(0, this.rowInput).toArray();
      this.cols = Observable.range(0, this.colsInput).toArray();
  }

  public BuildColumnSpecs(){}
  public BuildRowSpecs(){}

}

image

Sample
Grid template
Directives to automate columns and rows

@Component({
  selector: "my-app",
  template: `
    <StackLayout>
      <TextField [(ngModel)]="colsInput" hint="Enter cols"></TextField>
      <TextField [(ngModel)]="rowInput" hint="Enter rows"></TextField>
      <Button text="Build Grid" (tap)="update()"></Button>
      <!-- columns ="*,*,* ... n" effectively --> 
      <GridLayout [auto-grid-columns]="autoCreateColumns" 
                  [auto-grid-rows]="autoCreateRows">
          <template ngFor #row [ngForOf]="rows" #y="index">
            <template ngFor #col [ngForOf]="cols" #x="index"> 
              <Label [col]="col" [row]="row" [text]="x + ',' + y"></Label>
            </template>
          </template>
      </GridLayout>
    </StackLayout>
  `,
  directives: [AutoGridColumns,AutoGridRows]
})
...

10x10
image

15x7 ... ok ... text wrapping good enough...
image

I have a odd issue with it but i'll open up a seperate issue (updating the same grid with different row or column counts - the build button).

Thank you both! Got it working:

<GridLayout columns="{{itemSpec}}" rows="{{itemSpec}}">
            <template ngFor #row [ngForOf]="puzzle" #y="index">
                <template ngFor #col [ngForOf]="row" #x="index">
                    <Label [col]="x" [row]="y" [text]="col"></Label>
                </template>
            </template>
</GridLayout>

Code to create item spec:

private createItemSpec(length) {
        var itemSpec = [];
        for (var i=0; i<length; i++) {
            itemSpec.push("auto");
        }
        return itemSpec.join(",");
}

Thanks for your solutions. Based on that I've created a Pipe:

import { Pipe, PipeTransform } from '@angular/core';

@Pipe({
    name: 'autogrid'
})
export class AutogridPipe implements PipeTransform {
    transform(length: number, spec: string): any {
        var itemSpec = [];
        for (var i=0; i < length; i++) {
            itemSpec.push(spec);
        }
        return itemSpec.join(",");
    }
}

usage:
<GridLayout columns="auto, *, auto" [rows]="courses.length | autogrid:'auto'">

Cool! Thanks!

sorry for a necro post:
I am now doing something similair and am struggling:

<GridLayout [rows]="rows" [column]s=cols">
    <ng-template *ngFor="let item of array; let i = index">
        <ng-template *ngFor="let detail of item; let j = index">
            <Label [col]="j" [row]="i" [text]="detail.label"></Label>
        </ng-template>
    </ng-template>
</GridLayout>

nothing is shown. When I swap ng-template with StackLayout, it gets rendered (not correctly in columns, but at least something. Any thoughts? Thanks

Was this page helpful?
0 / 5 - 0 ratings