Angular 4 Nested FormArrays Cannot read property 'push' of null - javascript

I cannot push to an array.
i have a segment(id, time) that can have one or several people (role, infos). the field are generated dynamically. On load the page shows the fields (see image below).
when try to add a person I get error : ERROR TypeError: Cannot read property 'push' of null
Here is the .ts code:
addPerson(index: number) {
//let personRows3 = <FormArray>this.mySummaryForm.get('personRows3');
let personRows3 = <FormArray>this.mySummaryForm.get(`segmentRows3.${index}.personRows3`)
personRows3.push(this.fb.group({
personR3: '',
personI3: ''
}));
}
segmentRows3: this.fb.array([
this.fb.group({
segmentId3: '',
segmentTime3: '',
personRows3: this.fb.array([
this.fb.group({
personR3: '',
personI3: ''
})
])
})
]),
The .html code
<div formArrayName="segmentRows3">
<div *ngFor=" let segmentRow of mySummaryForm.controls.segmentRows3.controls; let i=index " >
<div class="form-group" [formGroupName]="i" > {{i+1}}
<label for="segmentId3">Segment ID
<select formControlName="segmentId3" formControlName="segmentId3" placeholder="pick" type="text" id="segmentId3" class="form-control" [(ngModel)]="levelNumSegment3" (ngModelChange)="toNumberSegment3()">
<option *ngFor="let level of segmentId" [value]="level.num">{{level.name}}</option>
</select>
</label>
<label for="segmentTime3">Segment time
<input formControlName="segmentTime3" type="text" id="segmentTime3" class="form-control" placeholder="select a time" (ngModelChange)="onChange($event)">
</label>
<div formArrayName="personRows3">
<div *ngFor=" let personRow of segmentRow.controls.personRows3.controls; let j=index " >
<div class="form-group" [formGroupName]="j" > {{j+1}}
<label for="personR3">person Role
<input formControlName="personR3" [typeahead]="personRole" [typeaheadOptionsLimit]="10" [typeaheadMinLength]="0" type="text" id="personR3" class="form-control" placeholder="select a role" (ngModelChange)="onChange($event)" >
</label>
<label for="personI">Person infos
<input formControlName="personI3" [typeahead]="states" [typeaheadOptionsLimit]="10" [typeaheadMinLength]="0" type="text" id="personI3" class="form-control" placeholder="select infos" (ngModelChange)="onChange($event)" >
</label>
<label><span (click)="deletePerson(j)" class="btn btn-danger">Remove</span></label>
</div>
</div>
</div>
<br><button type="button" (click)="addPerson(j)" class="btn btn-info">Add a person</button><br><br><br>
</div>
</div>
</div>

The above error will occur when your personRows3 is null or don't have elements.
Add a check before pushing the elements,
if(personRows3){
personRows3.push(this.fb.group({
personR3: '',
personI3: ''
}));
}

Related

getElementById return empty values so post empty values

I'm working with DOM and web API to POST some information about the company like name, worker's name.
But when I write something in the input DOM can't reach the value and return empty so I post an empty object.
That looks like :
adress: ""
companyName: ""
contactName: ""
contactTitle: ""
My form block:
<form>
<div class="form-group">
<label for="">Company Name</label>
<input
type="text"
class="form-control"
id="companyName"
placeholder="Company Name!"
/>
</div>
<div class="form-group">
<label for="">Contact Name</label>
<input
type="text"
class="form-control"
id="contactName"
placeholder="Contact Name!"
value=""
/>
</div>
<div class="form-group">
<label for="">Contact Title</label>
<input
type="text"
class="form-control"
id="contactTitle"
placeholder="Contact Title!"
/>
</div>
<div class="form-group">
<label for="">Country</label>
<input
type="text"
class="form-control"
id="inputCountry"
placeholder="Country!"
/>
</div>
</form>
And my JS code:
'use strict';
let inputCompanyName = document.getElementById('companyName');
let inputContactName = document.getElementById('contactName');
let inputContactTitle = document.getElementById('contactTitle');
let country = document.getElementById('inputCountry');
const btnSubmit = document.getElementById('submit');
let newCompany = {
companyName: inputCompanyName.value,
contactName: inputContactName.value,
contactTitle: inputContactTitle.value,
adress: country.value,
};
btnSubmit.addEventListener('click', e => {
e.preventDefault();
axios
.post('https://northwind.vercel.app/api/suppliers', newCompany)
.then(res => {
console.log('Response', res.data);
alert('Success!');
});
});
I tried innerHTML and innerText and form method but I cant solve this problem.
You're reading the values immediately upon loading the page, long before the user has had a chance to enter any values.
Instead, read the values in the click event:
btnSubmit.addEventListener('click', e => {
let newCompany = {
companyName: inputCompanyName.value,
contactName: inputContactName.value,
contactTitle: inputContactTitle.value,
adress: country.value,
};
// the rest of the click handler logic...
});

