Lets say I have the following getter/setter methods
and I want to call this in the following way:
<ng-template ngFor let-person="$implicit" [ngForOf]="people" let-i=index let-last=last>
<app-card [cardItem]="people[i]" [nextCard]="next(i)"></app-card>
</ng-template>
PS: Think of this as circular array. Where by I need prev, current, and next items.
However I get the following Error
Angular: Member 'next' in not callable
Why is that? And whats the solution?
Thanks
Thank you guys for your help and explanation. With your help i managed to make it work:
<app-card [currentCard]="people[i]" [nextCard]="people[i === people.length - 1 ? 0: i + 1]" [prevCard]="i == 0 ? people[people.length - 1] : people[i - 1]"></app-card>
So its pretty much circular array. Lets assume we have the following:
people["James Dan", "Aluan Haddad", "Jota Toledo"]
So few conditions:
index = 0
) - then my prev
will be people[people.length - 1]
which is the last element in the array. And if my current is on index 1, then my prev will be index 0 and next will be index 2.The Angular template syntax is, in general, a subset of JavaScript syntax with some notable differences and with many restrictions.
However what you have here is actually invalid in JavaScript as well. It is not valid to call a property accessor. Ever.
Given the following property
get p() {
console.info('read p');
return this.wrapped;
}
set p(value) {
console.info('wrote p');
this.wrapped = value;
}
The get
accessor is invoked implicitly when the property thusly named is read.
For example:
console.log(o.p); // read p
The set
accessor is invoked implicitly when the property thusly named is written.
For example:
o.p = x; // wrote p;
The same rules apply in Angular templates.
However, your example of
<app-card [cardItem]="people[i]" [nextCard]="next(i)">
suggests that a property is not what you want here.
The correct usage of a property implies the following syntax
<app-card [cardItem]="people[i]" [nextCard]="next = i">
Which I do not believe is supported by the Angular template syntax and even if it is doesn't make a lot of sense and would be hard to read.
Instead you should create a method that returns a value
getNext(i: number) {
this._index = i + 1;
this._index = i % this.people.length;
return this.people[this._index];
}
Then used in your template as
<app-card [cardItem]="people[i]" [nextCard]="getNext(i)">
Having said that, I think the entire design is questionable. You seem to be going through contortions to store excess mutable state independently of the array that naturally maintains it.
I believe you would be much better served by removing the method and the property entirely and using
<app-card
*ngFor="let person of people; let i = index"
[previousCard]="people[i === 0 ? people.length - 1 : i - 1]"
[cardItem]="person"
[nextCard]="people[i === people.length - 1 ? 0 : i + 1]">
If you want a cleaner syntax, you might define a property, with a get
accessor only, that returns a view of your array as objects with previous
, current
, and next
properties.
get peopleAsPreviousCurrentAndNextTriplets() {
return this.people.map((person, i) => ({
previous: this.people[i === 0 ? this.people.length - 1 : i - 1],
current: person,
next: this.people[i === this.people.length - 1 ? 0 : i + 1]
}));
}
This can be more readable in complex code because its abstracts away the index for more semantic properties that we can use directly. Perhaps more importantly, it enables TypeScript's world-class tooling to validate the computation.
<app-card
*ngFor="let item of peopleAsPreviousCurrentAndNextTriplets"
[previousCard]="item.previous"
[cardItem]="item.current"
[nextCard]="item.next">
And thus we come full circle. Note how we define the get
accessor and how we read the property it defines without ()
, implicitly invoking that accessor.
The last example is probably overkill for this scenario but I think it's useful nonetheless.