What happens if we have a function in our View that takes parameters?
|
userFullName() { return this.firstName + ' ' + this.lastName; } |
Easy! We just say <div>${userFullName()}</div>
!
But what happens if the users first or last name changes dynamically? Oh dear, functions are only evaluated once at bind time…
Hang on! Let’s make it a property accessor! They are by default dirty-checked!
|
get userFullName() { return this.firstName + ' ' + this.lastName; } |
Easy! We just say <div>${userFullName}</div>
!
OK… so now it’s updated every 120ms or so, and we can optimise it by adding @computedFrom()
in front:
|
@computedFrom('firstName','lastName') get userFullName() { return this.firstName + ' ' + this.lastName; } |
So now it will only update when one of those dependent variables changes. Awesome.
Now, I use full-name in several places on-screen, so I want the full name displayed with a variable delimiter, so let’s add a parameter to the call of ‘delim’.
Uh-oh, accessors don’t allow parameters, so it’s back to a method. But methods don’t allow dirty checking or @computedFrom… or @observable…
There are two suggested options; Signals or dummy dependency parameters:
Signals involve adding a ‘signal’ call whenever you want something rebound, and as a messaging system they can be acknowledged by many fields. In this case we can say
|
<div>${userFullName(' ') & signal:'my-signal'}</div> |
then in our code we might create lastNameChanged and firstNameChanged methods that call the signal method on an injected BindingSignaller:
|
signaler.signal('my-signal'); |
I’m not wild on this, as I don’t like arbitrary string event names, I don’t want to write interceptors for every property that might ever need a signal.
An alternative is to add dummy properties in the binding so that Aurelia automatically rebinds when one of them changes (thanks to some of @danyow’s awesome magic dependency detection).
|
userFullName(delimiter, ...params) { return this.firstName + ' ' + this.lastName; } |
The syntax ...params
is called a ‘spread’ operator and means ‘as many things as you like here’ (rather like the old **args in C coded apps)
Now in our view we can say <div>${userFullName(' ', firstName, lastName)}</div>
and our function will only be called when a dependent property updates! Awesome…ish. Now we have exposed the underlying implementation of our model into our view, and what if our method has a dependency on the time, or on 5 different private variables?
A final option is to do some sort of periodic update. We can use signals in a time:
|
setInterval(() => signaler.signal('my-signal'), 5000); |
or we could write a binding behavior:
|
import {sourceContext} from 'aurelia-binding'; export class DirtyBindingBehavior { bind(binding, source, frequency=500) { if (!binding.updateTarget) { throw new Error('Only property bindings and string interpolation bindings can be dirty checked. Trigger, delegate and call bindings cannot be dirty checked.'); } binding.dirtyTimer = setInterval(t => binding.call(sourceContext), frequency); } unbind(binding, source) { clearInterval(binding.dirtyTimer); } } |
This example means we can say
|
<div>${userFullName(' ') & dirty}</div> |
to have the field auto-update every 500ms, or we can override the frequency inline to be every second:
|
<div>${userFullName(' ') & dirty:1000}</div> |
This is not ideal, as we can’t have a userFullNameChanged event method triggered on our model, but it is a workaround that requires very little intrusive code.
Use any dirty checking style binding behavior with extreme caution. Performance can take a significant hit, but if it’s a suitable method, then go for it!