参考使用 ngFor 循环动态生成的自动完成输入

Refer to autocomplete inputs generated dynamically with ngFor loop

我正在设置一个发票 Angular 页面,其中的行是动态添加的,每一行都应该有一个自己的自动完成输入和其他不同的输入 inputs.How 我可以在我的组件中独立地引用每个自动完成输入吗这样我就可以在我的 displayWith 函数中使用正确的那个了?

我的问题与 this 相似,但我在那里找不到正确的答案:(

HTML

<tr *ngFor="let ligneFacture of facture.lignesFacture; let i =index;">
    <td width="5%" align="center">
        <button type="button" class="button btn-primary"
                style="border-radius: 50px;" disabled><span>{{i+1}}</span>
        </button>
    </td>
    <td width="25%">
        <form>
            <mat-form-field>
                <input type="text" class="form-control" matInput
                       (ngModelChange)="watchChangesArticle($event)"
                       (focus)="cursorFocusArticle($event)"
                       [matAutocomplete]="autoArticle"
                       [ngModelOptions]="{standalone: true}"
                       [(ngModel)]="ligneFacture.articleId">
                <mat-autocomplete #autoArticle="matAutocomplete"
                                  [displayWith]="displayFnArticle.bind(this)"
                                  [autoActiveFirstOption]="true">
                    <mat-option *ngFor="let article of articles;"
                                [value]="article.id">
                        {{article.libelle}}
                    </mat-option>
                </mat-autocomplete>
            </mat-form-field>
        </form>
    </td>
    <td width="10%"><input type="text" class="form-control"
                           style="text-align: center"
                           [ngModelOptions]="{standalone: true}"
                           [(ngModel)]="ligneFacture.unite"/>
    <td width="10%"><input type="number" class="form-control" #quantity
                           id="quantity{{i+1}}"
                           [ngModelOptions]="{standalone: true}"
                           [(ngModel)]="ligneFacture.quantite"/>
    </td>
    <td width="13%">
        <div class="input-group">
            <input type="number" class="form-control"
                   [ngModelOptions]="{standalone: true}"
                   [(ngModel)]="ligneFacture.tauxTva" readonly/>
            <div class="input-group-append">
                <span class="input-group-text">%</span>
            </div>
        </div>
    </td>

    <td width="15%"><input type="number" class="form-control"
                           [ngModelOptions]="{standalone: true}"
                           [(ngModel)]="ligneFacture.prixUnitaire" tabindex="-1"
                           [value]="updateChanges(ligneFacture)"
                           readonly/></td>

    <td width="15%"><input type="number" class="form-control"
                           [ngModelOptions]="{standalone: true}"
                           [(ngModel)]="ligneFacture.prixTotal" tabindex="-1" readonly/>
    </td>
    <td>
        <div>
            <button type="button" class="btn btn-danger"
                    (click)="removeLine(ligneFacture)" tabindex="-1">
                <span>[X]</span>
            </button>
        </div>
    </td>
</tr>
<br>
<tr>
    <td></td>
    <td></td>
    <td></td>
    <td></td>
    <td></td>
    <td colspan="2" align="right">
        <button type="button" class="btn btn-primary" (click)="addLine()">
            <span>[+] Ajouter une ligne</span>
        </button>
    </td>
</tr>

TS:

export class FactureUpdateComponent implements OnInit {
    @ViewChild('autoArticle') matAutocompleteArticle: MatAutocomplete;
    //
    //

    displayFnArticle(item) {
        const matOptions = this.matAutocompleteArticle.options.filter(x => x.value === item);
        if (matOptions.length !== 0) {
            setTimeout(() => {
                const element = document.getElementById('quantity' + this.facture.lignesFacture.length);
                element.focus();
            }, 0);
            return matOptions.map(x => x.viewValue)[0];
        } else {
            return this.facture.lignesFacture.filter(x => x.articleId === item).map(x => x.articleLibelle);
        }
    }

    watchChangesArticle(event, ligneFacture: ILigneFacture) {
        this.articleService.queryByKeyword(event, this.req).subscribe(
            (res: HttpResponse<IArticle[]>) => {
                ligneFacture.articles = res.body;
            },
            (res: HttpErrorResponse) => this.onError(res.message)
        );
    }

    cursorFocusArticle(event, ligneFacture: ILigneFacture) {
        if (event.target.value === '') {
            this.articleService.queryByKeyword('', this.req).subscribe(
                (res: HttpResponse<IArticle[]>) => {
                    ligneFacture.articles = res.body;
                },
                (res: HttpErrorResponse) => this.onError(res.message)
            );
        }
    }
}

预期结果:在 displayFnArticle 函数中,我可以访问当前行的自动完成输入

Actual result : im just getting access to the same first autocomplete input id which may be causing the following dysfunction

您可以在那里使用模板引用。文档:https://angular.io/guide/template-syntax#template-reference-variables--var-.

例子在这里:https://stackblitz.com/edit/angular-template-ref-with-fn

所以你有 HTML 这样的:

<button *ngFor="let i of array" #ref (click)="doStuff(ref)" class="{{ 'a' + i }}">{{ 'a' + i }}</button>

然后你这样处理:

doStuff(instance: HTMLElement) {
  console.log('>> stuff', instance.classList);
}

这只是一个包含简单元素的示例,但您可以使用 MatAutocomplete 执行相同的操作。

我在有关模板引用变量的评论中找到了一些帮助的解决方案:

现在,我可以直接在我的组件中访问当前的自动完成功能,不再需要 @ViewChild

HTML:

  <mat-autocomplete #autoArticle="matAutocomplete" #currentAutocomplete
        [displayWith]="displayFnArticle.bind(this, currentAutocomplete)" [autoActiveFirstOption]="true">
          <mat-option *ngFor="let article of articles;"
                                [value]="article.id">
                        {{article.libelle}}
          </mat-option>
  </mat-autocomplete>

TS :

    displayFnArticle(autocomplete: MatAutocomplete, item) {
        const matOptions = autocomplete.options.filter(x => x.value === item);
        if (matOptions.length !== 0) {
            setTimeout(() => {
                const element = document.getElementById('quantity' + this.facture.lignesFacture.length);
                element.focus();
            }, 0);
            return matOptions.map(x => x.viewValue)[0];
        } else {
            return this.facture.lignesFacture.filter(x => x.articleId === item).map(x => x.articleLibelle);
        }
    }