angular survey dynamic questions rendering template driven or reactive - angular9

I am new at angular currently started a project with angular 9 to build survey application where I have
dynamic questions with
different question type (radio,checkbox, textbox, textarea, dropdown,etc)
question options (dynamic for each question)
dynamic attributes like (required, minlength, maxlength, pattern, data-attributes)
I started with template driven form, but having issues with attribute, multiple checkboxes and may face more issue in future
while reactive Forms looks too much complex
Looking for expert suggesion which method should I follow
currently at teplate driven forms to render checkboxes I wrote this code
<!--checkbox type=2 -->
<div class="optgroup" *ngSwitchCase="2">
<div *ngFor="let opt of question.QuestionOption; let io = index" class="form-check form-check-inline mx-3" >
<input type="checkbox"
name="data[{{survey.Survey.id}}][{{section.id}}][{{question.id}}][]"
[id]="'rdo'+is+iq+io"
value="{{opt.title}}"
class="form-check-input"
ngModel />
<label [for]="'rdo'+is+iq+io" class="form-check-label">{{opt.title}}</label>
</div>
</div>
I am able to select multiple checkboxes but at {{f.value | json}} only last changed value is coming as boolean but not array of select values.
and secondly how can add attribute dynamically
if(webservice.currentQuestion.attrubutes != null){
loop over attributes and
add each attribute to let's say above checkbox or <input type="text"> or <select>
}
have no idea how can add these dynamic attributes

Related

How to map non-input tags in html to individual iterations when using *ngFor directive in Angular during reverse routing?

I am facing a problem with *ngFor directive while reverse routing.
I am using the loop to input certain employment details multiple times. I am posting code snippet here for example and the entire file is huge.
<div class="row p-fluid" style="padding-top:30px;">
<div class="p-field p-col-12">
<span class="p-float-label">
<input type="text"
class="inputTextBox"
id="employer{{i}}"
pInputText
name="incomeName{{i}}"
[(ngModel)]="income.EmploymentName"
#employerCtrl="ngModel"
required>
<p-message class="messageError" severity="error" text="Employer is required" *ngIf="employerCtrl.errors?.['required'] && employerCtrl.touched && IsEmployed[i]"></p-message>
<p-message class="messageError" severity="error" text="Source description is required" *ngIf="employerCtrl.errors?.['required'] && employerCtrl.touched && IsBenefit[i]"></p-message>
<p-message class="messageError" severity="error" text="Business name is required" *ngIf="employerCtrl.errors?.['required'] && employerCtrl.touched && IsSelfEmployed[i]"></p-message>
<label for="employer{{i}}" class="inputLabel" *ngIf="IsEmployed[i]">Employer</label>
<label for="employer{{i}}" class="inputLabel" *ngIf="IsBenefit[i]">Source Description</label>
<label for="employer{{i}}" class="inputLabel" *ngIf="IsSelfEmployed[i]">Business Name</label>
</span>
</div>
</div>
Like this there are multiple fields. I am using following *ngFor in div.
<div *ngFor="let income of formData.ApplicantIncomeList;let i=index">
Using this I am collecting data for multiple incomes using an array in Angular.
There is a provision to choose from multiple income types. Labels in the code snippet will be decided by the type of income chosen.
During the forward flow, when I click on continue button, everything works fine Expected behavior is not observed when I get back to this tab through reverse routing (hitting back on browser). Value of the latest iteration was getting displayed for all the previous iterations.
Using Name attribute for input fields solved the problem for me. However, I am not able to map the labels to the iterations.
Eg: For income type 'Benefit' label must be 'Source Description'. But when I come to this page through reverse routing, it shows the label applicable to the last iteration.
Is there any way to distinguish label tag for each iteration? "Name" attribute is not applicable to non-input fields.

ngModel and checkbox/radio not working properly

I made html template for my app. Basically it's a radio/checkboxes with text inputs which contain answers to questions. It worked just fine until I've decided to add ngModel to them. The thing is that when I add 2 or more answers and click on a label to set the correct one(/s) only the last one selects, moreover the answertext disappears whenever I click on label.
html text:
<div *ngIf="question.typeQuestions==1">
<div *ngFor="let item of question.selectedAnswer; let i = index" class="checkbox-variable">
<input type="checkbox" id="customCheckbox{{i}}" name="customCheckbox" class="checkbox-square" [(ngModel)]="item.isCorrect" >
<label class="checkbox-label" for="customCheckbox{{i}}"><input class="checkbox-text" type="text" [(ngModel)]="item.text"></label>
</div>
</div>
ChrisY solved the problem.
having multiple input with the same name is definitive wrong here. Try name="customCheckbox{{i}}". When using ngModel you need a name that identifies the form control. It has to be unique

Handling multiple radio buttons in a Quiz Angular 5

