dynamic formcontrol in reactive forms - angular - javascript

I have a dynamic formControl where addControl is as follows in my component.ts file:
//...
this.reportList = this.data.reportList;
for (let i = 0; i < this.reportList.length; i++) {
const key = this.reportList[i].quesId+'';
this.afterActionReportForm.addControl(key, new FormControl(this.reportList[i].boardAnswers));
}
...///
my component.html file code looks as follows:
<div class="row" *ngIf="reportList && reportList.length > 0" style="padding:10px;">
<div *ngFor="let e of reportList; let i = index" class="col-sm-6" style="margin-top: 2em;">
{{i+1}}. {{e.question}}
<textarea style="width:100%" #{{e.quesId}} rows="8" cols="75" maxlength="500" formControlName="{{e.quesId}}"
class="form-control" pInputTextArea ></textarea>
<span> {{e.boardAnswers.length}} of 500 characters</span>
</div>
</div>
The display part is working fine. But the span has a count which id calculated on the fly and shows up in the UI based on what the textArea is inputed.
like for example shows 25 of 500 characters and as you keep typing the counter has to increase. I was able to do that for my static form as follows:
This one works in static form:
<div class="col-sm-10">
<textarea style="width:80%" #message rows="8" cols="75" maxlength="500"
formControlName="message" class="form-control" pInputTextArea ></textarea>
<span> {{message.value?.length || 0}} of 500 characters</span>
</div>
But the same concept doesnt work for dynamic form.{{e.boardAnswers.length}} gives me value only when the page loads up, however when i type in the textArea it doesnt increment.
How do I handle the same with dynamic forms. Suggestions please?

It's working script:
app.component.html
<form [formGroup]="afterActionReportForm">
<div class="row" *ngIf="reportList && reportList.length > 0" style="padding:10px;">
<div *ngFor="let e of reportList; let i = index" class="col-sm-6" style="margin-top: 2em;">
{{i+1}}. {{e.question}}
<textarea style="width:100%" #{{e.quesId}} rows="8" cols="75" maxlength="500" [formControlName]="e.quesId"
class="form-control" pInputTextArea ></textarea>
<span> {{afterActionReportForm.get(e.quesId.toString()).value.length}} of 500 characters</span>
</div>
</div>
</form>
app.component.ts
ngOnInit() {
this.afterActionReportForm = new FormGroup({});
this.reportList = this.data.reportList;
for (let i = 0; i < this.reportList.length; i++) {
const key = this.reportList[i].quesId+'';
this.afterActionReportForm.addControl(key, new FormControl(this.reportList[i].boardAnswers));
}
}
And more better create method for counting length
getStrLength(key: string | number): number {
return this.afterActionReportForm.get(key.toString()).value?.length || 0;
}
<span> {{getStrLength(e.quesId)}} of 500 characters</span>

I had same requirement. I used the getter method this.f to fetch the form controls. Compared the current quesId with the id from the object of controls to get the value and length. This works for me
Html
<form [formGroup]="afterActionReportForm">
<div class="row" *ngIf="reportList && reportList.length > 0" style="padding:10px;">
<div *ngFor="let e of reportList; let i = index" class="col-sm-6" style="margin-top: 2em;">
{{i+1}}. {{e.question}}
<textarea style="width:100%" #{{e.quesId}} rows="8" cols="75" maxlength="500" [formControlName]="e.quesId"
class="form-control" pInputTextArea ></textarea>
<span > {{getMyLength(e.quesId)}} of 500 characters</span>
</div>
</div>
</form>
TS
get f() { return this.afterActionReportForm.controls; }
getMyLength(id){
var len;
Object.entries(this.f).forEach(([key,value]) => {
if(key === id){
len= value.value.length;
}
});
return len;
}

Related

How to dynamically add a required HTML element to a form using javascript?

