typescript - Angular 6 Formularios Reactivos - Establecer dinámicamente la <option> seleccionada de un <select> FormControl según una condición
Necesito saber cómo establecer dinámicamente la <option> seleccionada de un <select> FormControl según una condición.
Digamos que tenemos clientes con pedidos. Hay un menú desplegable donde seleccionamos un cliente y, dependiendo de ese cliente, las opciones para el menú desplegable de pedidos deberían establecerse dinámicamente y se debería seleccionar un pedido específico.
Además, ambos menús desplegables tienen una <option> “(nuevo cliente/pedido)” en caso de que aún no haya un cliente o un pedido, por lo que si dirigimos a ese formulario sin tener un cliente, esta opción “nuevo” debería estar seleccionada en ambos menús desplegables. Si tenemos un cliente sin pedidos, la opción “nuevo” debería estar seleccionada en el menú desplegable de pedidos.
Esto es el form.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>
Este es el form.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;
}
Actualmente estoy dirigiendo al formulario y proporcionando un id con la URL. El menú desplegable de clientes selecciona el cliente correcto dependiendo del id en la URL.
Sin embargo, el menú desplegable de pedidos solo se llena correctamente, pero la selección no funciona. No se selecciona ninguna opción en absoluto.
Si reemplazo el valor en esta declaración:
this.customerForm.patchValue({ orders: this.filteredOrders[0].id }con la cadena "(nuevo pedido)" ya especificada en el html:
this.customerForm.patchValue({ order: "(new order)" })funciona. La cadena "(nuevo pedido)" está seleccionada en el menú desplegable de pedidos. Simplemente no funciona para los filteredOrders.
¿Qué estoy haciendo mal aquí? ¿Quizás necesito un enfoque completamente diferente aquí?
Respuesta #1
Está bien, después de un par de horas lo entendí. Aquí están mis archivos .html y .ts actualizados para tu interés:
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>
archivo .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);
}
}
Como puedes ver, en el HTML ahora uso "[value]" en lugar de "[ngValue]" y "customer.name"/"order.id" en lugar de solo "customer"/"order".
En el archivo .ts me deshice del método "patchValue()" y utilicé el método "setValue".
Aquí está el ejemplo funcional de Stackblitz:
https://stackblitz.com/edit/angular-conditionaldropdown-gnajge
Respuesta #2
Creo que el problema proviene del objeto que está seleccionado en el selector de pedidos (también en el de clientes, de hecho).
<option [ngValue]="order" *ngFor="let order of filteredOrders">
{{order.id}}
</option>
Esto significa que se seleccionará todo el objeto order y no solo el id del pedido. En este caso, si intentas actualizar el valor solo con el order.id, entonces no funcionará: el selector espera un objeto Order, no el order.id.
He armado un Stackblitz rápido con una versión funcional del código que pusiste en la pregunta: https://stackblitz.com/edit/angular-drop-dowb. La única diferencia real es que
this.customerForm.patchValue({
orders: this.filteredOrders[0]
});
se usa en lugar de
this.customerForm.patchValue({
orders: this.filteredOrders[0].id }
});
Actualizar
Entonces, he actualizado el stackblitz.
Para mantener el campo orders con los valores correctos junto al customer, necesitarás añadir un (change) en el campo 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>
No olvides pasar el campo id del cliente como valor en lugar del customer - A Angular no parece gustarle. Con el valor id, entonces necesitarás agregar una función updateOrders en el 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]);
}
En el caso de que el id no esté en la lista de clientes, entonces solo actualizarás los orders con un valor null, correspondiente a tu (nuevo pedido). Y si el id está en la lista de clientes, entonces actualizarás los filteredOrders.
