我正在开发一个Angular 应用程序,它有一个带有我需要遵循的预定义类的体系结构。我显示了一个地址列表,并添加了CRUD功能。
我遇到的问题是在编辑地址时。填充下拉列表的数据通过对.Net核心后端的API调用来获得。我认为我遇到的问题是在数据从后端到达之前访问数据/属性。问题是,当得到错误时,包含国家和城市的下拉列表会被填充,但正确的值(正在编辑的记录的国家和城市)不会自动选择。
单击“编辑”按钮后出现此错误。有趣的是,当编辑窗口已经打开,我再次点击编辑,所有的工作正常,没有错误。正确的值也是从下拉列表中选择的。
错误:
TypeError: Cannot read properties of undefined (reading 'nativeElement')
at push.wnGv.SelectComponent.writeValue (select.component.ts:42:6)
at shared.ts:122:24
at model.ts:1188:25
at Array.forEach (<anonymous>)
at FormControl.setValue (model.ts:1187:22)
at model.ts:1520:27
at Array.forEach (<anonymous>)
at FormGroup.setValue (model.ts:1518:24)
at NgForm.setValue (ng_form.ts:283:18)
at eloket-form.ts:10:5
Angular代码:adres-form.component.ts:
export interface FlowbackPersoneelslidAdresForm {
straatLijn: string;
straatLijn2: string;
nummer: number;
bus: number;
codeGemeente: CodeGemeente;
codeAdresType: CodeAdrestype;
codeLand: CodeLand;
//isPrive: boolean;
isCorrespondentie: boolean;
beginDatum: moment.Moment;
eindDatum: moment.Moment;
attest: any;
}
@Component({
selector: 'app-adres-form',
templateUrl: './adres-form.component.html',
styleUrls: ['./adres-form.component.scss']
})
export class AdresFormComponent implements OnInit {
@ViewChild('f', { static: true }) FlowbackPersoneelslidAdresForm: EloketForm<FlowbackPersoneelslidAdresForm>;
@Input() serverValidationError: ValidationBag;
@Input() submitButtonText: string;
@Input() set flowBackPersoneelslidAdres(value: FlowbackPersoneelslidAdres) {
if (!value) {
return;
}
console.log('before setFormValue, value passed to setFormValue:', value);
console.log(value.codeAdresType);
setFormValue(this.FlowbackPersoneelslidAdresForm, {
straatLijn: value.straat,
straatLijn2: value.straatLijn2,
nummer: value.huisNummer,
bus: value.busNummer,
codeAdresType: value.codeAdresType,
codeGemeente: value.codeGemeente,
codeLand: value.codeLand,
beginDatum: moment(value.beginDatum),
eindDatum: moment(value.eindDatum),
isCorrespondentie: value.isCorrespondentie,
//isPrive: value.isPrive,
attest: value.flowbackPersoneelslidAdresAttest
});
if (value.flowbackPersoneelslidAdresAttest) {
this.attestAanwezig = true;
this.attest = new FlowbackPersoneelslidAdresAttest(value.flowbackPersoneelslidAdresAttest);
}
}
@Output() public flowbackPersoneelslidAdresChange = new EventEmitter<FlowbackPersoneelslidAdresForm>();
//defaults to current date
minDateActiefVanaf: moment.Moment = moment(new Date());
codeAdrestypeSelectConfig: ModelSelectConfig<CodeAdrestype>;
codeAdrestypes: CodeAdrestype[];
codeGemeenteSelectConfig: ModelSelectConfig<CodeGemeente>;
codeGemeenten: CodeGemeente[];
codeLandSelectConfig: ModelSelectConfig<CodeLand>;
codeLanden: CodeLand[];
fileToUpload: File = null;
attestAanwezig: boolean = false;
attest: FlowbackPersoneelslidAdresAttest;
loaded: boolean = false;
constructor(private confirmationService: ConfirmationService, private codeTabelService: CodeTabelService) {
this.loadCodeAdrestypes();
this.loadCodeGemeenten();
this.loadCodeLanden();
console.log('min actief vanaf', this.minDateActiefVanaf);
}
/*
ngAfterViewInit(): void {
this.loadCodeAdrestypes();
this.loadCodeGemeenten();
this.loadCodeLanden();
}
*/
ngOnInit(): void {
}
public async handleFileInput(files: FileList) {
this.fileToUpload = files.item(0);
}
savePersoneelslidAdres({ valid, value }: EloketForm<FlowbackPersoneelslidAdresForm>) {
if (valid) {
console.log("savePersoneelslidAdres", value);
value.attest = this.fileToUpload;
this.flowbackPersoneelslidAdresChange.emit(value);
}
}
async HaalAttest(id: number) {
//todo: implement
//let bewijs = await this.flowbackService.GetFlowbackPersoneelslidAdresAttest(id).toPromise();
let bewijs = null;
let blob = convertByteArrayToBlob(bewijs.data.content, bewijs.data.contentType);
FileSaver.saveAs(blob, bewijs.data.fileName.substr(9));
}
async DeleteAttest(id: number) {
const confirmed = await this.confirmationService.promptConfirm('Personalia.Bank.VerwijderenAttestConfirm');
if (confirmed) {
//todo: implement
//var deleted = await this.flowbackService.DeleteFlowbackPersoneelslidAdresAttest(id).toPromise();
var deleted = true;
if (deleted) {
this.attestAanwezig = false;
}
}
}
async loadCodeAdrestypes() {
this.codeAdrestypes = [];
const codeAdrestypeDto = await this.codeTabelService.GetCodeAdrestypes().toPromise();
this.codeAdrestypes = codeAdrestypeDto.map(item => new CodeAdrestype(item))
.sort((a, b) => (b.omschrijving < a.omschrijving ? 1 : -1));
this.codeAdrestypeSelectConfig = new ModelSelectConfig(this.codeAdrestypes,
codeAdrestype => codeAdrestype.codeAdrestypeId.toString(),
codeAdrestype => codeAdrestype.omschrijving);
}
async loadCodeGemeenten() {
this.codeGemeenten = [];
const codeGemeenteDto = await this.codeTabelService.GetCodeGemeentes().toPromise();
this.codeGemeenten = codeGemeenteDto.map(item => new CodeGemeente(item))
.sort((a, b) => (b.postNrGemeente < a.postNrGemeente ? 1 : -1));
this.codeGemeenteSelectConfig = new ModelSelectConfig(this.codeGemeenten,
codeGemeente => codeGemeente.codeGemeenteId.toString(),
codeGemeente => codeGemeente.postNrGemeente.toString() + ' ' + codeGemeente.gemeente);
this.loaded = true;
}
async loadCodeLanden() {
this.codeLanden = [];
const codeLandDto = await this.codeTabelService.GetCodeLanden().toPromise();
this.codeLanden = codeLandDto.map(item => new CodeLand(item))
.sort((a, b) => (b.naam < a.naam ? 1 : -1));
this.codeLandSelectConfig = new ModelSelectConfig(this.codeLanden,
codeLand => codeLand.codeLandId.toString(),
codeLand => codeLand.naam);
}
}
adres-form-component.html:
<form #f="ngForm" (ngSubmit)="savePersoneelslidAdres(f)">
<div class="form-group row mb-3">
<label class="col-4 col-form-label">van</label>
<div class="col-8">
<app-date-picker name="beginDatum" [minDate]="minDateActiefVanaf" ngModel></app-date-picker>
</div>
</div>
<div class="form-group row mb-3">
<label class="col-4 col-form-label">tot</label>
<div class="col-8">
<app-date-picker name="eindDatum" [minDate]="minDateActiefVanaf" ngModel></app-date-picker>
</div>
</div>
<div class="form-group row mb-3">
<label class="col-4 col-form-label">adrestype</label>
<div class="col-8">
<app-select name="codeAdresType" [selectConfig]="codeAdrestypeSelectConfig" ngModel>
</app-select>
</div>
</div>
<div class="form-group row mb-3">
<label class="col-4 col-form-label">Straatlijn</label>
<div class="col-8">
<input class="form-control" type="text" name="straatLijn" ngModel>
</div>
</div>
<div class="form-group row mb-3">
<label class="col-4 col-form-label">Straatlijn2</label>
<div class="col-8">
<input class="form-control" type="text" name="straatLijn2" ngModel>
</div>
</div>
<div class="form-group row mb-3">
<label class="col-4 col-form-label">Nummer</label>
<div class="col-8">
<input class="form-control" type="text" name="nummer" ngModel>
</div>
</div>
<div class="form-group row mb-3">
<label class="col-4 col-form-label">Bus</label>
<div class="col-8">
<input class="form-control" type="text" name="bus" ngModel>
</div>
</div>
<div class="form-group row mb-3">
<label class="col-4 col-form-label">Gemeente</label>
<div class="col-8">
<app-select name="codeGemeente" [selectConfig]="codeGemeenteSelectConfig" ngModel></app-select>
</div>
</div>
<div class="form-group row mb-3">
<label class="col-4 col-form-label">Land</label>
<div class="col-8">
<app-select name="codeLand" [selectConfig]="codeLandSelectConfig" ngModel></app-select>
</div>
</div>
<div class="form-group row mb-3">
<label class="col-4 col-form-label">Gebruiken als correspondentieadres?</label>
<div class="col-8">
<input type="checkbox" name="isCorrespondentie" ngModel>
<label>Correspondentie</label>
</div>
</div>
<div class="form-group row mb-3">
<label translate="Personalia.Bank.Bewijs" class="col-4 col-form-label"></label>
<div class="col-8">
<ng-container *ngIf="attestAanwezig">
<label>{{attest.naam}}</label>
<button (click)="HaalAttest(attest.id)" class="btn btn-sm btn-light"><i class="far fa-file-pdf fa-2x"></i></button>
<button (click)="DeleteAttest(attest.id)" class="btn btn-sm btn-light"><i class="fal fa-trash-alt fa-2x"></i></button>
</ng-container>
<ng-container *ngIf="!attestAanwezig">
<input type="file" id="file" (change)="handleFileInput($event.target.files)">
</ng-container>
</div>
</div>
<div class="row">
<div class="col-12">
<button class="btn btn-dark float-right" type="submit" [translate]="submitButtonText"></button>
</div>
</div>
<app-global-form-error [error]="serverValidationError" [possibleErrors]="['AfwezigheidtypeYearsIncorrectOrder']"></app-global-form-error>
</form>
wijzig-adres.component.html(编辑地址):
<h2>Adres wijzigen</h2>
<app-adres-form *ngIf="wijzigFlowbackPersoneelslidAdres && wijzigFlowbackPersoneelslidAdres.codeGemeenteId != null" submitButtonText="Button.Edit" [serverValidationError]="serverValidationError" [flowBackPersoneelslidAdres]="wijzigFlowbackPersoneelslidAdres" (flowbackPersoneelslidAdresChange)="onWijzigFlowbackPersoneelslidAdres($event)"></app-adres-form>
wijzig-adres.component.ts:
@Component({
selector: 'app-wijzig-adres',
templateUrl: './wijzig-adres.component.html',
styleUrls: ['./wijzig-adres.component.scss']
})
export class WijzigAdresComponent implements OnInit {
wijzigFlowbackPersoneelslidAdres: WijzigFlowbackPersoneelslidAdres;
serverValidationError: ApiError;
id: number;
constructor(private flowbackService: FlowbackService, private notificationService: NotificationService,
private router: Router, private route: ActivatedRoute, private routeParamsChange: RouteParamsChange) {
this.routeParamsChange.watch(this.route, (params) => {
this.id = params.id;
this.loadData();
});
}
ngOnInit(): void {
}
async loadData() {
const flowbackPersoneelslidAdres = await this.flowbackService.GetFlowbackPersoneelslidAdresById(this.id).toPromise();
this.wijzigFlowbackPersoneelslidAdres = new WijzigFlowbackPersoneelslidAdres(flowbackPersoneelslidAdres.data);
console.log('load data in wijzig-adres component:', this.wijzigFlowbackPersoneelslidAdres);
}
async onWijzigFlowbackPersoneelslidAdres(flowback: FlowbackPersoneelslidAdresForm) {
CatchFormError(this, async () => {
const updateAdres: WijzigFlowbackPersoneelslidAdres = {
id: this.id,
straat: flowback.straatLijn,
straatLijn2: flowback.straatLijn2,
huisNummer: flowback.nummer,
busNummer: flowback.bus,
codeAdrestypeId: flowback.codeAdresType.codeAdrestypeId,
codeGemeenteId: flowback.codeGemeente.codeGemeenteId,
codeLandId: flowback.codeLand.codeLandId,
beginDatum: flowback.beginDatum.format('MM/DD/YYYY'),
eindDatum: flowback.eindDatum.format('MM/DD/YYYY'),
isPrive: false,
isCorrespondentie: flowback.isCorrespondentie
};
let objJsonStr = JSON.stringify(updateAdres);
let objJsonB64 = Buffer.from(objJsonStr).toString("base64");
await this.flowbackService.UpdateFlowbackPersoneelslidAdres({ flowback: objJsonB64, file: flowback.attest as any }).toPromise();
this.notificationService.showMessage('Personalia.Bank.RekeningNummerGewijzigd');
await this.router.navigate(['../../'], { relativeTo: this.route });
});
}
}
adres-overzicht.component.html(地址概述)
<app-master-detail [route]="route">
<app-master>
<div class="page">
<div class="header">
<h3>ADRESSEN</h3>
</div>
<div class="content">
<div class="row">
<div class="col-12">
<div class="onderdeel-adres" *ngFor="let adres of Adressen; let i = index;">
<table>
<tr>
<td colspan="2">
<h3>{{adres.adresType}}</h3>
</td>
</tr>
<tr>
<td class="label"><label translate="Personalia.Adres.Van"></label></td>
<td class="inhoud">{{adres.beginDatum|date:'dd/MM/yyyy'}}</td>
</tr>
<tr>
<td class="label"><label translate="Personalia.Adres.Tot"></label></td>
<td class="inhoud">
<ng-container *ngIf="!adres.eindDatum.startsWith('2999')">{{adres.eindDatum|date:'dd/MM/yyyy'}}</ng-container>
<ng-container *ngIf="adres.eindDatum.startsWith('2999')"><span translate="Personalia.Adres.Onbepaald"></span></ng-container>
</td>
</tr>
<tr>
<td colspan="2"> </td>
</tr>
<tr>
<td class="label"><label translate="Personalia.Adres.Straat"></label></td>
<td class="inhoud">{{adres.straat}}</td>
</tr>
<tr>
<td class="label"><label translate="Personalia.Adres.Nr"></label></td>
<td class="inhoud">{{adres.huisnummer}}</td>
</tr>
<tr>
<td class="label"><label translate="Personalia.Adres.Bus"></label></td>
<td class="inhoud">{{adres.busnummer}}</td>
</tr>
<tr>
<td class="label"><label translate="Personalia.Adres.PostcodeGemeente"></label></td>
<td class="inhoud">{{adres.codeGemeente.postnrDeelGemeente}} {{adres.codeGemeente.deelGemeente}}</td>
</tr>
<tr>
<td class="label"><label translate="Personalia.Adres.Land"></label></td>
<td class="inhoud">{{adres.codeLand.naam}}</td>
</tr>
<tr>
<td colspan="2" class="inhoudCorAdres"> <span *ngIf="adres.isCorrespondentie" translate="Personalia.Adres.Correspondentie"></span></td>
</tr>
</table>
</div>
<ng-container *ngIf="flowBackAdresGegevens && flowBackAdresGegevens.length > 0">
<div [ngClass]="{'onderdeel-flowback-adres':flowback.toestand !== 30, 'onderdeel-error-flowback-adres':flowback.toestand === 30}" *ngFor="let flowback of flowBackAdresGegevens; let i = index;">
<table>
<tr>
<td class="label"><label translate="">adrestype</label></td>
<td class="inhoud">{{ flowback.codeAdresType.omschrijving }}</td>
</tr>
<tr>
<td class="label"><label translate="Personalia.Adres.Van"></label></td>
<td class="inhoud">{{ flowback.beginDatum | date:'dd/MM/yyyy' }}</td>
</tr>
<tr>
<td class="label"><label translate="Personalia.Adres.Tot"></label></td>
<td class="inhoud">
<ng-container *ngIf="!flowback.eindDatum.startsWith('2999')">{{ flowback.eindDatum | date:'dd/MM/yyyy' }}</ng-container>
<ng-container *ngIf="flowback.eindDatum.startsWith('2999')"><span translate="Personalia.Adres.Onbepaald"></span></ng-container>
</td>
</tr>
<tr>
<td class="label"><label translate="Personalia.Adres.Straat"></label></td>
<td class="inhoud">{{ flowback.straat }} {{ flowback.straatLijn2 }}</td>
</tr>
<tr>
<td class="label"><label translate="Personalia.Adres.Nr"></label></td>
<td class="inhoud">{{ flowback.huisNummer }}</td>
</tr>
<tr>
<td class="label"><label translate="Personalia.Adres.Bus"></label></td>
<td class="inhoud">{{ flowback.busNummer }}</td>
</tr>
<tr>
<td class="label"><label translate="Personalia.Adres.PostcodeGemeente"></label></td>
<td class="inhoud">{{ flowback.codeGemeente.postnrDeelGemeente }} {{ flowback.codeGemeente.deelGemeente }}</td>
</tr>
<tr>
<td class="label"><label translate="Personalia.Adres.Land"></label></td>
<td class="inhoud">{{ flowback.codeLand.naam }}</td>
</tr>
<tr>
<td class="label"><label translate="">iscorrespondentie</label></td>
<td class="inhoud"><input type="checkbox" [checked]="flowback.isCorrespondentie" disabled="true"/></td>
</tr>
<tr>
<td class="label"><label translate="">opmerking</label></td>
<td class="inhoud">{{ flowback.flowbackOpmerking }}</td>
</tr>
<!-- todo: only show opmerking when toestand = 30 (geweigerd?) -->
<tr *ngIf="flowback.toestand === 30">
<td class="label"><label translate="Personalia.Bank.Opmerking"></label></td>
<td class="inhoud">{{ flowback.flowbackOpmerking }}</td>
</tr>
<ng-container *ngIf="flowback.toestand !== 20">
<tr>
<td colspan="2" style="text-align: center;">
<button class="btn btn-sm btn-light" (click)="OnEdit(flowback)"><i class="fal fa-edit fa-2x"></i></button>
<button class="btn btn-sm btn-light" (click)="onDelete(flowback)"><i class="fal fa-trash-alt fa-2x"></i></button>
</td>
</tr>
</ng-container>
</table>
</div>
</ng-container>
<!-- make sure the data arrived from the api before accessing it -->
<ng-container *ngIf="Adressen && Adressen.length > 0">
<div class="onderdeel-toevoegen">
<table>
<tr>
<td colspan="2">
<h3><label translate="Personalia.Adres.Nieuw"></label></h3>
</td>
</tr>
<tr>
<td>
<app-date-picker [(ngModel)]="datumVanaf" [minDate]="datumVanaf"></app-date-picker>
</td>
<td>
<button [routerLink]="['nieuw', datumVanaf.format('MM/DD/YYYY'), Adressen[0].personeelslidId]"
translate="Personalia.Adres.Toevoegen" class="btn btn-dark"></button>
</td>
</tr>
</table>
</div>
</ng-container>
<div class="onderdeel-waarschuwing">
<label>Gegevens niet correct ?<br/><br/>Contacteer uw personeelsadviseur of dossierbeheerder</label>
</div>
</div>
</div>
</div>
</div>
</app-master>
<app-detail>
<router-outlet></router-outlet>
</app-detail>
</app-master-detail>
adres-overzicht.component.ts:
@Component({
selector: 'app-adres-overzicht',
templateUrl: './adres-overzicht.component.html',
styleUrls: ['./adres-overzicht.component.scss'],
providers: [ChildRouteChanges, RouteParamsChange]
})
export class AdresOverzichtComponent implements OnInit {
public Adressen: PersoneelslidAdres[];
public datumVanaf: Moment;
public flowBackAdresGegevens: FlowbackPersoneelslidAdres[];
constructor(public route: ActivatedRoute, childRouteChanges: ChildRouteChanges, public router: Router,
public personeelslidservice: PersoneelslidService, public flowbackService: FlowbackService,
public confirmationService: ConfirmationService, public notificationService: NotificationService) {
this.datumVanaf = moment().add(1, 'M').startOf('month');
childRouteChanges.watch(router, route, () => {
this.loadPersoneelslidAdressen();
});
}
ngOnInit(): void {
this.loadPersoneelslidAdressen();
}
async loadPersoneelslidAdressen() {
let personeelslidAdresDto = await this.personeelslidservice.GetPersoneelslidAdres().toPromise();
this.Adressen = personeelslidAdresDto.data;
let flowbackPersoneelslidAdresDto = await this.flowbackService.GetFlowbackPersoneelslidAdres().toPromise();
this.flowBackAdresGegevens = flowbackPersoneelslidAdresDto.data;
}
async OnEdit(flowback: FlowbackPersoneelslidAdres) {
const id = flowback.id;
console.log('id', flowback.id);
this.router.navigate(['wijzig', id], { relativeTo: this.route });
}
async onDelete(flowback: FlowbackPersoneelslidAdres) {
try {
const confirmed = await this.confirmationService.promptConfirm('Personalia.Adres.Flowback.VerwijderenConfirm', { beginDatum: apiStringToDate(flowback.beginDatum).format('DD/MM/YYYY')});
if (confirmed) {
await this.flowbackService.VerwijderFlowbackPersoneelslidAdres(flowback.id).toPromise();
this.loadPersoneelslidAdressen();
}
} catch (e) {
const { error } = e as ErrorEloket;
if (!error.validationResult.isValid) {
for (const err of error.validationResult.errors) {
this.notificationService.showMessage(`Validatie.${ValidationError[err.error]}`, err.parameters);
}
}
}
}
}
model-select-config.ts:
import { Option } from './option';
import { SelectConfig } from './select-config';
export class ModelSelectConfig<T> implements SelectConfig<T> {
public selectOptions: Option[];
public options: T[];
constructor(items: T[], public keySelector: (t: T) => string, textSelector: (t: T) => string) {
this.options = items;
this.selectOptions = items?.map(i => new Option(keySelector(i), textSelector(i)));
}
}
select.component.ts:
import { Component, Input, forwardRef, ViewChild, ElementRef, HostBinding } from '@angular/core';
import { NG_VALUE_ACCESSOR, ControlValueAccessor } from '@angular/forms';
import { SelectConfig } from './select-config';
@Component({
selector: 'app-select',
templateUrl: './select.component.html',
styleUrls: ['./select.component.scss'],
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => SelectComponent),
multi: true
}
]
})
export class SelectComponent<T> implements ControlValueAccessor {
@ViewChild('select') select: ElementRef<HTMLSelectElement>;
@Input() selectConfig: SelectConfig<T>;
@HostBinding('class.read-only') @Input() readOnly: boolean;
@Input() emptyTranslationKey: string;
@Input() defaultValue: string;
private onChange = (_: T) => { };
public onTouched = () => { };
constructor() {
}
selectedChange(key: string) {
const { keySelector: keyFor } = this.selectConfig;
const option = this.selectConfig.options.find(o => keyFor(o) === key);
this.onChange(option);
}
writeValue(obj: T): void {
if (!obj) { return; }
//this fixes the issue
/*
setTimeout(() => {
this.select.nativeElement.value = this.selectConfig.keySelector(obj);
}, 100);
*/
this.select.nativeElement.value = this.selectConfig.keySelector(obj);
}
registerOnChange(fn: (option: T) => void): void {
this.onChange = fn;
}
registerOnTouched(fn: () => void): void {
this.onTouched = fn;
}
setDisabledState?(isDisabled: boolean): void {
this.readOnly = isDisabled;
}
toggleSelect() {
this.select.nativeElement.click();
}
}
select.component.html:
<select class="form-control" #select (change)="selectedChange($event.target.value)"
*ngIf="selectConfig" [disabled]="readOnly" [value]="defaultValue">
<option disabled selected *ngIf="emptyTranslationKey" [value]="null" [translate]="emptyTranslationKey"></option>
<option *ngFor="let option of selectConfig.selectOptions" [value]="option.key" [translate]="option.text"></option>
</select>
注意我在select.component.ts的注解中添加的行修复了这个问题。然而,这不是一个可接受的解决方案,因为编辑这个文件是最后一次去+这将延迟所有使用它的代码。
任何人都有一个想法,我可以做什么,以使这一工作正确?
1条答案
按热度按时间wz3gfoph1#
解决方案是添加一个初始值为false的Subject,从API加载数据后设置为true。只有这样,表单数据才会被设置,触发在正确的时间填充选择。