Say I have ngFor which needs to render the result of some expensive function based on three this params and the current ngFor item. Is there a lean way to memoize such function? To further "complicate it" I use that function in three places in the ngFor template. Ideally I would like something like fn(ngForParam) and get full memoization based on the 4 dependencies.
#Memoize template function with dynamic parameter
114 messages · Page 1 of 1 (latest)
Easiest way is probably to transform the array in the component, store the result of the transformation, and iterate on the resulting array.
@bitter bloom well if I do need the item itself it means I need to map each item to an object containing the item plus the computed value. If I have more computed values I need to add more properties. It's a bit... Smelly 😅
Any reason not to use a pipe for that?
I guess u can always memoize it like so: https://medium.com/angular-in-depth/how-to-improve-angular-performance-by-just-adding-just-8-characters-877bde708ddd
But pipes are more typical in angular for that.
@boreal oriole well I need three static params and one dynamic param
So I will end up passing all four params from template each time
Pipes are nothing but a memoized function in a specific pattern
Where angular takes Care of the memoization for u
And passing static and dynamic values to a pipe are pretty common.
Ye I thought about it but it's verbose:
fnNameHere | memoize:someLongParamName1:someLongParamName2:someLongParamName3:someLongParamName4
And I have it in three different places in template
Yeah, when u want to memoize things, i would still encourage passing all values and not rely on things on "this" inside of the memoized function.
Even when static, but yeah i get what u mean.
But pipes are all about memoization in angular.
Or well, all about memoization function calls from in the html
Can't some smart memo make it into fnNameHere(dynamicValue)
Also if its static, why can the values not exist in the pipe?
So u only pass the Dynamic ones ?
They are not static - they are on the component this
And anyway that means a specific pipe for each case 😦
I would prefer a pipe, or what @bitter bloom mentioned. Both are common patterns in angular. But u can use what i linked above as well if that's what u want .
The closest I ever got is:
(fnNameHere | memoize:dynamicValue)() which isn't nice either
Can u elaborate on what the static params are then ?
Inputs for example
I don't see how that is static
They can change? And would cause weird behavior if they did and memoization didnt account for that
Ah by static I mean not part of ngFor for example or change based on template instance
So i would still think for proper memoization, they all need to be passed.
Anyway, there are 3 solutions in this thread to achieve it. U can pick what u like. But considering future angular devs in the team, sticking to angular standards could make things easier.
Indeed - I will try to be more clear:
If I only need this params for memoization I can use memo function with dependencies in component. The problem starts when there ngFor based params
Bur yeah just wanted to try and get rid of the verbose way to pass all of them again and again
And thus my question if one can avoid it while retaining memoization
I don't think so.
I assume you know, but memoization is the concept of remembering inputs and outputs. If you take away one of the inputs, it's not part of the memoization anymore, and if the value changes of that property, it wont recalculate.
The easiest to avoid passing the params i believe is what @bitter bloom said. But that doesnt use memoization, but you dont need it in that case.
And that's both fairly common and very good for perfomance.
Cant get any better if you avoid any calculation in the html, even more so if u avoid it inside an ngFor.
As you know at that point that you have an array with all calculated values up front.
You can then add trackBy and could even get more improvements in terms of performance as opposed to trying to memoize inside the ngFor.
(that goes for both pipes and memoized component methods inside the ngFor)
So i don't think its "smelly" when it opens up for these kind of improvements.
And to answer more to what you wanted, what about passing only the ngFor value to the component method, have that method not use memoization, but have it call another, memoized method, passing the ngFor param, as well as the others and counting on memoization on that level?
Not something i would do, but feels like you are only looking for a solution like that.
At least you get full memoization on all params, while not passing the static ones from the html, but still passing it to the memoized function.
This would still violate the fact that it's discouraged to call methods from the html tho, but the perfomance implications should be limited.
One downside of this is that you will need a complicated trackBy if you want to use that to further optimize, because you can not ask angular to optimize the ngFor by only looking at the array. You need both the array and all memoized static params to inform angular when it should rerender the ngFor items.
☝️ tbf the only solution that makes that easier to manage is @bitter bloom (even better, it just comes free with his solution) , not even pipes will make this easier (so the best solution, performance wise, here is to forget about memoization on the ngFor level) So yeah, i would still encourage his solution.
@boreal oriole I see thanks for the detailed answer
Sorry for the wall of text tho. 🤣🤣
And thanks to @bitter bloom too
I guess there isn't an easy way then
For class only params it's easy to do
And ye the function that calls memoized function I thought about - it isn't nice either
U can move the html in ngFor to a different component, pass all as inputs, and they are now all class props.
I just didn't like the idea of mapping beforehand for say four calculations
It's always better to provide the html with precalculated things.
Whether you like it or not is a different thing, but it makes things alot easier as of that point on
Ye I get what you mean
Another component might be overkill sometimes like a new pipe is
Depends. U can add onPush and have a new layer of performance optimization.
From my experience components have overhead when there are many
But ye it disconnects the change detection
Depends what many means, but you can have alot and still be blazing fast. What you dont move to a different component, you cant optimize on it's own.
So for heavy ngFors, i would argue its probably not overkill to be able to optimize on a per item level.
(but ofcourse if this is about one line of html, i get it. If it's about 15+, it's a non discussion for me)
Not only do u gain onPush possibilities, u also remove the fact that u need to reason about ngFor params and component params anymore.
So would argue if it gives u that, how is it overkill?
Anyway, i don't think u need a component to solve this.
Ye overkill for small templates of course 🙂
Anyway thanks - hoped there is a neat way I didn't know about - but the long and detailed answer helped me too 🙂
Can i recommend u one YouTube video?
About someone of the angular team demoing something along those lines?
How a template function kills the app and how he solves it
AngularConnect is returning to London in 2018. Learn more at https://angularconnect.com.
Video sponsored by Rangle.io (https://rangle.io)
One thing is sure - performance matters! Blocking the main thread of an application, causing frame drops, is one of the most efficient ways to get rid of a significant portion of our users. Fortunately, the ...
You might know all of it, or not. But still, awesome talk by Minko!
Nice thanks will watch it (not sure if I did already)
Anyway I hope signals come to angular - won't solve that specific problem but will make the whole memoization concept easier imo
Not sure i get how, or at least why it would be different from rxjs in this case (apart from a more easy API to use)
But yeah, not to familiar with it.
Regardless, without wanting to sound rude, i would try and keep html simple as much as possible and not even try what you are trying. Feels more like things i would do in JSX than in angular.
Mostly I have easy html indeed with all logic baked in the state management. Sometimes there are dumb components which has logic like this
One can argue that's when component store comes in
But generally an ngFor with small template that has some calculation that depends on inputs and item isn't that easy to abstract out (say isDisabled + isReadonly + disabled tooltip, etc ..)
That's why you avoid that in the first place .
Convert your data to a "viewmodel"
And go from there.
But yes, its complicated to get performant if u don't.
Having said all of the above, i wouldnt recommend pipes in this case tbh but go all on on what was mentioned by jbnizet.
So that recommendation of mine made little sense at the end.
Hmm is viewModel for arrays to? Say a persons array will be an object with all calculated html stuff in it? All built in component?
Viewmodel is the model for your view, it can be An array of viewmodels.
Built in selectors vs in component?
I mean say store has persons array - should it convert to view model or in component itself?
I would pull users from the store, shape it in a smart component to a vm, pass it to a component that expects the shaped user(s)
I don't think a selector should worry about views, but it perfectly can!
So a person component would accept that so called view person?
Hmmm, i was about to add that i think it depends.
Or ngOnChanges inside will handle it
Could go in the person component tbh
Exactly.
But then you need to pass extra data just for that
What kind of extra data ?
If for example you cant click edit person if some falg exists which theoretically isn't relevant to the dumb person component
If the component's ui need it, how is it "extra" data ?
It will need it anyway
To make that decision
You could pass canEdit boolean
I don't see how that changes anything.
If the ui disables the button based on a flag, it needs the flag
Well we went a bit far from the original question 🙂