In the form I am making, there is a section that requires users to enter the amount of people in their family. After they provide it, the form generates enough input fields so that the user can enter information for each family member.
What I am having trouble with is none of the attributes that I am trying to apply to the input element actually work.
function addHouseMembers(that){
var family = document.getElementById("family-input-container");
while(family.hasChildNodes()){
family.removeChild(family.lastChild);
}
for(var i = 1; i < that.value; i++){
family.appendChild(document.createTextNode("Family Member " + (i+1)));
family.appendChild(document.createElement("br"));
//name
family.appendChild(document.createTextNode("Name: " ));
var input = document.createElement("input");
input.type = "text";
input.name = "member" + i + "_name";
input.pattern = "/^[a-zA-Z ]*$/";
input.required = true;
family.appendChild(input);
family.appendChild(document.createElement("br"));
}
}
The parameter that refers to the input where the user would put in the number of people in their family.
And here is the relevant HTML:
<form>
<div class="form-group">
<label class="col-lg-3 control-label">What is the total amount of people living in your household?</label>
<div class="col-lg-3 inputGroupContainer">
<div class = "input-group">
<input type="text" class="form-control" name="household-size" required onchange="addHouseMembers(this);"/>
</div>
</div>
</div>
<div class="form-group", id="family-info">
<label class="col-lg-12">Information for other Family Members</label>
<div class="col-lg-3 inputGroupContainer">
<div class = "input-group" id = "family-input-container" required>
</div>
</div>
</div>
</form>
The element shows up as it should, and is submitted with the form when the user hits the submit button, but the regex pattern and required attributes are not enforced.
in addHouseMembers(that) the value of that is a string, not a number, and you have to check if is value can be 'translated' in an integer value.
use the "onchange" event on the input field household-size is not a good idea because this event is triggered each time a digit of the number entered, which has the effect of erasing and completely rewrite the family-input-container part
I Imagine you are looking for something like that ?
const myForm = document.getElementById('my-form')
, familyElm = document.getElementById('family-input-container')
, parserDOM = new DOMParser()
;
function newHouseMember(ref)
{
let div=
` <div>
Family Member ${ref}<br>Name: <br>
<input type="text" name="member${ref}_name" pattern="/^[a-zA-Z ]*$/" required >
</div>`
return parserDOM.parseFromString( div, 'text/html').body.firstChild
}
myForm.btPeoples.onclick=_=>
{
let nbPeoples = parseInt(myForm['household-size'].value)
if (!isNaN(nbPeoples) && nbPeoples > 0 )
{
familyElm.innerHTML = ''
for (let i=1; i<=nbPeoples; i++)
{
familyElm.appendChild( newHouseMember(i) )
}
}
}
<link href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" rel="stylesheet" >
<form id="my-form">
<div class="form-group">
<label class="col-lg-3 control-label">What is the total amount of people living in your household?</label>
<div class="col-lg-3 inputGroupContainer">
<div class = "input-group">
<input class="form-control" name="household-size" required value="" placeholder="number of peoples" pattern="\d*" />
<button name="btPeoples" class="btn btn-info" type="button" >check it!</button>
</div>
</div>
</div>
<div class="form-group", id="family-info">
<label class="col-lg-12">Information for other Family Members</label>
<div class="col-lg-3 inputGroupContainer">
<div class="input-group" id="family-input-container">
</div>
</div>
</div>
</form>

How we can checked some checkboxes from the array In Angular 6

