Angular 2 - Ng Para usar números en lugar de colecciones


191

...por ejemplo...

<div class="month" *ngFor="#item of myCollection; #i = index">
...
</div>

Es posible hacer algo como ...

<div class="month" *ngFor="#item of 10; #i = index">
...
</div>

... sin apelar a una solución no elegante como:

<div class="month" *ngFor="#item of ['dummy','dummy','dummy','dummy','dummy',
'dummy','dummy','dummy']; #i = index">
...
</div>

?


8
Tengo el mismo problema. Realmente molesto, uno no puede hacer cosas tan simples con angular 2.
Albania

1
Quizás esto pueda ser útil: stackoverflow.com/questions/3895478/…
Pizzicato

Respuestas:


198

Dentro de su componente, puede definir una matriz de números (ES6) como se describe a continuación:

export class SampleComponent {
  constructor() {
    this.numbers = Array(5).fill().map((x,i)=>i); // [0,1,2,3,4]
    this.numbers = Array(5).fill(4); // [4,4,4,4,4]
  }
}

Vea este enlace para la creación de la matriz: es la mejor manera de crear una matriz de enteros de 1..20 en JavaScript .

Luego puede iterar sobre esta matriz con ngFor:

@Component({
  template: `
    <ul>
      <li *ngFor="let number of numbers">{{number}}</li>
    </ul>
  `
})
export class SampleComponent {
  (...)
}

O en breve:

@Component({
  template: `
    <ul>
      <li *ngFor="let number of [0,1,2,3,4]">{{number}}</li>
    </ul>
  `
})
export class SampleComponent {
  (...)
}

55
Si, Thierry! No es tu culpa, de hecho, pero sigue en el mismo contexto :( No es elegante en absoluto. Pero como eres un desarrollador A2 muy hábil, puedo suponer que no hay una solución mejor. ¡Es triste!
Marco Jr

De hecho, no hay nada para esto en Angular2 en la sintaxis del bucle. Debe aprovechar lo que JavaScript proporciona para construir matrices. Por ejemplo: Array(5).fill(4)para crear[4,4,4,4,4]
Thierry Templier el

3
PD: la anotación @View se ha eliminado en angular2 beta 10 y superior.
Pardeep Jain

23
El uso Array.fill()en Angular 2 Typecript produce el siguiente error Supplied parameters do not match any signature of call t arget.: al verificar los documentos Array.prototype.fill, dice que requiere 1 argumento ... developer.mozilla.org/en/docs/Web/JavaScript/Reference/…
Joshua Russell

55
Array(5).fill(1).map((x, i) => i + 1); /*[1,2,3,4,5]*/esto resuelve el error en TS
mshahbazm

90

@OP, estabas terriblemente cerca con tu solución "no elegante".

Qué tal si:

<div class="month" *ngFor="let item of [].constructor(10); let i = index"> ... </div>

Aquí obtengo el Arrayconstructor de una matriz vacía: [].constructorporque Arrayno es un símbolo reconocido en la sintaxis de la plantilla, y soy demasiado vago para hacerlo Array=Arrayo counter = Arrayen el mecanografiado del componente como lo hizo @ pardeep-jain en su cuarto ejemplo. Y lo llamo sin newporque newno es necesario para obtener una matriz del Arrayconstructor.

Array(30)y new Array(30)son equivalentes

La matriz estará vacía, pero eso no importa porque lo que realmente desea utilizar ia partir ;let i = indexde su bucle.


13
Esta es la mejor respuesta.
kagronick

Esta solución desencadena la detección de cambios. Supongo que debido a la nueva matriz.
Tobias81

1
@ Tobias81, ¿podrías explicarlo? ¿Está diciendo que cada vez que la aplicación ejecuta la detección de cambios, los contenidos de * ngFor se vuelven a dibujar porque se vuelve a crear la matriz? Eso definitivamente vale la pena señalar. Se podría evitar creando un campo de matriz en el TS para hacer referencia, de modo que sea igual cada vez que se ejecute la detección de cambios. Pero eso sería definitivamente menos elegante de lo deseado. ¿Está presente el mismo problema de detección de cambio en el segundo ejemplo en la respuesta seleccionada de Thierry Templier? <li *ngFor="let number of [0,1,2,3,4]">{{number}}</li>
jcairney

Esta es la mejor solución encontrada para este problema
Khush

1
@ Tobias81, he comprobado para asegurarme de que la detección de cambios no recrea el contenido de ngFor repetidamente, colocando una declaración de impresión dentro del constructor de un componente que creo como hijo de la directiva ngFor de ejemplo. No veo los componentes que se recrean en cada iteración de Detección de cambios, por lo que no creo que haya realmente un problema (al menos en Angular 8).
jcairney

83

No, todavía no hay un método para que NgFor use números en lugar de colecciones. Por el momento, * ngFor solo acepta una colección como parámetro, pero puede hacerlo mediante los siguientes métodos:

Usando tubería

pipe.ts

import {Pipe, PipeTransform} from 'angular2/core';

@Pipe({name: 'demoNumber'})
export class DemoNumber implements PipeTransform {
  transform(value, args:string[]) : any {
    let res = [];
    for (let i = 0; i < value; i++) {
        res.push(i);
      }
      return res;
  }
}


<ul>
  <li>Method First Using PIPE</li>
  <li *ngFor='let key of 5 | demoNumber'>
    {{key}}
  </li>
</ul>

Usar una matriz de números directamente en HTML (Ver)

<ul>
  <li>Method Second</li>
  <li *ngFor='let key of  [1,2]'>
    {{key}}
  </li>
</ul>

Usando el método Split

<ul>
  <li>Method Third</li>
  <li *ngFor='let loop2 of "0123".split("")'>{{loop2}}</li>
</ul>

Usar la creación de una nueva matriz en el componente

<ul>
  <li>Method Fourth</li>
  <li *ngFor='let loop3 of counter(5) ;let i= index'>{{i}}</li>
</ul>

export class AppComponent {
  demoNumber = 5 ;

  counter = Array;

  numberReturn(length){
    return new Array(length);
  }
}

Demostración de trabajo


44
También podría usar el Array.fill()método para generar la matriz en lugar de lo res.push()que se muestra en la respuesta de Thierrys.
Günter Zöchbauer 01 de

si puedo pero hay algo mal con push? Quiero decir que ambos métodos son correctos, pero aún así, si hay alguna diferencia. entre ellos.
Pardeep Jain

3
No, sigue siendo una buena solución +1. Simplemente encuentro el Array.fill()más elegante que el loop usando push y también es probablemente más eficiente.
Günter Zöchbauer

1
Me gusta esta solución counter = Array, muy inteligente;)
Verri

