Apologies on what might be an easy question but as a Product Manager trying to write Angular I'm hitting my head on the wall.
What I'm trying to create is an Angular form that has conditional sections. There's an initial checkbox: Should entry information be collected?
Based on this being checked it should open up the next section: Entry Categories
Within Entry Categories there are four options: Apple, Banana, Orange, Kiwi
If ANY of the four options in Entry Categories are selected it should open up the next section: Entry Details AND the entry details tied to that category should show the options.
All Entry Details are the same they have a checkbox for: Organic, GMO, Lego and an input with placeholder: Fruit Name
So if Apple is selected in Entry Categories it should open up Entry Details and display the following checkboxes: Organic, GMO, Lego; and a Fruit Name input.
I've been able to put a conditional from the first checkbox to the Entry Categories from here is where it gets dicey. My original attempt was a compilation of using formControlNames and every checkbox correlating with ngModel. It was also hyper complex. So then I made some massive changes to pull from an array in the ts file.
I'm running into the following issues:
My original entry html and ts files: entry.component.html
<div fxlayout="row" fxLayoutalign="start center" class="padded-bottom">
<mat-checkbox id="requireEntryInformation" formControlName="requireEntryInformation" [(ngModel)]="showEntryProduct">Should entry information be collected?
</mat-checkbox>
</div>
<div fxLayout="column" fxLayoutalign="start center" id="entryProductCategorySection" *ngIf="showEntryProduct">
<h2>Entry Categories</h2>
<div fxLayout="row" fxLayoutAlign="stretch">
<div fxFlex="50" fxFlexOffset="10" fxLayout="column" fxLayoutAlign="baseline">
<mat-checkbox id="apple_category" [(ngModel)]="showApple">Apple</mat-checkbox>
<mat-checkbox id="orange_category">Orange</mat-checkbox>
</div>
<div fxFlex="50" fxFlexOffset="10" fxLayout="column" fxLayoutAlign="baseline">
<mat-checkbox id="kiwi_category">Kiwi</mat-checkbox>
<mat-checkbox id="banana_category">Banana</mat-checkbox>
</div>
</div>
</div>
<div fxLayout="column" fxLayoutAlign="start center" id="entryDetailSection" *ngIf="showEntryDetailSection">
<h2>Entry Details</h2>
<div fxLayout="row" *ngIf="showApple">
<h3>Apple</h3>
<mat-checkbox>Organic</mat-checkbox>
<mat-checkbox>GMO</mat-checkbox>
<mat-checkbox>Lego</mat-checkbox>
</div>
</div>
entry.component.ts
showEntryProduct: boolean;
showApple: boolean;
defineFormGroup = () => {
this.initializeDefaultFormValues();
this.formGroup = this.formBuilder.group({
requireEntryInformation: [this.defaultFormValues['requireEntryInformation'], Validators.required]
});
}
That failed a lot so I built out: StackBlitz Example
I don't know how to handle an input field in the entry array.
entries = [
{
name: 'Apple',
options: ['Organic', 'GMO', 'Lego'],
checked: false,
field_name: 'String'
},
{
name: 'Banana',
options: ['Organic', 'GMO', 'Lego'],
checked: false,
},
{
name: 'Kiwi',
options: ['Organic', 'GMO', 'Lego'],
checked: false,
},
{
name: 'Contact',
options: ['Email Required?', 'Address Required?'],
checked: true,
},
];
In my results as soon as I click on the Apple it immediately loads the Entry Details when I want that section to be completely after Kiwi. Actual Results
You should start with the "data" you want to "store", not with the .html
Some like
<div fxlayout="row" fxLayoutalign="start center">
<mat-checkbox [(ngModel)]="showEntryProduct"
>Should entry information be collected?
</mat-checkbox>
</div>
<div *ngIf="showEntryProduct">
<div>
<h2>Entry Category</h2>
<div *ngFor="let entry of entries">
<div>
<mat-checkbox
[checked]="entry.checked"
(change)="unselectRest(entry)"
>{{ entry.name }}</mat-checkbox
>
</div>
</div>
<div *ngFor="let entry of entries">
<div *ngIf="entry.checked">
<div>
<h2>Preferred {{ entry.name }}</h2>
<mat-checkbox *ngFor="let option of entry.options">{{
option
}}</mat-checkbox>
</div>
<input mat-input [(ngModel)]="fruitName"/>
</div>
</div>
</div>
</div>
using in .ts
//declare also a variable fruitName
fruitName:string="";
unselectRest(entry:any){
this.entries.forEach(x=>{
x.checked=x==entry
})
}
Show the data (I make "exclusive" the checkbox), but you can not know what are the option selected
if the checked are "exclusive" we can change a bit the "entries" array adding a property: selected
entries = [
{
...
selected:-1,
},
{
...
selected:-1,
},
....
]
then we can change also the mat-check related to options
<mat-checkbox *ngFor="let option of entry.options;let i=index"
[checked]="entry.selected==i"
(change)="entry.selected=$event.checked?i:-1">
{{option}}
</mat-checkbox>
Now we have all we need in "entriesArray. e.g. in submit you can use some like
submit()
{
//find the entry checked
const entrie=this.entries.find(x=>x.checked)
//create an object "on fly" with the values
const result=entrie?{name:entrie.name,
option:entrie.selected>=0?entrie.options[entrie.selected]:
'',
fruitName:this.fruitName}:null
alert(JSON.stringify(result))
}
NOTE: There'r anothers ways to do, I only show a way using the event (change) of the mat-checkbox and [checked] property. If you're not using material, you can use some similar using ngModel. It's possible alse do some similar using reactive forms (but the philosophy change a bit)
NOTE2: remember: always think first in the data we can get, after think about the .html