I have a form for editing data.In it there is some checkboxes. I get the previously selected checked box array[] from the DB I want to checked those check boxes as checked in the edit profile form. When we editing the page.
app.component.html file:
<form [formGroup]="editCategoryForm" >
<div class="form-group">
<mat-form-field>
<input matInput placeholder="Name" formControlName="name" >
</mat-form-field>
</div>
<div formArrayName="categoryArray" >
<fieldset *ngFor="let address of editCategoryForm.controls.categoryArray['controls']; let i = index" >
<div [formGroupName]="i" >
<div class="form-group">
<mat-form-field>
<input matInput placeholder="Label" formControlName ="label" required>
</mat-form-field>
<br/>
<div class="check-box" *ngFor="let data of measurementData">
<input type="checkbox" (change)="onChange(i,data._id,data.name, $event.target.checked)" > {{data.name}}
</div>
<div class="col-sm-1">
<button mat-mini-fab color="warn" *ngIf="editCategoryForm.controls.categoryArray['controls'].length > 1" title="Remove Fields" (click)="removeLair(i)">x</button>
</div>
</div>
</div>
</fieldset>
<br/>
<div class="form-group">
<button mat-raised-button color="accent" (click)="addNew()">Add Measurement</button>
</div>
<div class="form-group">
<button style="float: right;margin: 29px;" mat-raised-button color="primary" (click)="submitdata()">Submit</button>
</div>
</div>
</form>
I have this for capturing the array of measurements. that are in the DB:
this.category = {
"_id":"5c4b0d6918f72032c0569004",
"name":"categorytest",
"measurements": [{
"measurements": [
{"name":"Chest","id":"5c4ac1c4da2dfe251aeee037"},
{"name":"Stomach","id":"5c4ac1d6da2dfe251aeee038"},
{"name":"Hip","id":"5c4ac1dbda2dfe251aeee039"},
{"name":"Length","id":"5c4ac201da2dfe251aeee03c"}
],
"label":"testfff"
},
{
"measurements":[{"name":"Chest","id":"5c4ac1c4da2dfe251aeee037"}],
"label":"httt"
}]
}
app.component.ts File:
this.https.post<any>('api/category/details', data).subscribe(response => {
this.category = response.category;
this.editCategoryForm.controls['name'].setValue(this.category.name);
console.log(this.category);
console.log(this.category.measurements.length);
for (let i = 0; i < this.category.measurements.length; i++) {
if (i !== 0) {
const control = <FormArray>this.editCategoryForm.controls['categoryArray'];
control.push(this.getData());
}
this.categoryArray.at(i).get('label').setValue(this.category.measurements[i].label);
}
});
Here is a Stackblitz demo.
inputChecked(i,data){
let checked = false;
//console.log(this.category.measurements[i].measurements);
//console.log('data = ', data);
for (let l = 0; l < this.category.measurements[i].measurements.length; l++){
let temp = this.category.measurements[i].measurements[l];
//console.log('inside =',temp);
if (temp.name == data.name && temp.id == data._id){
checked = true; }
}
return checked;
}
put the above in your ts file, then reference it like so in your html file:
<input type="checkbox" (change)="onChange(i,data._id,data.name, $event.target.checked)" [checked]="inputChecked(i,data)"> {{data.name}}

trying to sum a list of key value pairs in javascript