11

No podía soportar la idea de asignar una matriz para la repetición simple de componentes, así que he escrito una directiva estructural. En la forma más simple, eso no hace que el índice esté disponible para la plantilla, se ve así:

import { Directive, Input, TemplateRef, ViewContainerRef } from '@angular/core';

@Directive({ selector: '[biRepeat]' })
export class RepeatDirective {

  constructor( private templateRef: TemplateRef<any>,
             private viewContainer: ViewContainerRef) { }

  @Input('biRepeat') set count(c:number) {
    this.viewContainer.clear();
    for(var i=0;i<c;i++) {
      this.viewContainer.createEmbeddedView(this.templateRef);
    }
  }
}

http://plnkr.co/edit/bzoNuL7w5Ub0H5MdYyFR?p=preview


Estoy de acuerdo en que el enfoque de matriz es feo, pero esto me parece una optimización prematura.
Aluan Haddad

3
Por supuesto, pero también un ejercicio de redacción de una directiva. Por otro lado, no es más largo que la tubería, lo que sería un segundo enfoque sensato.
productos del

Ese es un buen punto, no hay muchas oportunidades para obtener algunos de ustedes con el concepto de directivas estructurales personalizadas.
Aluan Haddad

Nice one @pdudits - Todavía funciona con las últimas versiones: plnkr.co/edit/8wJtkpzre3cBNokHcDL7?p=preview [no dude en actualizar su plnkr]
AT

5

Lo resolví así usando Angular 5.2.6 y TypeScript 2.6.2:

class Range implements Iterable<number> {
    constructor(
        public readonly low: number,
        public readonly high: number,
        public readonly step: number = 1
    ) {
    }

    *[Symbol.iterator]() {
        for (let x = this.low; x <= this.high; x += this.step) {
            yield x;
        }
    }
}

function range(low: number, high: number) {
    return new Range(low, high);
}

Se puede usar en un componente como este:

@Component({
    template: `<div *ngFor="let i of r">{{ i }}</div>`
})
class RangeTestComponent {
    public r = range(10, 20);
}

Comprobación de errores y afirmaciones omitidas a propósito por brevedad (por ejemplo, qué sucede si el paso es negativo).


2
¿Hay alguna manera en HTML como de <div *ngfor="let i of 4, i++"></div>pueden ser
Nithila Shanmugananthan

5

también puedes usar así

export class SampleComponent {
   numbers:Array<any> = [];
   constructor() {
      this.numbers = Array.from({length:10},(v,k)=>k+1);
   }
}

HTML

<p *ngFor="let i of numbers">
   {{i}}
</p>

4

Puedes usar lodash:

@Component({
  selector: 'board',
  template: `
<div *ngFor="let i of range">
{{i}}
</div>
`,
  styleUrls: ['./board.component.css']
})
export class AppComponent implements OnInit {
  range = _.range(8);
}

No probé el código pero debería funcionar.


¿Hay alguna manera en HTML como de <div *ngfor="let i of 4, i++"></div>pueden ser
Nithila Shanmugananthan

Si necesita io indexa un código, puede hacerlo*ngFor="let i of range; let i = index"
Alex Po

3

Esto también se puede lograr así:

HTML:

<div *ngFor="let item of fakeArray(10)">
     ...
</div>

Mecanografiado:

fakeArray(length: number): Array<any> {
  if (length >= 0) {
    return new Array(length);
  }
}

Demo de trabajo


2

Dado que el método fill () (mencionado en la respuesta aceptada) sin argumentos arroja un error, sugeriría algo como esto (funciona para mí, Angular 7.0.4, Typecript 3.1.6)

