web-dev-qa-db-fra.com

Angular 6 Reactive Forms - Définissez la <option> sélectionnée d'un <select> FormControl dynamiquement par condition

J'ai besoin de savoir comment définir dynamiquement le <option> Sélectionné d'un <select> FormControl par condition.

Disons que nous avons des clients avec des commandes. Il existe un menu déroulant dans lequel nous sélectionnons un client et, en fonction de ce client, les options du menu déroulant des commandes doivent être définies dynamiquement et une commande spécifique doit être sélectionnée.

De plus, les deux menus déroulants ont un <option> "(Nouveau client/commande)" dans le cas où il n'y a pas encore de client ou de commande, donc si nous acheminons vers ce formulaire sans avoir de client, cette "nouvelle" option devrait être sélectionné dans les deux menus déroulants. Si nous avons un client sans commande, l'option "nouveau" doit être sélectionnée dans le menu déroulant des commandes.

Voici le formulaire.component.html:

<form [formGroup]="customerForm">
    <select formControlName="customers">
        <option>(new customer)</option>
        <option [ngValue]="customer" *ngFor="let customer of customers">
            {{customer.name}}
        </option>
    </select>
    <select formControlName="orders">
        <option>(new order)</option>
        <option [ngValue]="order" *ngFor="let order of filteredOrders">
            {{order.id}}
        </option>
    </select>
</form>

Voici le formulaire.component.ts:

customerForm: FormGroup;

customers: Customer[] = [
{   "id": 1,
    "name": "Meghan Castellano",
    "orderIds": [1,2] },
{   "id": 2,
    "name": "Monika Weiss",
    "orderIds": [3,4] }];

orders: Order[] = [{"id": 1},{"id": 2},{"id": 3},{"id": 4}];

filteredOrders: Order[];

constructor( private route: ActivatedRoute,
            private fb: FormBuilder) {}

