form
form copied to clipboard
Error while binding array
This is a...
- bug report
What toolchain are you using for transpilation/bundling?
- angular/cli
Environment
NodeJS Version: 6.10 Typescript Version: 2.3.0 Angular Version: 4.0.3 angular-redux/store version: 6.2.0 angular/cli version: 1.0.0-beta.32.3 OS: windows 10
Getting error Cannot read property 'arrivalTime' of undefined
The problematic areas are surrounded by comment ERROR HERE
my html:
<div class="form-panel">
<div class="steps-panel">
<div class="step" *ngFor="let step of steps; let i = index; let isLast = last" [ngClass]="{current: i + 1 == (addClientForm$ | async)?.currentPosition, pass: i + 1 < (addClientForm$ | async)?.currentPosition}">
<div class="circle-holder">
<div class="circle">
<span *ngIf="i + 1 >= (addClientForm$ | async)?.currentPosition">{{i + 1}}</span>
<md-icon *ngIf="i + 1 < (addClientForm$ | async)?.currentPosition">done</md-icon>
</div>
<div class="label">{{step}}</div>
</div>
<div *ngIf="!isLast" class="line"></div>
</div>
</div>
<ng-container [ngSwitch]="(addClientForm$ | async)?.currentPosition">
<form class="form" *ngSwitchCase="1" [connect]="['hqAddClientForm', 'clientDetails']">
<div class="regular-input-holder">
<div class="label">שם העסק</div>
<input type="text" name="businessName" ngControl ngModel>
</div>
<div class="regular-input-holder">
<div class="label">מספר העסק</div>
<input type="text" name="businessNumber" ngControl ngModel>
</div>
<div class="regular-input-holder">
<div class="label">כתובת</div>
<input type="text" name="address" ngControl ngModel>
</div>
<div class="regular-input-holder">
<div class="label">טלפון</div>
<input type="text" name="phone" ngControl ngModel>
</div>
<div class="regular-input-holder">
<div class="label">פקס</div>
<input type="text" name="fax" ngControl ngModel>
</div>
<div class="save-and-continue">
<button md-raised-button class="md-primary-button" (click)="saveContinue()">
<md-icon>done</md-icon>שמור והמשך
</button>
</div>
</form>
<form class="form" *ngSwitchCase="2" [connect]="['hqAddClientForm', 'paymentDetails']">
<div class="regular-input-holder">
<div class="label">אופן תשלום</div>
<input type="text" name="paymentMethod" ngControl ngModel>
</div>
<div class="regular-input-holder">
<div class="label">אחוזי הנחה</div>
<input type="text" name="discountPercentage" ngControl ngModel>
</div>
<div class="save-and-continue">
<button md-raised-button class="md-primary-button" (click)="saveContinue()">
<md-icon>done</md-icon>שמור והמשך
</button>
</div>
</form>
<ng-container *ngSwitchCase="3">
<form class="form" [connect]="['hqAddClientForm', 'managers', 'tmp']">
<div class="regular-input-holder">
<div class="label">שם ושם משפחה</div>
<input type="text" name="firstAndLastName" ngControl ngModel>
</div>
<div class="regular-input-holder">
<div class="label">אימייל</div>
<input type="email" name="email" ngControl ngModel>
</div>
<div class="regular-input-holder">
<div class="label">נייד</div>
<input type="phone" name="phone" ngControl ngModel>
</div>
<div class="save-and-continue">
<button md-raised-button class="md-primary-button" (click)="addManager()">
<md-icon>add</md-icon>הוספת מנהל
</button>
</div>
</form>
<div class="managers">
<div class="manager" *ngFor="let manager of (addClientForm$ | async)?.managers?.managers">
<div class="info">
<div class="full-name">{{manager.firstAndLastName}}</div>
<div class="phone-email">
<span>{{manager.phone}}</span>
<span>{{manager.email}}</span>
</div>
</div>
<md-icon class="edit">edit</md-icon>
</div>
</div>
<div class="save-and-continue">
<button md-raised-button class="md-primary-button" (click)="saveContinue()">
<md-icon>done</md-icon>שמור והמשך
</button>
</div>
</ng-container>
<div class="system-type form" *ngSwitchCase="4">
<div class="switch-buttons">
<div class="button-option" [ngClass]="{clicked: (addClientForm$ | async)?.clientSystemType?.type === 'shifts'}" (click)="toggleSystemType()">משמרות</div>
<div class="button-option" [ngClass]="{clicked: (addClientForm$ | async)?.clientSystemType?.type === 'general'}" (click)="toggleSystemType()">כללי</div>
</div>
<form *ngIf="(addClientForm$ | async)?.clientSystemType?.type === 'general'" class="form" [connect]="['hqAddClientForm', 'clientSystemType', 'general']">
<div class="regular-input-holder">
<div class="label">סוג לוח</div>
<input type="text" name="calendarType" ngControl ngModel>
</div>
</form>
<form *ngIf="(addClientForm$ | async)?.clientSystemType?.type === 'shifts'" class="form" [connect]="['hqAddClientForm', 'clientSystemType', 'shiftsType']">
<!-- ERROR HERE -->
<ng-template connectArray let-index connectArrayOf="shifts">
<div class="regular-input-holder" [ngModelGroup]="index">
<div class="header">משמרת {{index + 1}}</div>
<select ngControl ngModel name="arrivalTime">
<option *ngFor="let num of times" [value]="num">{{num}}</option>
</select>
<select ngControl ngModel name="departureTime">
<option *ngFor="let num of times" [value]="num">{{num}}</option>
</select>
</div>
</ng-template>
<!-- END ERROR HERE -->
</form>
<div class="save-and-continue">
<button md-raised-button class="md-primary-button" (click)="saveContinue()">
<md-icon>done</md-icon>שמור והמשך
</button>
</div>
</div>
</ng-container>
</div>
The rest of the forms are working fine
here is my interface:
export interface HQAddClientForm {
currentPosition: number,
clientDetails: {
businessName: string,
businessNumber: number,
// belongsTo: string,
// businessType: string,
address: string,
phone: string,
fax: string,
},
paymentDetails: {
paymentMethod: string,
discountPercentage: number
},
managers: {
tmp: HQClientManager,
managers: HQClientManager[]
},
clientSystemType: {
type: 'general' | 'shifts',
generalType: {
calendarType: string
},
// --- ERROR HERE ---
shiftsType: {
numberOfShifts: number,
shifts: {
arrivalTime: string,
departureTime: string
}[]
}
// --- END ERROR HERE ---
},
routes: {
tmp: HQClientRoute,
routes: HQClientRoute[]
}
}
export interface HQClientManager {
firstAndLastName: string,
email: string,
phone: string
}
export interface HQClientRoute {
origin: string[],
destination: string[],
totalKM: number,
permanentDriver: {
driverKey: string,
name: string
},
priceExpiritionDate: string,
driverPayment: {
amount: number,
incluedTax: boolean
},
totalPayment: {
amount: number,
incluedTax: boolean
}
}
export const initHQAddClientForm: HQAddClientForm = {
currentPosition: 1,
clientDetails: {
businessName: null,
businessNumber: null,
// belongsTo: null,
// businessType: null,
address: null,
phone: null,
fax: null,
},
paymentDetails: {
paymentMethod: null,
discountPercentage: null
},
managers: {
tmp: {
firstAndLastName: null,
email: null,
phone: null
},
managers: []
},
clientSystemType: {
type: 'shifts',
generalType: {
calendarType: null
},
// --- ERROR HERE ---
shiftsType: {
numberOfShifts: null,
shifts: [{
arrivalTime: null,
departureTime: null
}]
}
// --- END ERROR HERE ---
},
routes: {
tmp: {
origin: [],
destination: [],
totalKM: 0,
permanentDriver: {
driverKey: null,
name: null
},
priceExpiritionDate: null,
driverPayment: {
amount: 0,
incluedTax: null
},
totalPayment: {
amount: 0,
incluedTax: null
}
},
routes: []
}
}
this is the stack trace:
core.es5.js:1084 ERROR Error: Uncaught (in promise): TypeError: Cannot read property 'arrivalTime' of undefined
TypeError: Cannot read property 'arrivalTime' of undefined
at http://localhost:4200/vendor.bundle.js:30431:22
at http://localhost:4200/vendor.bundle.js:30356:66
at Array.forEach (native)
at FormGroup._forEachChild (http://localhost:4200/vendor.bundle.js:30356:36)
at FormGroup._checkAllValuesPresent (http://localhost:4200/vendor.bundle.js:30430:14)
at FormGroup.setValue (http://localhost:4200/vendor.bundle.js:30237:14)
at http://localhost:4200/vendor.bundle.js:30936:18
at ZoneDelegate.invoke (http://localhost:4200/vendor.bundle.js:156556:26)
at Object.onInvoke (http://localhost:4200/vendor.bundle.js:8978:37)
at ZoneDelegate.invoke (http://localhost:4200/vendor.bundle.js:156555:32)
at http://localhost:4200/vendor.bundle.js:30431:22
at http://localhost:4200/vendor.bundle.js:30356:66
at Array.forEach (native)
at FormGroup._forEachChild (http://localhost:4200/vendor.bundle.js:30356:36)
at FormGroup._checkAllValuesPresent (http://localhost:4200/vendor.bundle.js:30430:14)
at FormGroup.setValue (http://localhost:4200/vendor.bundle.js:30237:14)
at http://localhost:4200/vendor.bundle.js:30936:18
at ZoneDelegate.invoke (http://localhost:4200/vendor.bundle.js:156556:26)
at Object.onInvoke (http://localhost:4200/vendor.bundle.js:8978:37)
at ZoneDelegate.invoke (http://localhost:4200/vendor.bundle.js:156555:32)
at resolvePromise (http://localhost:4200/vendor.bundle.js:156934:31)
at http://localhost:4200/vendor.bundle.js:156985:17
at ZoneDelegate.invokeTask (http://localhost:4200/vendor.bundle.js:156589:31)
at Object.onInvokeTask (http://localhost:4200/vendor.bundle.js:8969:37)
at ZoneDelegate.invokeTask (http://localhost:4200/vendor.bundle.js:156588:36)
at Zone.runTask (http://localhost:4200/vendor.bundle.js:156356:47)
at drainMicroTaskQueue (http://localhost:4200/vendor.bundle.js:156749:35)
at <anonymous>
Thought
When I change the value in the problematic
Thanks
I am also experiencing this in a similar scenario. I have a parent form that is nested three layers deep in the state tree, which contains an array of child subforms. During the FORM_CHANGED
action, the changes from the child form are placed in the parent form instead. Below I'll put a simplified version of the form, which should illustrate the general problem.
I have a parent form at the path: ['workflow', 'page1', 'form']
.
It contains an array of child forms, childForms
, which is therefore at the path ['workflow', 'page1', 'form', 'childForms']
.
This child array is populated with a default first value before the page loads, so my state tree before the FORM_CHANGED
event ostensibly looks like the following:
{
workflow: {
page1: {
form: {
input1: "loadedInput",
childForms: [
{
childInput1: "loadedChildInput1"
}
]
}
}
}
}
Following this generic example, in the HTML template, I have:
<form [connect]="['workflow', 'page1', 'form']">
<input type="text"
name="input1"
ngControl ngModel>
<ng-template connectArray let-index connectArrayOf="childForms">
<div class="row" [ngModelGroup]="index">
<input type="text"
name="childInput1"
ngControl ngModel>
</div>
</ng-template>
</form>
NOTE: While the documentation shows the connect
directive invoked without the brackets, I have tried it with and without them and the same error occurs in either case.
After the FORM_CHANGED
event that occurs on the page load, the state tree from above looks like the following:
{
workflow: {
page1: {
form: {
0: {
childInput1: "loadedChildInput1"
},
input1: "loadedInput",
childForms: [
{
childInput1: "loadedChildInput1"
}
]
}
}
}
}
Right before this initial FORM_CHANGED
event, I see the exact same error that @yonathan06 posted (with a different field name, obviously).
So, to me, it seems that the reducer is posting the changed input value to the parent state, not the child state. Perhaps this is not a problem if the parent state is at the root of the state tree, as is the case in the example from the docs? But even if that is the case, it should be fixed so it works with nested states as well.
I am having this issue as well. The parent state is nested one level down the state tree, so it looks like this is the source of the problem. Any clues? Thanks!
@jkachurek if you do the following you can get it at the right level
<form [connect]="['workflow', 'page1', 'form']">
<input type="text"
name="input1"
ngControl ngModel>
<ng-template connectArray let-index connectArrayOf="childForms">
<div [ngModelGroup]="'childForms'"> <!-- <-- Add this-->
<div class="row" [ngModelGroup]="index">
<input type="text"
name="childInput1"
ngControl ngModel>
</div>
</div>
</ng-template>
</form>
This works, However if your loading dyncamic content from the server it always seems to set your values back to empty. But at least its poiting at the correct level
Hi guys. Im facing the very same issue, only somewhat later. Are there any solution to this? @el-davo's solution didn't work for me :/