<div class="month" *ngFor="let item of items">
...
</div>

En código de componente:

this.items = Array.from({length: 10}, (v, k) => k + 1);

1
<div *ngFor="let number of [].constructor(myCollection)">
    <div>
        Hello World
    </div>
</div>

Esta es una manera agradable y rápida de repetir la cantidad de veces en myCollection.

Entonces, si myCollection fuera 5, Hello World se repetiría 5 veces.


1

Uso de directiva estructural personalizada con índice:

Según la documentación angular:

createEmbeddedView Instancia una vista incrustada y la inserta en este contenedor.

abstract createEmbeddedView(templateRef: TemplateRef, context?: C, index?: number): EmbeddedViewRef.

Param          Type           Description
templateRef    TemplateRef    the HTML template that defines the view.
context        C              optional. Default is undefined.
index          number         the 0-based index at which to insert the new view into this container. If not specified, appends the new view as the last entry.

Cuando angular crea una plantilla llamando a createEmbeddedView, también puede pasar el contexto que se usará en su interior ng-template.

Usando el parámetro opcional de contexto, puede usarlo en el componente, extrayéndolo dentro de la plantilla tal como lo haría con * ngFor.

app.component.html:

<p *for="number; let i=index; let c=length; let f=first; let l=last; let e=even; let o=odd">
  item : {{i}} / {{c}}
  <b>
    {{f ? "First,": ""}}
    {{l? "Last,": ""}}
    {{e? "Even." : ""}}
    {{o? "Odd." : ""}}
  </b>
</p>

for.directive.ts:

import { Directive, Input, TemplateRef, ViewContainerRef } from '@angular/core';

class Context {
  constructor(public index: number, public length: number) { }
  get even(): boolean { return this.index % 2 === 0; }
  get odd(): boolean { return this.index % 2 === 1; }
  get first(): boolean { return this.index === 0; }
  get last(): boolean { return this.index === this.length - 1; }
}

@Directive({
  selector: '[for]'
})
export class ForDirective {
  constructor(private templateRef: TemplateRef<any>, private viewContainer: ViewContainerRef) { }

  @Input('for') set loop(num: number) {
    for (var i = 0; i < num; i++)
      this.viewContainer.createEmbeddedView(this.templateRef, new Context(i, num));
  }
}

0

Encuentre adjunto mi solución dinámica si desea aumentar el tamaño de una matriz dinámicamente después de hacer clic en un botón (así es como llegué a esta pregunta).

Asignación de variables necesarias:

  array = [1];
  arraySize: number;

Declare la función que agrega un elemento a la matriz:

increaseArrayElement() {
   this.arraySize = this.array[this.array.length - 1 ];
   this.arraySize += 1;
   this.array.push(this.arraySize);
   console.log(this.arraySize);
}

Invocar la función en html

  <button md-button (click)="increaseArrayElement()" >
      Add element to array
  </button>

Iterar a través de la matriz con ngFor:

<div *ngFor="let i of array" >
  iterateThroughArray: {{ i }}
</div>

¿Hay alguna manera en HTML como de <div *ngfor="let i of 4, i++"></div>pueden ser
Nithila Shanmugananthan

tienes que iterar sobre una matriz. Si necesita el escalar, puede iterar sobre una matriz con el tamaño correcto e instanciar un escalar además: * ngFor = "let item of array; let i = index"
Jan Clemens Stoffregen

0

La forma más simple que he probado

También puede crear una matriz en su archivo componente y puede llamarla con la directiva * ngFor regresando como una matriz.

Algo como esto ....

import { Component, OnInit } from '@angular/core';

@Component({
  selector: 'app-morning',
  templateUrl: './morning.component.html',
  styleUrls: ['./morning.component.css']
})
export class MorningComponent implements OnInit {

  arr = [];
  i: number = 0;
  arra() {
    for (this.i = 0; this.i < 20; this.i++) {
      this.arr[this.i]=this.i;
    }
    return this.arr;
  }

  constructor() { }

  ngOnInit() {
  }

}

Y esta función se puede usar en su archivo de plantilla html

<p *ngFor="let a of arra(); let i= index">
value:{{a}} position:{{i}}
</p>

2
¿Hay alguna manera en HTML como de <div *ngfor="let i of 4, i++"></div>pueden ser
Nithila Shanmugananthan

0

Mi solución:

export class DashboardManagementComponent implements OnInit {
  _cols = 5;
  _rows = 10;
  constructor() { }

  ngOnInit() {
  }

  get cols() {
    return Array(this._cols).fill(null).map((el, index) => index);
  }
  get rows() {
    return Array(this._rows).fill(null).map((el, index) => index);
  }

En html:

<div class="charts-setup">
  <div class="col" *ngFor="let col of cols; let colIdx = index">
    <div class="row" *ngFor="let row of rows; let rowIdx = index">
      Col: {{colIdx}}, row: {{rowIdx}}
    </div>
  </div>
</div>

Esto crea una nueva matriz en cada obtención. Podría crear gastos generales
Remco Vlierman
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.