Vuejs get index in multiple input forms

I have an array of strings like:
questions: [
"Question 1?",
"Question 2?",
"Question 3?",
"Question 4?",
],
Then I have form fields in my data() like:
legitForm: {
name: '', // this will be equal to question string
answer: '',
description: '',
duration: '',
},
Now, the problem I'm facing is when I fill inputs for any of questions
above the same field for other questions gets same value.
Here is my template code:
<div v-for="(question, index) in questions" :key="index">
<form #submit.prevent="legitStore(question)" method="post">
<div class="row">
<div class="col-md-12">
<p>
<strong>{{question}}</strong>
</p>
</div>
<div class="col-md-6">
<label for="answer">Answer</label>
<select class="mb-1 field" v-model="legitForm.answer" name="answer" id="answer">
<option value="1">Yes</option>
<option value="0">No</option>
</select>
</div>
<div class="col-md-6">
<label for="duration">Duration</label>
<input class="field" v-model="legitForm.duration" type="text">
</div>
<div class="col-md-12">
<label for="description">Description</label>
<textarea style="height: 190px;" type="text" cols="5" rows="10" id="address" class="mb-1 field" v-model="legitForm.description"></textarea>
</div>
</div>
<button type="submit" class="saveButtonExperience float-right btn btn--custom">
<span class="text">Save</span>
</button>
</form>
</div>
And this is my post method that sends data to backend:
legitStore(question) {
this.legitForm.name = question; // set "name" in `legitForm` to value of `question` string
axios.post('/api/auth/userLegitsStore', this.legitForm, {
headers: {
Authorization: localStorage.getItem('access_token')
}
})
.then(res => {
// reset my data after success
this.legitForm = {
name: '',
answer: '',
description: '',
duration: '',
};
})
.catch(error => {
var errors = error.response.data;
let errorsHtml = '<ol>';
$.each(errors.errors,function (k,v) {
errorsHtml += '<li>'+ v + '</li>';
});
errorsHtml += '</ol>';
console.log(errorsHtml);
})
},
Here is issue screenshot:
Note: I've tried to
change legitForm to array like legitForm: [], and legitForm: [{....}]
add index to my inputs v-model
but I've got errors so i wasn't sure what I'm doing wrong, that's why
I'm asking here.
If you think of your questions as questions and answers, you can do something like this:
questions: [
{
question: 'Question 1',
answer: null,
description: null,
duration: null,
},
{
question: 'Question 2',
answer: null,
description: null,
duration: null,
},
]
Then when looping through your form, it would be more like this:
<div v-for="(question, index) in questions" :key="index">
<form #submit.prevent="legitStore(question)" method="post">
...
<select class="mb-1 field" v-model="question.answer" name="answer" id="answer">
<option value="1">Yes</option>
<option value="0">No</option>
</select>
...
</form>
</div>
And in the storing function you could send the data in the question instead of this.legitForm
Like you said you tried here:
change legitForm to array like legitForm: [], and legitForm: [{....}]
add index to my inputs v-model
You are supposed to be doing that.
I would change legitForm to:
//create legitForms equal to the length of questions
const legitForms = [];
for (i in questions) legitForms.push(
{
name: '', // this will be equal to question string
answer: '',
description: '',
duration: '',
}
);
and in template:
<div v-for="(question, index) in questions" :key="index">
<form #submit.prevent="legitStore(question)" method="post">
<div class="row">
<div class="col-md-12">
<p>
<strong>{{question}}</strong>
</p>
</div>
<div class="col-md-6">
<label for="answer">Answer</label>
<select class="mb-1 field" v-model="legitForms[index].answer" name="answer" id="answer">
<option value="1">Yes</option>
<option value="0">No</option>
</select>
</div>
<div class="col-md-6">
<label for="duration">Duration</label>
<input class="field" v-model="legitForms[index].duration" type="text">
</div>
<div class="col-md-12">
<label for="description">Description</label>
<textarea style="height: 190px;" type="text" cols="5" rows="10" id="address" class="mb-1 field" v-model="legitForms[index].description"></textarea>
</div>
</div>
<button type="submit" class="saveButtonExperience float-right btn btn--custom">
<span class="text">Save</span>
</button>
</form>
</div>
<div v-for="(question, index) in questions" :key="index">
In your template you iterate through questions and within this tag render object legitForm it all questions will refer to the same 1 object that's why all question have the same data.
You should have had create an array of question contains it own question's content like
<template>
<div v-for="(question, index) in questions" :key="index">
<form #submit.prevent="legitStore(question)" method="post">
...
<div class="col-md-12">
<p>
<strong>{{question.id}}</strong>
</p>
</div>
...
<select class="mb-1 field" v-model="question.answer" name="answer" id="answer">
<option value="1">Yes</option>
<option value="0">No</option>
</select>
...
</form>
</div>
</template>
<script>
class QuestionForm {
// pass default value if you want
name = ''
answer = ''
description = ''
duration = ''
id = ''
constructor(form) {
Object.assign(this, form)
}
}
function initQuestions(num) {
// can generate a Set object cause question are unique
return Array.from({ length: num }, (v, i) => i).map(_j => new QuestionForm())
}
export default {
data() {
return {
questions: initQuestions(5), // pass number of question you want to generate
}
}
}
</script>

