Gathering detailed insights and metrics for ngx-function-expression
Gathering detailed insights and metrics for ngx-function-expression
Gathering detailed insights and metrics for ngx-function-expression
Gathering detailed insights and metrics for ngx-function-expression
@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression
Rename destructuring parameter to workaround https://bugs.webkit.org/show_bug.cgi?id=220517
@babel/helper-member-expression-to-functions
Helper function to replace certain member expressions with function calls
@babel/helper-optimise-call-expression
Helper function to optimise call expression
@babel/helper-explode-assignable-expression
Helper function to explode an assignable expression
Performant calls to local functions in Angular Bindings
npm install ngx-function-expression
Typescript
Module System
Node Version
NPM Version
57
Supply Chain
87.3
Quality
77.6
Maintenance
100
Vulnerability
98.9
License
Updated on 28 Oct 2024
Minified
Minified + Gzipped
TypeScript (84.92%)
JavaScript (15.08%)
Cumulative downloads
Total Downloads
Last day
50%
Compared to previous day
Last week
-62.3%
Compared to previous week
Last month
12.8%
Compared to previous month
Last year
17.6%
Compared to previous year
1
2
Warning: For Angular Versions < 13, please use ngx-function-expression@^2.0.0, because the package is Ivy-only as of Dec 2021.
Using Functions in Angular Templates is a double-edged sword.
While you can significantly reduce your template code by putting logic in component methods, this idea comes with its own pitfalls: Because you can't mark a method as pure, Angular will keep calling that method in every change detection cycle, waiting for the outputs to change, resulting in a huge amount of function calls.
By using ngx-function-expression, you are allowing Angular to memoize the result of your function calls as long as the parameters don't change.
This library comes with the following benefits:
The Pipes provided by this module have a very simple yet powerful syntax.
The most basic syntax, already capable of handling all sorts of function calls is the following:
function | fnApply:[...args]:thisArg
user.method
.this
-context of the function call. In most
cases, you can just omit this parameter to call the function in the components scope, just as if you
were calling it with the call syntax (method()
) from the template. ngx-function-expression always
infers the component instance as thisArg, if you don't specify it otherwise.Javascript Code | fnApply Call |
---|---|
component.method() | method | fnApply |
component.method(arg1, ...args) | method | fnApply:[arg1, ...args] |
someObject.method(arg) | someObject.method | fnApply:[arg]:someObject |
1@Component({ 2 template: '{{pow | fnApply:[3, 2]}}' // will render '9' 3}) 4class TestComponent { 5 public pow(base: number, exponent: number): number { 6 return Math.pow(base, exponent); 7 } 8}
Obviously, this could also be achieved by implementing a PowerPipe or precalculating the values in the component rather than in the template, and, most of the time, this is exactly what you should do!
But in reality, people will not write a pipe for every operation, or some methods are better contained in a component to access the context of that component.
As with any Angular pipe, you can chain them together to receive exactly the results you want.
1@Component({ 2 template: `Explosion in {{createCountdown | fnApply | async}}` 3}) 4class TestComponent { 5 createCountdown(): Observable<number> { 6 return interval(1000).pipe(take(5), map(i => 5 - i)); 7 } 8}
When looking at this example, note that using {{createCountdown() | async}}
would result in the AsyncPipe
subscribing to a whole new observable in every tick, keeping the countdown on 5 forever.
Using fnApply will call the method exactly once and then listen to changes on the returned observable using AsyncPipe.
Just imagine you have some XHR request or costly computations in the observable you're subscribing to...
1@Component({ 2 template: `<i *ngIf="user | fnMethod:'hasEditPermissions'">` 3}) 4class TestComponent { 5 hasEditPermissions(): Observable<boolean> { 6 return this.user.permissions$.pipe(map(permissions => permissions.edit)); 7 } 8}
thisArg
,
you can either pass undefined/null or an empty tuple as arguments in that case.method | fnApply:arg
.As you've seen before, you can call methods on any object in your scope with the basic syntax:
{{obj.getName | fnApply:[]:obj}}
What's bothering is that you have to specify the reference object twice - once to get a reference to the method, and
then again to set it as the correct this
argument. This is not only hardly readable, but is also very easy to
forget the last parameter and thus getting wrong results, which are frustrating to debug.
To simplify this experience and save you some precious debugging time, there's another syntax to directly call a method
on a given reference object: fnMethod
. You can use it like this:
{{obj | fnMethod:'getName':[]}}
(of course, the empty argument list is optional)
As you type, your IDE will automatically list possible public methods, automatically extracted from the reference objects type. Even with this syntax, your arguments and return type will be type-checked and result in a type-safe template. While this is not necessarily shorter, I think it's way easier to read and understand what happens in this example, because the syntax is more similar to a normal method call.
When using fnApply on a component method, the library will automatically bind the method to your component instance. This can simplify several use-cases where other solutions are overly verbose or even impossible, because data is only available in that component.
Example:
1@Component({ 2 template: `<div *ngFor="let listItem of list" 3 [hidden]="!(hasPermissionsForItem | fnApply:listItem)">` 4}) 5class ListComponent { 6 public list: ListItem[]; 7 private user: User; 8 9 public hasPermissionsForItem(listItem: ListItem): boolean { 10 return this.user.permissions.check(listItem); 11 } 12}
ngx-function-expression will always try to infer the parameter types and the return type of your given function, allowing you to write type-safe templates.
Example:
1@Component({ 2 template: ` 3 {{(add | fnApply:[1, 'Carl'])}} // Won't compile, because add expects number arguments 4 {{(add | fnApply:[1, 2])}} // Works fine 5 ` 6}) 7class ComplexMathsComponent implements PunIntended { 8 public add(l: number, r: number) { 9 return l + r; 10 } 11}
npm install ngx-function-expression
.FunctionExpressionModule
to your application and use the fnApply
and fnMethod
pipes
in your templates.Feel free to use GitHub issues for further questions, suggestions, feature requests and bug reports.
I'm happy to make this module useful to as many people as possible!
See CHANGELOG.md
parametrizedFunction | fnApply:[]
works for parametrizedFunction(requiredArg: ...)
Currently, any function that requires parameters can be called with an empty argument array. I checked and rewrote
the typings multiple times, but I just won't understand what's wrong or why this even happens. As soon as we provide
at least one argument to the pipe, the type checking works as expected.
If you're a TypeScript Goddess or solved a similar issue in the past, feel free to contact me or create a Pull Request!
No vulnerabilities found.
Reason
no binaries found in the repo
Reason
license file detected
Details
Reason
0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0
Reason
Found 0/30 approved changesets -- score normalized to 0
Reason
no SAST tool detected
Details
Reason
no effort to earn an OpenSSF best practices badge detected
Reason
project is not fuzzed
Details
Reason
security policy file not detected
Details
Reason
branch protection not enabled on development/release branches
Details
Reason
21 existing vulnerabilities detected
Details
Score
Last Scanned on 2024-11-25
The Open Source Security Foundation is a cross-industry collaboration to improve the security of open source software (OSS). The Scorecard provides security health metrics for open source projects.
Learn More