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

时间:2019-06-13 09:37:18

标签: angular viewchild mat-autocomplete

我正在设置发票Angular页面,其中动态添加行,每行应具有自己的自动完成输入以及其他不同的输入。我如何在组件内部独立引用每个自动完成输入,以便可以在内部使用正确的我的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:我只是可以访问相同的第一个自动完成输入ID,这可能会导致following dysfunction

2 个答案:

答案 0 :(得分:1)

您可以在此处使用模板引用。文件: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进行同样的操作。

答案 1 :(得分:0)

在有关模板引用变量的注释中,我找到了一个在此处找到帮助的解决方案:

现在,我可以直接在自己的组件中访问当前的自动完成功能,而不再需要@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);
        }
    }