问题
This is one of the page of my Angular app.
Inside the ngOnInit method, I call the api twice to get different data I need and loop through the data by using forEach method to build reactive form, but somehow, I encountered one of two different error.
This
enter image description here
and this
enter image description here
and sometime there is no error at all.
It would be grateful if someone can tell me what's wrong with my code.
here is part of my code
user-manager.component.ts
export class UserManagerComponent implements OnInit {
constructor(
private fb: FormBuilder
) {}
ngOnInit(): void {
this.commonData.getDropdownData('menus')
.pipe(takeWhile(() => this.alive))
.subscribe(r => {
if (r.status === 0) {
this.menuForm = this.fb.group({});
this.menuList = r.result.menus;
this.menuList.forEach(i => {
const control = new FormControl();
(this.menuForm as FormGroup).setControl(i.MenuId, control);
i.children.forEach(a => {
const con = new FormControl();
(this.menuForm as FormGroup).setControl(a.MenuId, con);
});
});
this.isMenuFormReady = true;
} else {
this.toastrService.danger(r.message);
}
},
error => {
if (!environment.production) {
this.menuForm = this.fb.group({});
this.menuList = menuMock.menus;
this.menuList.forEach(i => {
const control = new FormControl();
(this.menuForm as FormGroup).setControl(i.MenuId, control);
i.children.forEach(a => {
const con = new FormControl();
(this.menuForm as FormGroup).setControl(a.MenuId, con);
});
});
this.isMenuFormReady = true;
}
console.log(error);
});
this.commonData.getDropdownData('permissions')
.pipe(takeWhile(() => this.alive))
.subscribe(r => {
if (r.status === 0) {
this.permissionsForm = this.fb.group({});
this.permissionsList = r.result;
this.permissionsList = this.groupBy(this.permissionsList, function(item) {
return [item.GroupId];
});
this.permissionsList.forEach(i => {
const id = i[0].GroupId;
this.permissionsForm.addControl(id, this.fb.group({}));
i.forEach(a => {
const con = new FormControl();
(this.permissionsForm.get(id) as FormGroup).addControl(a.ActionId, con);
});
});
this.isPermissionFormReady = true;
} else {
this.toastrService.danger(r.message);
}
},
error => {
if (!environment.production) {
this.permissionsForm = this.fb.group({});
this.permissionsList = userMock.Permission;
this.permissionsList = this.groupBy(this.permissionsList, function(item) {
return [item.GroupId];
});
this.permissionsList.forEach(i => {
const id = i[0].GroupId;
// console.log('id: ', id)
this.permissionsForm.addControl(id, this.fb.group({}));
i.forEach(a => {
const con = new FormControl();
(this.permissionsForm.get(id) as FormGroup).addControl(a.ActionId, con);
});
});
this.isPermissionFormReady = true;
// console.log(this.permissionsForm);
}
console.log(error);
});
}
groupBy( array , f ) {
const groups = {};
array.forEach( function(o) {
const group = JSON.stringify( f(o) );
groups[group] = groups[group] || [];
groups[group].push( o );
});
return Object.keys(groups).map( function( group ) {
return groups[group];
});
}
}
user-manager.component.html
<nb-card>
<nb-card-header>
form1
</nb-card-header>
<nb-card-body>
<form [formGroup]="permissionsForm" autocomplete="off" *ngIf="isPermissionFormReady">
<nb-card *ngFor="let item of permissionsList; let i = index;" [formGroupName]="i">
<nb-card-header>
{{item[0].GroupName}}
</nb-card-header>
<nb-card-body class="pt-0">
<div class="row">
<div class="col-4 my-1" *ngFor="let permission of item">
<label>
<input type="checkbox" [formControlName]="permission.ActionId"/>
{{permission.ActionName}}
</label>
</div>
</div>
</nb-card-body>
</nb-card>
</form>
</nb-card-body>
</nb-card>
<nb-card>
<nb-card-header>
form2
</nb-card-header>
<nb-card-body>
<form [formGroup]="menuForm" autocomplete="off" *ngIf="isMenuFormReady">
<div class="row">
<div class="col-4" *ngFor="let menu of menuList" >
<nb-card>
<nb-card-header>
<label>
<input type="checkbox" id="{{menu.MenuId}}" [formControlName]="menu.MenuId" (change)="menuClick($event)"/>
{{menu.title}}
</label>
</nb-card-header>
<nb-card-body class="pt-0">
<div class="row">
<div class="col-12 my-1" *ngFor="let item of menu.children">
<label>
<input type="checkbox" id="{{item.MenuId}}" [formControlName]="item.MenuId" (change)="menuClick($event)"/>
{{item.title}}
</label>
</div>
</div>
</nb-card-body>
</nb-card>
</div>
</div>
</form>
</nb-card-body>
</nb-card>
回答1:
There is currently a bug in Chrome 80 which makes Array.reduce not to work according to the spec. So If you are using reactive form in your project you will face browser specific issue like ( form.get('key').value is undefined or valuechanges is undefined even if it exists), which was working fine in chrome 79. To fix this issue, add Array.reduce polyfill in your angular project manually as shown below.
Add this to main.ts
(function () {
function getChromeVersion() {
const raw = navigator.userAgent.match(/Chrom(e|ium)\/([0-9]+)\./);
return raw ? parseInt(raw[2], 10) : false;
}
const chromeVersion = getChromeVersion();
if (chromeVersion && chromeVersion >= 80) {
Array.prototype.reduce = function (callback /*, initialValue*/) {
'use strict';
if (this == null) {
throw new TypeError('Array.prototype.reduce called on null or undefined');
}
if (typeof callback !== 'function') {
throw new TypeError(callback + ' is not a function');
}
let t = Object(this), len = t.length >>> 0, k = 0, value;
if (arguments.length === 2) {
value = arguments[1];
} else {
while (k < len && !(k in t)) {
k++;
}
if (k >= len) {
throw new TypeError('Reduce of empty array with no initial value');
}
value = t[k++];
}
for (; k < len; k++) {
if (k in t) {
value = callback(value, t[k], k, t);
}
}
return value;
};
}
})();
ForMoreInfo
来源:https://stackoverflow.com/questions/60221545/add-formcontrol-accroding-to-the-data-returned-from-backend-but-got-two-differen