When the Api returned data,the data is not able to be set in the Object's datatype in angular

I have a normal input fields like this:
When i click on that "+" button this action happens and the service class is called as I have simple Json data arriving.I want to assign the selectionCustomOffice.custOfficeName=json data's.custOffcName; but I am getting the undefined result.
addedO(selectionCustomOffice:SelectionCustomOffice){
this.scoe=true;
this.selectionCustomOfficeService.getSingleCustomOffice(selectionCustomOffice.customOfficeId).subscribe((data)=>{
console.log("entered");
selectionCustomOffice.custOfficeName=data.custOffcName;
console.log( selectionCustomOffice.custOfficeName);
},(error)=>{
console.log(error);
});
this.empList.push(selectionCustomOffice);
this.selectionCustomOffice=new SelectionCustomOffice();
console.log(this.empList);
}
this.selectionCustomOfficeService.getSingleCustomOffice(selectionCustomOffice.customOfficeId).subscribe((data)=>{
console.log("entered");
selectionCustomOffice.custOfficeName=data.custOffcName;
console.log( selectionCustomOffice.custOfficeName);
},(error)=>{
console.log(error);
});
SelectionCustomOffice.ts
export class SelectionCustomOffice {
id: number;
fromDate: string;
toDate: string;
consignmentNo: number;
selectionId: number;
customOfficeId: number;
custOfficeName: string;
}
The form to send data is: I have used this field custom office as select field.
<div class="form-group row">
<div class="col-md-4">
<label>Custom Office</label>
</div>
<div class="col-md-2">
<label>From Date</label>
</div>
<div class="col-md-2">
<label>To Date</label>
</div>
<div class="col-md-4">Consignment No</div>
<div class="col-md-4">
<select class="form-control" id="customOfficeId" required [(ngModel)]="selectionCustomOffice.customOfficeId" name="customOfficeId"
>
<option *ngFor="let title of customoffices" [value]="title.custOfficeId">{{title.custOffcName}}</option>
</select>
</div>
<div class="col-md-2">
<input type="date" class="form-control" id="fromDate" required [(ngModel)]="selectionCustomOffice.fromDate" name="fromDate" />
</div>
<div class="col-md-2">
<input type="date" class="form-control" id="toDate" required [(ngModel)]="selectionCustomOffice.toDate" name="toDate" />
</div>
<div class="col-md-3">
<input type="number" class="form-control" id="consignmentNo" required [(ngModel)]="selectionCustomOffice.consignmentNo" name="consignmentNo">
</div>
<div class="col-md-1">
<button type="button" (click)="addedO(selectionCustomOffice)">+</button>
</div>
</div>
service class
getSingleCustomOffice(id: number): Observable<any> {
return this.http.get(`${this.baseUrl}/${id}`, { responseType: 'text' });
}
I think you have a small problem - You are reading your response as text
getSingleCustomOffice(id: number): Observable<any> {
return this.http.get(`${this.baseUrl}/${id}`, { responseType: 'text' });
}
Remove the { responseType: 'text' } from the http call or use JSON.parse(data) when you are reading data - { responseType: 'text' } this will return your response as string
Try like this if you want your response as text
this.selectionCustomOfficeService.getSingleCustomOffice(selectionCustomOffice.customOfficeId).subscribe((data)=>{
console.log("entered");
let outPut = JSON.parse(data);
selectionCustomOffice.custOfficeName=outPut.custOffcName;
console.log( selectionCustomOffice.custOfficeName);
},(error)=>{
console.log(error);
});
I think this might solve your problem - Happy coding !!

Angular form submitting

