'Why does *ngIf seem to break my reactive form? How do I handle conditional Inputs and validation in Angular?
What I want to accomplish is for the form to change as the user changes the form type from the radio.
Standard basically has 2 selects (one classic and a fancier one, made with ng-select) and custom has a simple classic text input.
I am trying to change the form's functionality dynamically as the form type changes using the radio.
Besides trying to use formBuilder.group, I also tried using .setValidators on the individual inputs, but the result is the same: when I change the radio and the custom_channel_name input is shown i get this console error "Error: Cannot find control with name: 'custom_channel_name'"
What am I doing wrong and how do I properly handle reactive forms in this fashion?
What I have so far looks like this: https://i.imgur.com/n24mKs7.png , https://i.imgur.com/FfCgXFX.png
[ component.html ]
<div>
<form [formGroup]="organizationChannelForm" (ngSubmit)="submitOrganizationChannelsForm()">
<div *ngIf="isChannelTypeStandard" class="grid gap-4 grid-cols-2">
<!-- <form-picker label="Countries" [values]="selectCountriesSources" labelField="name" valueField="warehouse_id"
formControlName="warehouse_id"></form-picker> -->
<div>
<label for="channel_id" class="text-gray-700 dark:text-gray-400 block w-full pb-1 text-sm">Channel</label>
<select [(ngModel)]="selectedChannel" id="channel_id" formControlName="channel_id"
class="block w-full dark:bg-gray-700 dark:text-gray-300 form-input">
<option *ngFor="let channel of selectChannelsSources" [value]="channel">{{ channel }}</option>
</select>
</div>
<div>
<label for="countries_ids" class="text-gray-700 dark:text-gray-400 block w-full pb-1 text-sm">Countries</label>
<ng-select [items]="selectCountriesSources" [(ngModel)]="selectedCountries"
[ngModelOptions]="{ standalone: true }" id="countries_ids" [multiple]="true" bindLabel="name"
name="countries_ids"></ng-select>
</div>
</div>
<div *ngIf="!isChannelTypeStandard">
<label for="custom_channel_name" class="text-gray-700 dark:text-gray-400 block w-full pb-1 text-sm">Custom Channel</label>
<input class="form-input block w-full dark:bg-gray-700 dark:text-gray-300"
type="text" id="custom_channel_name" formControlName="custom_channel_name">
</div>
<div class="flex mt-5">
<div class="flex ml-auto items-center">
<span class="dark:text-gray-400">Channel Type</span>
<div class="ml-6 flex">
<label class="flex items-center cursor-pointer">
<input [(ngModel)]="isChannelTypeStandard" [ngModelOptions]="{ standalone: true }" (change)="updateOrganizationChannelForm($event)"
type="radio" class="form-radio text-purple-600 h-4 w-4" name="channelType" [value]="true">
<span class="dark:text-gray-300 font-medium ml-2">Standard</span>
</label>
<label class="flex items-center cursor-pointer ml-4">
<input [(ngModel)]="isChannelTypeStandard" [ngModelOptions]="{ standalone: true }" (change)="updateOrganizationChannelForm($event)"
type="radio" class="form-radio text-purple-600 h-4 w-4" name="channelType" [value]="false">
<span class="dark:text-gray-300 font-medium ml-2">Custom</span>
</label>
</div>
</div>
<div class="ml-8 min-w-0 text-white flex flex-col items-end rounded-lg shadow-xs">
<button type="submit" aria-label="add" [disabled]="organizationChannelForm.errors || organizationChannelForm.pristine"
class="flex items-end justify-between px-4 py-2 text-sm font-medium leading-5 text-white transition-colors duration-150 bg-purple-600 border border-transparent rounded-lg active:bg-purple-600 hover:bg-purple-700 focus:outline-none focus:shadow-outline-purple disabled:bg-grey-600">
Assign Channel
<fa-icon class="ml-2" [icon]="icons.plus"></fa-icon>
</button>
</div>
</div>
</form>
</div>
[ component.ts ]
export class OrganizationChannelsComponent implements OnInit {
selectChannelsSources: Array<string> = ["eMag Marketplace", "Vtex", "Shopify", "Magento1", "Magento2", "WooCommerce", "Prestashop", "TeamShare", "Gomag", "Opencart", "MerchantPro", "Cscart", "Allegro", "Idosell", "ChannelAdvisor", "Shoprenter", "Transfer", "Defecte/Defects", "Manual Order"];
selectCountriesSources: Array<Country> = [];
icons = {
close: faTimes,
plus: faPlus
}
organizationChannelForm!: FormGroup;
selectedCountries: Array<Country> = [];
selectedChannel: Channel | undefined;
isChannelTypeStandard: boolean = true;
@Input() organizationId!: ID;
organizationChannels$: Observable<OrganizationChannel[]> = new BehaviorSubject<OrganizationChannel[]>([]);
channels$: Observable<Channel[]> = new BehaviorSubject<Channel[]>([]);
constructor(
private organizationChannelsService: OrganizationsChannelsService,
private organizationChannelsQuery: OrganizationChannelsQuery,
private countriesService: CountriesService,
private toasterService: ToasterService,
private formBuilder: FormBuilder,
) { }
ngOnInit(): void {
this.organizationChannelForm = this.formBuilder.group({
channel_id: ['', Validators.required],
});
this.organizationChannelsService.getOrganizationChannels(this.organizationId).subscribe();
this.organizationChannels$ = this.organizationChannelsQuery.selectOrganizationChannels(this.organizationId as number);
this.countriesService.get().subscribe(countries => this.selectCountriesSources = countries);
}
updateOrganizationChannelForm() {
if (this.isChannelTypeStandard) {
this.organizationChannelForm = this.formBuilder.group({
channel_id: ['', Validators.required],
});
}
else {
this.organizationChannelForm = this.formBuilder.group({
custom_channel_name: [Validators.required, Validators.minLength(8)]
});
}
}
}
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|