ngOnInit() {
    this.customerForm = this.fb.group({
        customers: '',
        orders: '' });

    let customerId = this.route.snapshot.getParam('id');

    if (customerId == 0) {
        this.customerForm.patchValue({
            customers: "(new customer)",
            orders: "(new order)" });
    }
    else
    {
        let customer = this.customers[customerId];

        this.customerForm.patchValue({ customers: customer.name });

        if (customer.orderIds.length != 0) {
            this.filteredOrders = getCustomersOrders(customer);

        this.customerForm.patchValue({
            orders: this.filteredOrders[0].id }
        }
    }
}

getCustomersOrders(customer: Customer): Order[] {
    let orders: Order[];

    for (var id = 0; id < customer.orderIds.length; id++) {
        let orderId = customer.orderIds[id];
        let order = this.orders.find(i => i.id == orderId);
        orders.Push(order); }

    return orders;
}

Actuellement, j'achemine vers le formulaire et je fournis un identifiant avec l'URL. Le menu déroulant client sélectionne le bon client en fonction de l'ID dans l'URL.

Cependant, le menu déroulant de commande n'est rempli correctement, mais la sélection ne fonctionne pas. Aucune option n'est sélectionnée.

Si je remplace la valeur dans cette déclaration:

this.customerForm.patchValue({ orders: this.filteredOrders[0].id }

avec la chaîne "(new order)" déjà spécifiée dans le html:

this.customerForm.patchValue({ order: "(new order)" })

ça marche. La chaîne "(new order)" est sélectionnée dans le menu déroulant des commandes. Cela ne fonctionne tout simplement pas pour les commandes filtrées.

Qu'est-ce que je fais mal ici? Ai-je peut-être besoin d'une approche complètement différente ici?

4
PHilgarth

D'accord, après quelques heures, je l'ai compris. Voici mes fichiers .html et .ts mis à jour pour votre intérêt:

HTML:

<form class="form-container" [formGroup]="customerForm">
    <select formControlName="customer" (change)="onCustomerChanged($event)">
        <option>(new customer)</option>
        <option *ngFor="let customer of customers" [value]="customer.name">
            {{customer.name}}
        </option>
    </select>
    <select formControlName="order">
        <option>(new order))</option>
        <option *ngFor="let order of filteredOrders" [value]="order.id">
            {{order.id}}</option>
    </select>
</form>

fichier .ts

export class CustomerFormComponent implements OnInit {

  customerForm: FormGroup;
  selectedCustomer: Customer;
  filteredOrders: Order[];

  customers: Customer[] = [...];
  orders: Order[] = [...];  

  constructor(private route: ActivatedRoute,
              private fb: FormBuilder) { }

  ngOnInit() {
    this.customerForm = this.fb.group({
      customer: "",
      order: ""
    });

    let customerId = Number(this.route.snapshot.paramMap.get('customerId'));
    this.setFormControlValues(customerId);
  }

  setFormControlValues(customerId: number) {
    if (customerId == 0) {
      this.customerForm.get('customer').setValue("(new customer)");
      this.customerForm.get('order').setValue("(new order)");
    }
    else  {
      this.selectedCustomer = this.customers.find(i => i.id == customerId);
      this.filteredOrders = this.getCustomerOrders(this.selectedCustomer);

      this.customerForm.get('customer').setValue(this.selectedCustomer.name);
      this.customerForm.get('order').setValue(this.filteredOrders[0].orderNumber);
    }
  }

  getCustomerOrders(customer: Customer) : Order[] {
    let orders: Order[] = [];

    for (var id = 0; id < customer.orderIds.length; id++)  {
      let orderId = customer.orderIds[id];
      orders.Push(this.orders.find(i => i.id == orderId));
    }

    return orders;
  }

  onCustomerChanged(event: any) {
    this.selectedCustomer = this.customers.find(n => n.name == event.target.value);

    this.setFormControlValues(this.selectedCustomer.id);
  }
}

Comme vous pouvez le voir, dans le HTML, j'utilise maintenant "[value]" au lieu de "[ngValue]" et "customer.name"/"order.id" au lieu de seulement "customer"/"order".

Dans le fichier .ts, je me suis débarrassé de la méthode "patchValue ()" et j'ai introduit la méthode "setValue".

Voici l'exemple de stackblitz qui fonctionne:

https://stackblitz.com/edit/angular-conditionaldropdown-gnajge

6
PHilgarth

Je pense que le problème vient de l'objet qui est sélectionné dans le sélecteur de commande (également dans celui des clients, en fait).

<option [ngValue]="order" *ngFor="let order of filteredOrders">
    {{order.id}}
</option>

Cela signifie que tout l'objet order sera sélectionné et pas seulement l'identifiant de la commande. Dans ce cas, si vous essayez de corriger la valeur uniquement avec le order.id, alors cela ne fonctionnera pas: le sélecteur attend un objet Order, pas le order.id.

J'ai mis en place un stackblitz rapide avec une version de travail du code que vous avez mis dans la question: https://stackblitz.com/edit/angular-drop-dowb . La seule vraie différence est que

this.customerForm.patchValue({
    orders: this.filteredOrders[0]
});

est utilisé au lieu de

this.customerForm.patchValue({
    orders: this.filteredOrders[0].id }
});

Mettre à jour

J'ai donc mis à jour le stackblitz.

Pour conserver le champ orders avec les valeurs correctes le long du customer, vous devrez ajouter un (change) dans le champ select.

<select formControlName="customers" (change)="updateOrders($event)">
    <option [ngValue]="null">(new customer)</option>
    <option [value]="customer.id" *ngFor="let customer of customers" >
        {{customer.name}}
    </option>
</select>

N'oubliez pas de passer le champ client id comme valeur au lieu du customer - Angular ne semble pas l'aimer. Avec le id alors vous devrez ajouter une fonction updateOrders dans le component.ts.

  updateOrders(event) {
    const index = this.customers.findIndex(item => item.id == event.target.value);
    if (index === -1) {
      this.customerForm.controls.orders.setValue(null);
      return;
    }
    this.filteredOrders = this.getCustomersOrders(this.customers[index]);
    this.customerForm.controls.orders.setValue(this.filteredOrders[0]);
  }

Dans le cas où id ne figure pas dans la liste des clients, vous ne mettrez à jour que orders avec une valeur null, correspondant à votre (new order). Et si le id est dans la liste des clients, vous mettrez à jour le filteredOrders.

1
RoadEx