My educational experience is as follows:
<cdk-virtual-scroll-viewport itemSize="5" class="list-scroll"> <app-education-item *ngFor="let education of loadedEducations" (isSelected)="changeSelected(education)" [ngClass]="{ selected: education == loadedEducation }" [education]="education" (isRemoved)="removeEducation(education)" ></app-education-item> </cdk-virtual-scroll-viewport>
And the following components
<div [ngClass]="{ 'list-item-container-collapsed' : isCollapsed, 'list-item-container': !isCollapsed, 'unselected': !isActive, 'selected': isActive}" (click)="selectEducation()"> <div class="top-items-container" style="display: flex;"> <div class="item-text"> <span class="txt-header">{{educationHeader}}</span> <p class="txt-date"> <span>{{startDate}}</span> - <span>{{endDate}}</span> </p> </div> </div>
Has the following logic for displaying data obtained from parameters:
export class EducationItemComponent implements OnInit { @Input() education: Education; isCollapsed = false; isActive = false; startDate: string; endDate: string; educationHeader: string; educationDescription: string; constructor() { } ngOnInit(): void { console.log(this.education); this.startDate = this.education.startDate != '' ? formatDate(this.education.startDate, 'MMM yyyy', 'en-US') : formatDate(new Date(), 'MM YYYY', 'en-US') ; this.endDate = this.education.endDate != 'present' ? this.endDate = formatDate(this.education.endDate, 'MMM yyyy', 'en-US') : this.education.endDate; this.educationHeader = this.education.degree == undefined || this.education.description == undefined ? '' : this.education.degree + ' at ' + this.education.school; if (!this.education.description.enUS && this.education.description.nlNL) { this.educationDescription = this.education.description.nlNL; } else if (this.education.description.enUS) { this.educationDescription = this.education.description.enUS; } }
I use custom events to handle updates
@Output() updatedValue: EventEmitter<any> = new EventEmitter<string>(); constructor() {} ngOnInit(): void {} fieldChanged(changes: SimpleChanges) { this.updatedValue.emit(changes); }
Then I have the following html for manipulating the data:
<div class="update-wrap"> <div class="list-header">Update education</div> <div> <div class="col-sm-6 input-wrapper"> <app-input-field label="Institution" [value]="loadedEducation.school" (updatedValue)="loadedEducation.school = $event" ></app-input-field> </div> <div class="col-sm-6 input-wrapper date-picker-input"> <app-input-field label="Degree" [value]="loadedEducation.degree" (updatedValue)="loadedEducation.degree = $event" ></app-input-field> </div> </div> </div>
However, the updated data in the field [value]="loadedEducation.school" (updatedValue)="loadedEducation.school = $event"
will not be bound to the child component, so after refreshing and getting No data from the database will be displayed before.
What possibilities can I try to implement?
I tried to implement ngOnChanges but without success.
P粉6589549142024-03-22 14:12:38
The loadedEducations list does not change when you change the properties of the items in the list. Try refreshing the list (this.loadedEducations = returnedEducations
) or use state management
P粉0227236062024-03-22 13:08:51
The root cause of the problem is that @Input()
cannot detect changes inside objects and arrays because they are both reference types. Your education
property is an object, so changes made in the parent component that directly change the property (e.g. education.school = 'newValue'
) will not trigger the child component's property# Any changes to ##@Input() education
parent.component.ts
education: Education =
parent.component.html
child.component.ts
export class EducationItemComponent implements OnChanges { @Input() school: string; @Input() degree: string; ngOnChanges(changes: SimpleChanges): void { // will emit whenever .school or .degree is changed in the parent } }
advantage:
Additional boilerplate is required to
You lose the semantic coupling between parent and child components, they are actually bound by a shared interface (i.e. the
Does not scale well if properties are also reference types, in which case these properties also need to be unpacked and passed as primitives
education: Education =
export class EducationItemComponent implements OnChanges {
@Input() education: Education;
ngOnChanges(changes: SimpleChanges): void {
// will emit whenever updateEducation() is called in the parent
}
}
Retain the use of the
Encourage the use of Additional boilerplate is needed to
educationSubject: BehaviorSubject
export class EducationItemComponent implements OnChanges {
@Input() educationSubject: BehaviorSubject
{{ education.school }}
Full control over event sending/subscription. This is good for any other side effects you wish to trigger
Also advocates the use of
updateEducation()
functions in the parent component | async
), etc.