I am doing a dumb little coding challenge that has me stumped. I am trying to take a list of key value pairs like such:
John: 2
Jane: 3
John: 4
Jane: 5
Your objective is to sum the counts for each key in the textarea, and display the totals for each
key within the HTML document. The default value should result in the output, "The total for
John is 6. The total for Jane is 8."
I am trying to do this with no jquery or any other framework Here is what I have so far and I keep getting NaN for the value. I maybe have my logic reversed?
$(function() {
var keyStore = document.getElementById("keyValPairs");
if (!keyStore){
alert("you suck DIE!!!!!");
}
var hashTable = {};
var str = split(",");
for( var entry in str){
var a = entry.split(":")
if(!hashTable.hasOwnProperty(a[0])){
hashTable[a[0]] = 0;
}
hashTable[a[0]] += parseInt(a[1]);
}
console.log(obj);
});
I used the jquery function to wrap it because I don"t remember what the regular way of saying when the dom is loaded.
Html:
<div class="panel panel-primary">
<div class="panel panel-header">
<h1>Sum up your key value pairs</h1>
</div>
<div class="panel panel-body">
<div class="col-sm-6">
<textarea class="preescreen-input" name="keyValPairs" id="keyValPairs" cols="30" rows="10">
John : 2,
Jane: 3,
John : 4,
Jane : 5,
</textarea>
</div>
<div class="col-sm-2">
<button class="btn btn-primary">Sum</button>
</div>
<div class="col-sm-2">
<button class="btn btn-danger">
Insert
</button></br></br>
<label for="inputBoxKey">Enter Key</label>
<input type="text" id="inputBoxKey" name="inputBoxKey"></br></br>
<label for="inputBoxVal">Enter Value</label>
<input type=" text" id=inputBoxVal name="inputBoxVal">
</div>
<div class="col-2-offset-4 panel panel-footer">
<input type="text ">
</div>
</div>
</div>
Its just a stupid little programming challenge that I found online but none the less It has me perplexed. Any help would be appreciated.
You're calling split(",") without giving it a string to split on
when you do for( var entry in str), entry is the key, not the value of the entry instr
You need to bind the sum button to a function to run the code
You should trim your strings to remove whitespace
You should make sure names are valid and bail if not (to deal with blank lines)
Here's your code with these changes:
<div class="panel panel-primary">
<div class="panel panel-header">
<h1>Sum up your key value pairs</h1>
</div>
<div class="panel panel-body">
<div class="col-sm-6">
<textarea class="preescreen-input" name="keyValPairs" id="keyValPairs" cols="30" rows="10">
John : 2,
Jane: 3,
John : 4,
Jane : 5,
</textarea>
</div>
<div class="col-sm-2">
<button class="btn btn-primary" onclick="sum()">Sum</button>
</div>
<div class="col-sm-2">
<button class="btn btn-danger">
Insert
</button></br></br>
<label for="inputBoxKey">Enter Key</label>
<input type="text" id="inputBoxKey" name="inputBoxKey"></br></br>
<label for="inputBoxVal">Enter Value</label>
<input type=" text" id=inputBoxVal name="inputBoxVal">
</div>
<div class="col-2-offset-4 panel panel-footer">
<input type="text ">
</div>
</div>
</div>
and js:
window.sum = function() {
var keyStore = document.getElementById("keyValPairs");
if (!keyStore){
alert("you suck DIE!!!!!");
}
var hashTable = {};
var str = keyStore.value.split(",");
for( var key in str){
var entry = str[key].trim();
var a = entry.split(":");
if(a.length > 0) a[0] = a[0].trim();
if(a.length > 1) a[1] = a[1].trim();
if(a[0] == '') continue;
if(!hashTable.hasOwnProperty(a[0])){
hashTable[a[0]]=0;
}
hashTable[a[0]]+=parseInt(a[1]);
}
alert(JSON.stringify(hashTable));
}

Get info from dynamic/growing form