I'm new to Angular and Implementing a Quiz containing multiple MCQs.
But I am having trouble in radio button selection.
My Questions are coming from the database and Options too.
mcq.component.html
<form (ngSubmit)="ff.form.valid && answer(ff)" #ff="ngForm">
<div *ngFor="let question of questions">
<p style="font-size: 25px;">{{question.title}}</p>
<div *ngFor="let option of question.options">
<input [(ngModel)]="option_model.selected_option_id" #selected_option_id="ngModel" type="radio" value="{{option.id}}" name="{{question.id}}">
<!-- <input type="radio" value="{{option.id}}" name="{{question.id}}" ngModel > --> //This way it works fine but I need to use [(ngModel)] to submit the form
{{option.title}}
</div>
</div>
<input style="float: right" type="submit" value="Submit"/>
</form>
Note: The {{question.id}} is unique for each question. Also, this works well if I remove the [(ngModel)] attribute.
And here is what I'm trying to accomplish
The Problem: When I select an option from the second question it deselects the selected option from the First Question. Means I am only able to select one option from both questions.
Please Help me, what I am doing wrong. I've been stuck here for two days.
Okay, Git it Sorted. The issue was with the ngModel and name attribute
It works fine like this
<input [(ngModel)]="options[question.id]" [checked]="options[question.id]" value="{{question.id}}-{{option.id}}" type="radio"
name="option{{question.id}}">
And in typescript
options: any = [];
option: any = [];

Naming dynamically created radio button groups with Angular2

I'd like to preface this question by noting that I am relatively new to web development and Angular2.
I have an Angular2 component whose function is to create a tree view on a webpage using the angular-tree-component library (https://angular2-tree.readme.io). For each node in the tree, I have defined a popover HTML template (from ng2-bootstrap) with radio buttons in it.
Since the tree can be of any size, the radio buttons are created dynamically in the popover using a template declared in the tree component HTML.
My issue is that the radio button groups are linked together, as if they were all a single group. I have tried several different ways of naming the button groups dynamically (ideally they would all be separate groups), but nothing seems to work.
Example code:
<div class="testTree">
<Tree #tree id="tree1" [nodes]="nodes">
<template #treeNodeTemplate let-node="node" let-index="index">
<span>{{ node.data.name }}</span>
<template #popTemplate>
<div class="popDiv">
Name: {{node.data.name}}<br>
id: {{node.data.id}}<br>
</div>
<div [innerHtml]="html"></div></template>
<template #incTemplate>
<div class="popDiv">
<input type="radio" attr.name="node.data.id"
value="ab" (change)="foo(node, $event.target.value)"> Button1<br>
<input type="radio" attr.name="node.data.id"
value="bc" (change)="foo(node, $event.target.value)"> Button2<br>
<input type="radio" attr.name="node.data.id"
value="cd" (change)="foo(node, $event.target.value)"> Button3<br>
<input type="radio" attr.name="node.data.id"
value="de" (change)="foo(node, $event.target.value)"> Button4<br><br>
<button (click)="bar(node)">ok</button>
</div>
<div [innerHtml]="html"></div></template>
<button class="nodeButton" *ngIf="node.isActive || node.isFocused"
[popover]="popTemplate" placement="right" triggers="" popoverTitle="title1" #pop="bs-popover" (click)="pop.toggle()">Details</button>
<button class="nodeButton" *ngIf="node.isActive || node.isFocused"
[popover]="incTemplate" placement="right" triggers="" popoverTitle="title2" #pop2="bs-popover" (click)="pop2.toggle()">Options</button>
</template>
</Tree>
</div>
I have tried to name the button groups in the #incTemplate in the following ways:
name = "node.data.id"
name = "{{node.data.id}}"
[attr.name] = "node.data.id"
attr.name = "{{node.data.id}}"
attr.name = "node.data.id"
To see what was happening, I gave the radio buttons an id and selected it in the angular2 class to log the name value. The name was either blank or the literal string "node.data.id".
I would use ngModel, but since there could be a large number of radio button groups I can't have them using the same variables.
Each tree node has two variables ('id' and 'name') which are unique to each node and could be used to name the radio buttons. However, I can't seem to get the name field of the button to resolve any of the expressions and take the value of the data field provided by the tree node.
How can I get the radio button groups named dynamically so that they are separate from each other?
It turns out I had included (ngModel) and id fields in the radio buttons' definitions. Removing these fields allowed the radio buttons to function as intended despite the dynamically created groups sharing the same name.
Try this approach
<div *ngFor="let location of locations">
<input
[attr.id]="location"
type="radio"
name="location"
ngModel
[value]="location">
<label [attr.for]="location">{{location}}</label>
</div>
Note this is just a example demonstrating how it works you have to
adjust your code according to your requirements

How to make multiple instances of the same Angular 2 component work properly in the same container

I have component <editor [id]="id"> template like
<input id="name" />
<label for="name"></label>
and using it with CSS framework like MaterializeCSS which binds labels to inputs using unique HTML DOM id attribute.
Now, If I do this:
<div *ngFor="let someThing of listOfSomeThings">
<editor [id]="someThing.id"></editor>
</div>
angular generates many components with the same id, so id attribute is not encapsulated at all and components begin to conflict with each other.
I found a solution by doing this:
<input [id]="id + 'name'" />
<label [for]="id + 'name'" />
But this looks bad and leads to less maintainable code. Is there any neater and finer way to write fully-encapsulated-dom components (maybe some internal Angular 2 function that I just need to enable)? Or am I to write all components in this way to ensure that it will never conflict in such situations?
You could use UUID generator for ids or just big random number.