I'm new in programming and need a little advice.
I create two tables in my database - Employee (with foreign key "emp_depId") and Department with values (HR, Tech, Finance). In order to create an employee I need emp_depId, but from select in my form I get depName. What can I do? I must send a request to the server and get all departments from DB and than in data binding call method with "depName" argument, which return me necessary Id. Than I'll assign it to employee.emp_DepId and save.
Here is my form:
<form novalidate #form="ngForm" (ngSubmit)="submitForm(form)" (reset)="resetForm()" >
<div class="form-group">
<label>First Name</label>
<input class="form-control" name="firstName" [(ngModel)]="employee.firstName" required />
</div>
<div class="form-group">
<label>Last Name</label>
<input class="form-control" name="lastName" [(ngModel)]="employee.lastName" required />
</div>
<div class="form-group">
<label>Department</label>
<select class="form-control" name="depName" [(ngModel)]="getDepId(department.depName)">
<option *ngFor="let depName of getDepartments(); let i = index" [value] = "depName[i]">
{{depName}}
</option>
</select>
</div>
<button type="submit" class="btn btn-primary"
[class.btn-warning]="editing" [disabled]="form.invalid">
{{editing ? "Save" : "Create"}}
</button>
<button type="reset" class="btn btn-secondary" routerLink="/">Cancel</button>
</form>
Am I right? Thank you.
As per your code you can do it like :
<select class="form-control" name="emp_DepId" [(ngModel)]="employee.emp_DepId">
<option *ngFor="let depName of getDepartments(); let i = index" [value] = "getDepId(depName)">
{{depName}}
</option>
</select>
But ideally it should be like this :
// output of getDepartments() should look like this
[
{ id : 1 , name : 'department 1'},
{ id : 2 , name : 'department 2'}
{ id : 3 , name : 'department 3'}
]
<select class="form-control" name="emp_DepId" [(ngModel)]="employee.emp_DepId">
<option *ngFor="let dep of getDepartments(); let i = index" [value] = "dep.id">
{{dep.name}}
</option>
</select>

AngularJS - Conditionally hide a span

I want to hide the <span ng-show="currencyObject.to != 'undefined'">=</span> until the currencyObject.to is undefined which is supposed to be undefined until the user select an option from the select box.
I used ng-show="currencyObject.to != 'undefined'" to conditionally show-hide the span but it is not working. What I find is, when the page is freshly loaded, the = is visible.
<div class="row" ng-controller="QConvertController">
<div class="col-md-8 col-md-offset-2">
<div class="form-group">
<label for="amount">Amount</label>
<input type="number" step="any" class="form-control" id="amount" ng-model="currencyObject.amount">
</div>
</div>
<div class="col-md-8 col-md-offset-2">
<div class="form-group">
<label for="from">From</label>
<select class="form-control" id="from" ng-model="currencyObject.from" ng-change="getconvertedrate()">
<option ng-repeat="currencyCode in currencyCodes" value="{{currencyCode.value}}">{{currencyCode.display}}</option>
</select>
</div>
</div>
<div class="col-md-8 col-md-offset-2">
<div class="form-group">
<label for="to">To</label>
<select class="form-control" id="to" ng-model="currencyObject.to" ng-change="getconvertedrate()">
<option ng-repeat="currencyCode in currencyCodes" value="{{currencyCode.value}}">{{currencyCode.display}}</option>
</select>
</div>
</div>
<br>
<br>
<br>
<div class="col-md-8 col-md-offset-2">
<h1 class="display-4">{{currencyObject.amount}} {{currencyObject.from}} <span ng-show="currencyObject.to != 'undefined'">=</span> {{currencyObject.amount_converted}} {{currencyObject.to}}</h1>
</div>
</div>
QConvertController.js
var app = angular.module('qconvertctrlmodule', [])
.controller('QConvertController', function($scope, $http, $log) {
$scope.currencyObject = {};
$scope.currencyObject.from;
$scope.currencyObject.to;
$scope.currencyObject.amount;
$scope.currencyObject.amount_converted;
$scope.currencyObject.runCount = 0;
$scope.currencyCodes = [{value : 'INR', display : 'Indian Rupee'}, {value : 'USD', display : 'US Dollar'}, {value : 'GBP', display : 'British Pound'}];
$scope.getconvertedrate = function() {
$log.info("FROM : " + $scope.currencyObject.from);
$log.info("TO : " + $scope.currencyObject.to);
$http.get("http://api.decoded.cf/currency.php", {params : {from : $scope.currencyObject.from,
to : $scope.currencyObject.to, amount : $scope.currencyObject.amount}})
.then(function(response) {
$scope.currencyObject.amount_converted = response.data.amount_converted;
$log.info(response.data.amount_converted);
});
};
});
You don't need != 'undefined' to check the variable is defined or not
<span ng-show="currencyObject.to">=</span>
or
<span ng-hide="!currencyObject.to">=</span>
or
<span ng-if="currencyObject.to">=</span>
You can directly use as ng-show="currencyObject.to"
Also in Js correct usage of comparing with undefined is
if(typeof variable !== 'undefined'){ /*...*/ }

Categories