Невозможно отобразить коллекцию, динамически установленную во время выполнения

#javascript #angular #typescript

#javascript #угловой #typescript

Вопрос:

У меня есть фрагмент JSON, который представляет некоторые действия и параметры, которые может установить пользователь.

Я хочу отобразить действия в выпадающем списке, и когда пользователь выбирает один — отображаются требуемые параметры, связанные с действием.

Пример:

  • Пользователь выбирает действие с одним параметром, отображается единственное поле ввода
  • Пользователь выбирает действие с двумя параметрами, отображаются два поля ввода.

У меня почти получилось, но *ngFor не отображаются входные данные для выбранного действия:

введите описание изображения здесь

В onChange — если я печатаю this.steps , я вижу, что this.steps[i].SelectedAction.UIParameters имеет значение, поэтому я не уверен, почему оно не отображается.

JSON:

 [
  {
    "ActionEnum": "CLICKELEMENT",
    "Name": "Click Element",
    "UIParameters": [
      {
        "ParameterEnum": "ELEMENTID",
        "Description": "The id of the element to click"
      }
    ]
  },
  {
    "ActionEnum": "INPUTTEXT",
    "Name": "Input Text",
    "Description": "Enters text into the element identified by it's id",
    "UIParameters": [
      {
        "ParameterEnum": "ELEMENTID",
        "Description": "The id of the element"
      },
      {
        "ParameterEnum": "TEXTVALUE",
        "Description": "The text to enter into the element"
      }
    ]
  }
]
  

Typescript:

 import { Component, Output, EventEmitter, OnInit } from "@angular/core";
import { ActionService } from "../services/action-service";
import { Action } from "../models/Action";

@Component({
  selector: 'app-scenario-step-editor-component',
  template: `
  <form #formRef="ngForm">
    <div *ngFor="let step of steps; let in=index" class="col-sm-3">
      <div class="form-group">
        <label class="sbw_light">Action:</label><br />
        <select (change)='onChange()' [(ngModel)]="step.Action" name="action_name_{{in}}">
            <option *ngFor="let action of this.availableActions" [(ngModel)]="steps[in].value" name="action_name_{{in}}" class="form-control" required>
              {{action.Name}}
            </option>
        </select>
        <div *ngIf="steps[in].SelectedAction">
          <label class="sbw_light">Parameters:</label><br />

          <ng-template *ngFor="let parameter of steps[in].SelectedAction.UIParameters">
            <label class="sbw_light">{{parameter.ParameterEnum}}</label><br />
            <input (change)='onChange()' type="text" [(ngModel)]="steps[in].Parameters" name="parameter_name_{{in}}" class="form-control" #name="ngModel" required />
          </ng-template>

        </div>
      </div>
    </div>

    <button id="addStepBtn" type="button" class="btn btn-light" (click)="addScenarioStep()">Add Scenario Step  </button>
  </form>`
})

export class ScenarioStepEditorComponent implements OnInit {
  @Output() onSelectValue = new EventEmitter<{stepInputs: any[]}>();

  steps = [];
  availableActions: Action[];

  constructor(private actionService: ActionService) {}

  ngOnInit(): void {
    this.actionService.list().subscribe( result => {
      this.availableActions = resu<
    },
    error => console.log('Error getting actions...') );
  }

  /* When user picks an option, save the chosen action with the rest of the parameters*/
  onChange() {
    for (let i = 0; i < this.steps.length; i  ) {
      let actionIndex = this.availableActions.findIndex(a => a.Name === this.steps[i].Action);
      this.steps[i].SelectedAction = this.availableActions[actionIndex];
    }
    this.onSelectValue.emit( {stepInputs: this.steps} );
  }

  addScenarioStep() {
    this.steps.push({value: ''});
  }
}
  

Комментарии:

1. почему вы используете ng-template ? возможно, вы хотели ng-container вместо этого?..

2. Это сработало! Как вы узнали? Должно быть, я просто скопировал ее куда-то, не осознавая..

3. ng-template не предполагается, что она используется для такого поведения. ng-template используется для условного отображения некоторого содержимого или для предоставления содержимого другим элементам angular. Конкретно для вашего сценария, ng-container был подходящим элементом для использования 😉

Ответ №1:

 <ng-template *ngFor="let parameter of steps[in].SelectedAction.UIParameters">
            <label class="sbw_light">{{parameter.ParameterEnum}}</label><br />
            <input (change)='onChange()' type="text" [(ngModel)]="steps[in].Parameters" name="parameter_name_{{in}}" class="form-control" #name="ngModel" required />
          </ng-template>
  

Просто замените ng-template на ng-container :

 <ng-container *ngFor="let parameter of steps[in].SelectedAction.UIParameters">
            <label class="sbw_light">{{parameter.ParameterEnum}}</label><br />
            <input (change)='onChange()' type="text" [(ngModel)]="steps[in].Parameters" name="parameter_name_{{in}}" class="form-control" #name="ngModel" required />
          </ng-container>
  

Причины:

  • ng-container подходит для этой ситуации. Он просто содержит «что-то» и может быть повторен.
  • ng-template определяет шаблон. Здесь вам не нужен шаблон, шаблоны не предназначены для этого. Конечно, это может сработать, но это не подходит для вашего сценария.

Подробнее о ng-template и ng-container читайте здесь: https://blog.angular-university.io/angular-ng-template-ng-container-ngtemplateoutlet /

В качестве последнего примечания, вы могли бы использовать ng-template путем определения элемента, и вы могли бы использовать ng-container with *ngTemplateOutlet для отображения шаблона. Проверьте приведенное выше руководство для некоторых примеров.