I want to preface the question saying I have already read and understood other potentially duplicate questions (such as this one) and have been unable to make this work leading me to believe that the problem is a little different in nature or I misunderstood something.
The context: I have a parent component with two child components (a side menu and a main view). The side menu has some filters in the form of checkboxes bound to an object via [(ngModel)]
and trigger an event using (ngModelChange)="changeFilter()"
within the template.
In the parent component I catch the event: (filter)="updateFilters($event)"
. This function changes a public filter variable which I then pass along to the other child component like this: [filterObject]="filters"
In the main view component I feed the filterObject variable to a pipe in order to filter an array of objects.
The problem: The pipe gets called only once, after I changed the filter for the first time. The filterObject variable inside the main view shows up correctly updated when shown like this {{filterObject | json}}
but the setter is never called. Console logs put in the setter and getter show that only the getter is getting access (2-4)
Solutions I've tried:
Adding an additional parameter to the pipe to trigger it. The problem with this approach is that setting a new date needs to be done from the setter for filterObject
which doesn't get called - as stated above
Making the pipe impure (works but not a really good solution as it makes it process very big arrays of complex objects 2-4 times per click)
Making the parent update the filter variable, not through an assignment operator, but through Object.assign
in order to change the reference and potentially trigger the setter
Solutions I'm considering: If I don't manage to figure out what the problem is I'm thinking of making a service to communicate between the two components via observers. Otherwise I might have to settle for an impure pipe with some custom change detection embedded (I'm thinking of converting the filter object to a JSON string and checking the length).
side-menu-component.html
<input type="checkbox" id="filter-type-2" name="filter-type-2" [(ngModel)]="filterObject.type.me" (ngModelChange)="changeFilter()">
side-menu-component.ts
@Output() filter = new EventEmitter();
public filterObject = {
type: {
mo: true,
me: true,
ar: true,
cto: true,
gl: true,
dl: true
},
status: {
unreviewed: true,
approved: true,
other: true
}
};
// ...
changeFilter() {
this.filter.emit(this.filterObject);
}
parent-component.html
<aside>
<app-article-view-aside [article]="article" (close)="closeModal($event)" (filter)="updateFilters($event)"></app-article-view-aside>
</aside>
<div class="articleContainerMainView">
<app-article-view-main [article]="article" [filterObject]="filters"></app-article-view-main>
</div>
parent-component.ts
updateFilters(newFilter) {
this.filters = newFilter;
}
main-view-component.html
<app-finding
*ngFor="let finding of findings | findingsFilter:filterDate:filterObject; let i = index"
[finding]="finding"></app-finding>
main-view-component.ts
@Input() set filterObject(filterObject) {
console.log('setter');
this._filterObject = filterObject;
this.filterDate = Date.now();
}
get filterObject() {
console.log('getter');
return this._filterObject;
}
private _filterObject = {};
public filterDate = Date.now();
console output when first displaying the page:
setter
getter
getter
getter
getter
I really want to do it "The Angular Way" so my question to you is what am I missing here?
It may be that your side menu component emits the same object. Its content may be altered, but the emitted object is still the same. Consider emitting a copy of the object instead. Object.assign({}, this.filterObject)
may help you.