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.
The loadedEducations list does not change when you change the properties of the items in the list. Try refreshing the list (
in your projectthis.loadedEducations = returnedEducations
) or use state managementThe root cause of the problem is that
There are several ways to solve this problem, each with its advantages and disadvantages:@Input()
cannot detect changes inside objects and arrays because they are both reference types. Youreducation
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() educationPass only the properties you need as primitives
parent.component.ts
parent.component.html
child.component.ts
advantage:
Simple and intuitive to use, works “normally”-
No need for additional boilerplate to send changes - to child components
shortcoming:receive- changes to child components. As the number of @Input grows, it becomes unwieldy
You lose the semantic coupling between parent and child components, they are actually bound by a shared interface (i.e. the Education- interface)
Does not scale well if properties are also reference types, in which case these properties also need to be unpacked and passed as primitives
-
Rebuild object in parent when changedparent.component.ts
Deep Clone
child.component.ts
advantage:Education- interface to maintain the semantic coupling between parent components and child components
Encourage the use of Immutable objects- It is generally a good practice for objects
No additional boilerplate is required to
receive changes to - subcomponents.
shortcoming:send- changes to child components, i.e. create redundant updateEducation() functions in the parent component
Pass reactive elements into your subcomponent, such asBehaviorSubject
, and subscribe to changes directly
parent.component.ts
parent.component.html
child.component.ts
child.component.html
advantage:updateEducation()
functions in the parent component| async
), etc.