Have a form to create a contract, where that contract can have one or more users associated.
The area to input the users info, starts with only one field of one user, and one button to add more fields if needed.
<div id="utilizadores" class="row">
<div id="utilizador1" class="container-fluid">
<div class="row">
<div class="col-lg-5">
<input type="text" class="form-control" id="nomeUtilizador1" placeholder="Nome Utilizador">
</div>
<div class="col-lg-6">
<input type="text" class="form-control" id="funcaoUtilizador1" placeholder="Função">
</div>
</div>
</div>
</div>
This is the starting div
Clicking on Add User button it adds a new div under the "utilizador1"
<div id="utilizadores" class="row">
<div id="utilizador1" class="container-fluid">
<div class="row">
<div class="col-lg-5">
<input type="text" class="form-control" id="nomeUtilizador1" placeholder="Nome Utilizador">
</div>
<div class="col-lg-6">
<input type="text" class="form-control" id="funcaoUtilizador1" placeholder="Função">
</div>
</div>
</div>
<div id="utilizador2" class="container-fluid">
<div class="row">
<div class="col-lg-5">
<input type="text" class="form-control" id="nomeUtilizador2" placeholder="Nome Utilizador">
</div>
<div class="col-lg-6">
<input type="text" class="form-control" id="funcaoUtilizador2" placeholder="Função">
</div>
</div>
</div>
My question is, how can I get the number of users created, and insert them into a list using Javascript. The list will be a attribute of a Object (Contract).
What i have til now:
function test_saveItem() {
var contract = new Object();
contract.Dono = <% =uID %>;
contract.BoostMes = $("#boostMes").val();
contract.BoostAno = $("#boostAno").val();
var ListaUtilizadores = [];
var divs = document.getElementsByName("utilizador");
for (var i = 0; i < divs.length; i++){
var user = new Object();
user.Nome = $('#nomeUtilizador' + i).val();
ListaUtilizadores.push(user);
}
var test = JSON.stringify({ "contract": contract });
}
Any help appreciated
Edit: Got to a solution thanks to Shilly
List = [];
Array.prototype.slice.call(document.querySelectorAll('.user')).forEach(function (node, index) {
List.push({
"name" : document.getElementById('nameUser' + (index + 1)).value,
"job" : document.getElementById('jobUser' + (index + 1)).value
});
});
Something like this? But adding it into the addUser function as Super Hirnet says, will be more performant.
var divs = document.querySelector('#utilizadores').childNodes,
users = [];
Array.slice.call(divs).forEach(function (node, index) {
users.push({
"name" : divs[index].getElementById('nomeUtilizador' + (index + 1)).value
});
});
You can have an empty array and on every click of addUser put a new object into the array. The object can have information related to the added user.

How to add HTML contents Dynamically on Button Click Event with AngularJS

I need to add HTML content on Button Click event using AngularJS. Is it possible??
My index.html
<div class="form-group">
<label for="category"> How Many Questions Want You Add ? </label>
<div class="col-sm-10">
<input type="text" class="form-control input-mini" id="questionNos" name="questionNos" placeholder="Nos." ng-model="myData.questionNos">
<div class="input-append">
<button class="btn-warning btn-mini" type="button" ng-click="myData.doClick()">Generate</button>
</div>
</div>
</div>
I want to add Nos. of HTML divs as per quantity added dynamically..
myApp.js
angular.module("myApp", []).controller("AddQuestionsController",
function($scope) {
$scope.myData = {};
$scope.myData.questionNos = "";
$scope.myData.doClick = function() {
//Do Something...????
};
});
It should be possible. I would data-bind your Divs to viewModel elements, and in your doClick function create the viewModels.
I would avoid directly creating Html in your viewModel.
For example:
<div class="form-group">
<label for="category"> How Many Questions Want You Add ? </label>
<div class="col-sm-10">
<input type="text" class="form-control input-mini" id="questionNos" name="questionNos" placeholder="Nos." ng-model="myData.questionNos">
<div class="input-append">
<button class="btn-warning btn-mini" type="button" ng-click="myData.doClick()">Generate</button>
</div>
<div ng-repeat="q in myData.questions">
<!-- BIND TO Q HERE -->
</div>
</div>
</div>
And in doClick:
$scope.myData.doClick = function() {
var newQuestions = getNewQuestionViewModels($scope.myData.questionNos);
for (var i = 0; i < newQuestions.length; i++) {
$scope.myData.questions.push(newQuestions[i]);
}
};
You have to store questions in collection and do repeat.
DEMO
HTML:
<div>
<input type="text" ng-model="data.qcount">
<button type="button" ng-click="data.add()">Add</button>
</div>
<div>
<div ng-repeat="q in data.questions track by $index">
<pre>{{ q | json }}</pre>
</div>
</div>
JS:
$scope.data = {
questions: [],
qcount: 0,
add: function() {
var dummy = {
'title': 'Q title',
'body': 'Q body'
},
newQ = [];
for (var i = 0; i < $scope.data.qcount; ++i) {
newQ.push(dummy);
}
$scope.data.questions = $scope.data.questions.concat(newQ);
$scope.data.qcount = 0;
}
};

Categories