【问题标题】:Angular 2 - NgFor using numbers instead collectionsAngular 2 - NgFor 使用数字而不是集合
【发布时间】:2016-07-21 03:24:25
【问题描述】:

...例如...

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

有可能做类似的事情......

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

...不诉诸非优雅的解决方案,例如:

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

?

【问题讨论】:

标签: angular


【解决方案1】:

对于 Angular,这里有一些非常干净和简单的东西:

在 .ts 中:

max = 10;    

在 .html 中:

<div *ngFor="let dummy of ','.repeat(max).split(','); index as ix">
   - {{ix + 1}}:
</div>

【讨论】:

    【解决方案2】:

    我的组件.ts

    numbers: number[] = [];
    
    constructor() {
      this.numbers = new Array<number>(10)
    }
    

    我的组件.html

    <div *ngFor="let num of numbers; let i = index">{{ i }}</div>
    

    【讨论】:

      【解决方案3】:

      使用管道将数字转换为数组。

      @Pipe({
        name: 'enumerate',
      })
      export class EnumeratePipe implements PipeTransform {
        transform(n: number): number[] {
          return [...Array(n)].map((_,i) => i);
        }
      }
      

      然后在模板中使用管道。

      <p *ngFor="let i of 5 | enumerate">
         Index: {{ i }}
      </p>
      

      https://stackblitz.com/edit/angular-ivy-pkwvyw?file=src/app/app.component.html

      【讨论】:

      • 值在 ngFor 中不可访问
      • 检查堆栈闪电战
      • 我的意思是如果你需要打印 1,2,3,4,5
      【解决方案4】:

      目前还没有 NgFor 使用数字而不是集合的方法, 目前,*ngFor 只接受一个集合作为参数,但你可以通过以下方法做到这一点:

      使用管道

      演示编号.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;
        }
      }
      

      对于较新的版本,您必须更改导入并删除 args[] 参数:

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

      html:

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

      在 HTML(View) 中直接使用数字数组

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

      使用拆分方法

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

      在组件中使用创建新数组

      <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);
        }
      }
      

      #Working demo

      【讨论】:

      • 您也可以使用 Array.fill() 方法来生成数组,而不是像 Thierrys 回答中所示的 res.push()
      • 是的,我可以,但push 有什么问题吗?我的意思是这两种方法都是正确的,但如果有任何差异的话。他们之间。
      • 不,仍然是一个不错的解决方案 +1。我只是发现Array.fill() 比使用 push 的循环更优雅,而且它也可能更有效。
      • 我喜欢counter = Array的这个解决方案,非常聪明;)
      • 当然import { Pipe, PipeTransform } from '@angular/core';。编辑:好的,我知道了,将编辑您的帖子以更新版本。
      【解决方案5】:

      使用带有索引的自定义结构指令:

      根据 Angular 文档:

      createEmbeddedView 实例化一个嵌入式视图并将其插入到此容器中。

      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.
      

      当 Angular 通过调用 createEmbeddedView 创建模板时,它还可以传递将在ng-template 中使用的上下文。

      使用上下文可选参数,你可以在组件中使用它, 就像使用 *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));
        }
      }
      

      【讨论】:

        【解决方案6】:
        <div *ngFor="let number of [].constructor(myCollection)">
            <div>
                Hello World
            </div>
        </div>
        

        这是在 myCollection 中重复多次的好方法。

        所以如果 myCollection 为 5,Hello World 将重复 5 次。

        【讨论】:

        • 我喜欢这个解决方案,因为不要触摸组件。谢谢!
        【解决方案7】:

        也可以这样实现:

        HTML:

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

        打字稿:

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

        Working Demo

        【讨论】:

        • 不要这样做,方法 fakeArray 将在每次更改检测时调用。如果您想在这样的模板中使用方法,则可以使用纯管道。
        【解决方案8】:

        由于不带参数的 fill() 方法(在接受的答案中提到)会引发错误,我建议这样的事情(适用于我,Angular 7.0.4,Typescript 3.1.6)

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

        在组件代码中:

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

        【讨论】:

          【解决方案9】:

          你也可以这样使用

          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>
          

          【讨论】:

            【解决方案10】:

            我的解决方案:

            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);
              }
            

            在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>
            

            【讨论】:

            • 这会在每次获取时创建一个新数组。可能会产生开销
            【解决方案11】:

            @OP,您的“非优雅”解决方案非常接近。

            怎么样:

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

            这里我从一个空数组中获取Array 构造函数:[].constructor,因为Array 在模板语法中不是可识别的符号,我懒得做Array=Array 或@组件打字稿中的 987654326@ 就像 @pardeep-jain 在他的第四个示例中所做的那样。我在没有new 的情况下调用它,因为new 不是从Array 构造函数中取出数组所必需的。

            Array(30)new Array(30) 是等价的。

            数组将为空,但这并不重要,因为您真的只想在循环中使用来自;let i = indexi

            【讨论】:

            • 此解决方案触发更改检测。我猜是因为新的 Array。
            • @Tobias81,你能详细说明一下吗?您是说每次应用程序运行更改检测时,都会重新绘制 *ngFor 的内容,因为重新创建了数组?这绝对值得注意。可以通过在 TS 中实际创建一个数组字段来引用它,这样每次更改检测运行时它都是相同的。但这肯定不如预期的那么优雅。 Thierry Templier 选择的答案中的第二个示例中是否存在相同的更改检测问题? &lt;li *ngFor="let number of [0,1,2,3,4]"&gt;{{number}}&lt;/li&gt;
            • @Tobias81,我已经检查以确保更改检测不会重复重新创建 ngFor 的内容,方法是将 print 语句放在我作为子组件创建的组件的构造函数中例如 ngFor 指令。我没有看到每次变更检测迭代都重新创建组件,所以我认为实际上没有问题(至少在 Angular 8 中)。
            • 使用硬编码值效果很好,但是当我尝试使用变量时,它没有按预期工作。如果你能举个例子说明如何使用一个很棒的变量
            【解决方案12】:

            我尝试过的最简单的方法

            你也可以在你的组件文件中创建一个数组,你可以用 *ngFor 指令调用它,返回一个数组。

            类似这样的......

            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() {
              }
            
            }
            

            而且这个函数可以在你的html模板文件中使用

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

            【讨论】:

            • &lt;div *ngfor="let i of 4, i++"&gt;&lt;/div&gt;的html有什么办法可以
            【解决方案13】:

            我使用 Angular 5.2.6 和 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);
            }
            

            它可以在这样的组件中使用:

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

            为了简洁而故意省略了错误检查和断言(例如,如果 step 为负数会发生什么)。

            【讨论】:

            • &lt;div *ngfor="let i of 4, i++"&gt;&lt;/div&gt;的html有什么办法可以
            【解决方案14】:

            在您的组件中,您可以定义一个数字数组 (ES6),如下所述:

            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]
              }
            }
            

            请参阅此链接以创建数组:Tersest way to create an array of integers from 1..20 in JavaScript

            然后您可以使用ngFor 遍历这个数组:

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

            或者很快:

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

            【讨论】:

            • 是的,蒂埃里!确实,这不是你的错,但仍然在相同的上下文中:( 这一点都不优雅。但由于你是一个非常熟练的 A2 开发人员,我可以假设没有更好的解决方案。这很可悲!
            • 其实在 Angular2 的循环语法中并没有这方面的内容。您需要利用 JavaScript 提供的功能来构建数组。例如:Array(5).fill(4) 创建[4,4,4,4,4]
            • PS:在 angular2 beta 10 及更高版本中,@View 注解已被移除。
            • 在 Angular 2 Typescript 中使用 Array.fill() 会产生以下错误 Supplied parameters do not match any signature of call t arget. — 检查 Array.prototype.fill 文档,它说它需要 1 个参数... developer.mozilla.org/en/docs/Web/JavaScript/Reference/…
            • Array(5).fill(1).map((x, i) =&gt; i + 1); /*[1,2,3,4,5]*/这解决了TS中的错误
            【解决方案15】:

            如果您想在单击按钮后动态增加数组的大小,请查看附件中的动态解决方案(这就是我提出这个问题的方式)。

            必要变量的分配:

              array = [1];
              arraySize: number;
            

            声明向数组添加元素的函数:

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

            在html中调用函数

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

            使用 ngFor 遍历数组:

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

            【讨论】:

            • &lt;div *ngfor="let i of 4, i++"&gt;&lt;/div&gt;的html有什么办法可以
            • 你必须遍历一个数组。如果您需要标量,您可以迭代具有正确大小的数组并另外实例化一个标量: *ngFor="let item of array; let i = index"
            【解决方案16】:

            你可以使用 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);
            }
            

            我没有测试代码,但它应该可以工作。

            【讨论】:

            • &lt;div *ngfor="let i of 4, i++"&gt;&lt;/div&gt;的html有没有办法可以
            • 如果您需要i 或索引代码,那么您可以使用*ngFor="let i of range; let i = index"
            【解决方案17】:

            我无法忍受为组件的简单重复分配数组的想法,所以我编写了一个结构指令。在最简单的形式中,它不会使索引对模板可用,它看起来像这样:

            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

            【讨论】:

            • 我同意数组方法很难看,但这对我来说似乎是过早的优化。
            • 当然,也是编写指令的练习。另一方面,它不比管道长,这是第二种理智的方法。
            • 这很好,没有太多机会让您了解自定义结构指令的概念。
            • 不错的@pdudits - 仍然适用于最新版本:plnkr.co/edit/8wJtkpzre3cBNokHcDL7?p=preview [随时更新您的 plnkr]
            猜你喜欢
            • 1970-01-01
            • 2019-09-17
            • 2016-10-10
            • 2017-08-07
            • 2019-05-09
            • 2018-10-26
            相关资源
            最近更新 更多