2 computed properties based on the same dependency fire only once - ember.js

Having 2 computed properties based on the same dependency only one computed property runs. The docs says that it is cached what about a situation when I'd like to have the following:
foo: (->
console.log 'foo'
).property('dependency')
bar: (->
console.log 'bar'
).property('dependency')
Now the bar isn't called and I have to resort to observer. Can I make it work?
Edit
The question is about computed properties but it wasn't reflected in the example code - instead of property I used observes. It is now changed. Sorry for confusion.
Edit#2
I modified the great example by #MilkyWayJoe so that it now looks like my solution. Unfortunately (or fortunately) it works, but my solution didn't. Here's the gist what I was after:
With the help of a slider I could set a balance value to be transferred to another credit card provider. Let's say that the value is transferValue. Whenever it changed I had to calculate the annual interest to which the transfer fee was added.
So for example let's say that in my current credit card I have $1000 and the annual interest rate is 19%. It's way too much so I look for another, cheaper solution. It turns out that Bank X offers Balance Transfer Credit Card Y which interest rate is 10% + 3.5% transfer fee.
OK. So I set $1000 on the slider and here goes the magic. I want to calculate interest rate and transfer fee whenever the value changes.
In the modified example it works: http://jsfiddle.net/gqSMU/2/
but failed to do so in my first solution. It was kind of this one:
cardInterest: (->
apr = #get 'purchaseRate'
amount = #get 'transferValue'
#get('calculatedTransferFee') + #calculateInterest apr, amount
).property('transferValue', 'calculatedTransferFee')
As you can see it accesses calculatedTransferFee. The problem was that the value wasn't recalculated. I'm not sure it is worth of mentioning but in the first solution only cardInterest was requested by Handlebars template.
And this is my current solution with observer:
calculatedTransferFee: 0
transferValueDidChange: (->
if #get('isCurrentCardChosen')
transferFee = parseFloat #get('balanceTransferRate') / 100
transferValue = #get 'transferValue'
calculatedTransferFee = if isNaN(transferFee) then 0 else transferFee * transferValue
#set 'calculatedTransferFee', calculatedTransferFee
).observes('transferValue')
It isn't a nice solution, is it? That's why I thought it may be a better solution than to resort to an observer.
I hope that now it is clearer. I'd be grateful for any further feedback!

Not quite sure if I understand what you're trying to achieve since you're talking about property but your code is using observes (I realize this is a conceptual sample code, just not sure where you're going).
Usually, property will be a "reactive accessor" to a value, and 99.9% of the time you want it to be cached (it is by default, unless you say .property('whatever').volatile()), while observe will fire a function when whatever property it is watching changes. If you just want to have two properties firing for the same dependency you could:
App.SomeModel = Em.Object.extend({
someDependency: true,
foo: function() {
// all that's in here will fire only once when 'dependency' changes, and store
// the returning value in cache, and every time something reads this property,
// it will retrieve the cached value.
// A way to test this, is to run the following from your View:
// "alert(this.controller.get('content.foo'));"
// It will alert the string but will not log "whatever bro" again.
console.log('whatever bro');
return "this.foo %# a dependency".fmt(this.get('someDependency') ? "has" : "doesn't have");
}.property('someDependency'),
bar: function() {
// same as above
console.log('whatever dude');
return "this.bar %# a dependency".fmt(this.get('someDependency') ? "has" : "doesn't have");
}.property('someDependency'),
nope: function() {
// same as above, except this is volatile
// and will fire the console.log every time
console.log('y\'all need science');
return "this.nope %# a dependency".fmt(this.get('someDependency') ? "has" : "doesn't have");
}.property('someDependency').volatile()
});
(see fiddle)
If you need it to be logged everytime (perhaps for debugging or whatever reason), you could use volatile or observes.
If I'm tripping and this's not what you want, perhaps you could rephrase your question or refresh the sample code to something closer to your real-life scenario to clarify what's being asked.

Related

Emberjs inside of get computed making request to backend multiple times cause infinite loop

I have a table basically in every row i have get function that makes a backend request with store service. But somehow when there is one row it works expect, but when there is multiple rows it always try to recalculate get function which makes sending infinite request to backend. I am using glimmer component
I cannot use model relation on ember side at this point, there is deep chain on backend side. Thats why i am making backend request.
get <function_name>() {
return this.store.query('<desired_model_name>', { <dependent1_id>: <dependent1_id_from_args>, <dependent2_id>: <dependent2_id_from_args> });
}
I fixed this problem with using constructor. But do you have any idea why this get function re-calculate all the time? Dependent_ids are constant.
Weird thing is when results are [] empty array it does not re calculate every time. Even the query results are same it still try to recalculate every time and making infinite request to backend.
But do you have any idea why this get function re-calculate all the time?
When something like this happens, it's because you're reading #tracked data that is changed later (maybe when the query finishes).
because getters are re-ran every access, you'll want to throw #cached on top of it,
// cached is available in ember-source 4.1+
// or as early as 3.13 via polyfill:
// https://github.com/ember-polyfills/ember-cached-decorator-polyfill
import { cached } from '#glimmer/tracking';
// ...
#cached
get <function_name>() {
return this.store.query(/* ... */);
}
this ensures a stable object reference on the getter that the body of the getter only re-evaluates if tracked data accessed within the getter is changed.
Weird thing is when results are [] empty array it does not re calculate every time. Even the query results are same it still try to recalculate every time and making infinite request to backend.
Given this observation, it's possible that when query finishes, that it's changing tracked data that it, itself is consuming during initial render -- in which case, you'd still have an infinite loop, even with #cached (because tracked-data is changing that was accessed during render).
To get around that is fairly hard in a getter.
Using a constructor is an ok solution for getting your initial data, but it means you opt out of reactive updates with your query (if you need those, like if the query changes or anything).
If you're using ember-source 3.25+ and you're wanting something a little easier to work with, maybe ember-data-resourecs suits your needs
the above code would be:
import { query } from 'ember-data-resources';
// ...
// in the class body
data = query(this, 'model name', () => ({ query stuff }));
docs here
This builds off some primitives from ember-resources which implement the Resource pattern, which will be making a strong appearance in the next edition of Ember.

Understanding the point of supply blocks (on-demand supplies)

I'm having trouble getting my head around the purpose of supply {…} blocks/the on-demand supplies that they create.
Live supplies (that is, the types that come from a Supplier and get new values whenever that Supplier emits a value) make sense to me – they're a version of asynchronous streams that I can use to broadcast a message from one or more senders to one or more receivers. It's easy to see use cases for responding to a live stream of messages: I might want to take an action every time I get a UI event from a GUI interface, or every time a chat application broadcasts that it has received a new message.
But on-demand supplies don't make a similar amount of sense. The docs say that
An on-demand broadcast is like Netflix: everyone who starts streaming a movie (taps a supply), always starts it from the beginning (gets all the values), regardless of how many people are watching it right now.
Ok, fair enough. But why/when would I want those semantics?
The examples also leave me scratching my head a bit. The Concurancy page currently provides three examples of a supply block, but two of them just emit the values from a for loop. The third is a bit more detailed:
my $bread-supplier = Supplier.new;
my $vegetable-supplier = Supplier.new;
my $supply = supply {
whenever $bread-supplier.Supply {
emit("We've got bread: " ~ $_);
};
whenever $vegetable-supplier.Supply {
emit("We've got a vegetable: " ~ $_);
};
}
$supply.tap( -> $v { say "$v" });
$vegetable-supplier.emit("Radish"); # OUTPUT: «We've got a vegetable: Radish␤»
$bread-supplier.emit("Thick sliced"); # OUTPUT: «We've got bread: Thick sliced␤»
$vegetable-supplier.emit("Lettuce"); # OUTPUT: «We've got a vegetable: Lettuce␤»
There, the supply block is doing something. Specifically, it's reacting to the input of two different (live) Suppliers and then merging them into a single Supply. That does seem fairly useful.
… except that if I want to transform the output of two Suppliers and merge their output into a single combined stream, I can just use
my $supply = Supply.merge:
$bread-supplier.Supply.map( { "We've got bread: $_" }),
$vegetable-supplier.Supply.map({ "We've got a vegetable: $_" });
And, indeed, if I replace the supply block in that example with the map/merge above, I get exactly the same output. Further, neither the supply block version nor the map/merge version produce any output if the tap is moved below the calls to .emit, which shows that the "on-demand" aspect of supply blocks doesn't really come into play here.
At a more general level, I don't believe the Raku (or Cro) docs provide any examples of a supply block that isn't either in some way transforming the output of a live Supply or emitting values based on a for loop or Supply.interval. None of those seem like especially compelling use cases, other than as a different way to transform Supplys.
Given all of the above, I'm tempted to mostly write off the supply block as a construct that isn't all that useful, other than as a possible alternate syntax for certain Supply combinators. However, I have it on fairly good authority that
while Supplier is often reached for, many times one would be better off writing a supply block that emits the values.
Given that, I'm willing to hazard a pretty confident guess that I'm missing something about supply blocks. I'd appreciate any insight into what that might be.
Given you mentioned Supply.merge, let's start with that. Imagine it wasn't in the Raku standard library, and we had to implement it. What would we have to take care of in order to reach a correct implementation? At least:
Produce a Supply result that, when tapped, will...
Tap (that is, subscribe to) all of the input supplies.
When one of the input supplies emits a value, emit it to our tapper...
...but make sure we follow the serial supply rule, which is that we only emit one message at a time; it's possible that two of our input supplies will emit values at the same time from different threads, so this isn't an automatic property.
When all of our supplies have sent their done event, send the done event also.
If any of the input supplies we tapped sends a quit event, relay it, and also close the taps of all of the other input supplies.
Make very sure we don't have any odd races that will lead to breaking the supply grammar emit* [done|quit].
When a tap on the resulting Supply we produce is closed, be sure to close the tap on all (still active) input supplies we tapped.
Good luck!
So how does the standard library do it? Like this:
method merge(*#s) {
#s.unshift(self) if self.DEFINITE; # add if instance method
# [I elided optimizations for when there are 0 or 1 things to merge]
supply {
for #s {
whenever $_ -> \value { emit(value) }
}
}
}
The point of supply blocks is to greatly ease correctly implementing reusable operations over one or more Supplys. The key risks it aims to remove are:
Not correctly handling concurrently arriving messages in the case that we have tapped more than one Supply, potentially leading us to corrupt state (since many supply combinators we might wish to write will have state too; merge is so simple as not to). A supply block promises us that we'll only be processing one message at a time, removing that danger.
Losing track of subscriptions, and thus leaking resources, which will become a problem in any longer-running program.
The second is easy to overlook, especially when working in a garbage-collected language like Raku. Indeed, if I start iterating some Seq and then stop doing so before reaching the end of it, the iterator becomes unreachable and the GC eats it in a while. If I'm iterating over lines of a file and there's an implicit file handle there, I risk the file not being closed in a very timely way and might run out of handles if I'm unlucky, but at least there's some path to it getting closed and the resources released.
Not so with reactive programming: the references point from producer to consumer, so if a consumer "stops caring" but hasn't closed the tap, then the producer will retain its reference to the consumer (thus causing a memory leak) and keep sending it messages (thus doing throwaway work). This can eventually bring down an application. The Cro chat example that was linked is an example:
my $chat = Supplier.new;
get -> 'chat' {
web-socket -> $incoming {
supply {
whenever $incoming -> $message {
$chat.emit(await $message.body-text);
}
whenever $chat -> $text {
emit $text;
}
}
}
}
What happens when a WebSocket client disconnects? The tap on the Supply we returned using the supply block is closed, causing an implicit close of the taps of the incoming WebSocket messages and also of $chat. Without this, the subscriber list of the $chat Supplier would grow without bound, and in turn keep alive an object graph of some size for each previous connection too.
Thus, even in this case where a live Supply is very directly involved, we'll often have subscriptions to it that come and go over time. On-demand supplies are primarily about resource acquisition and release; sometimes, that resource will be a subscription to a live Supply.
A fair question is if we could have written this example without a supply block. And yes, we can; this probably works:
my $chat = Supplier.new;
get -> 'chat' {
web-socket -> $incoming {
my $emit-and-discard = $incoming.map(-> $message {
$chat.emit(await $message.body-text);
Supply.from-list()
}).flat;
Supply.merge($chat, $emit-and-discard)
}
}
Noting it's some effort in Supply-space to map into nothing. I personally find that less readable - and this didn't even avoid a supply block, it's just hidden inside the implementation of merge. Trickier still are cases where the number of supplies that are tapped changes over time, such as in recursive file watching where new directories to watch may appear. I don't really know how'd I'd express that in terms of combinators that appear in the standard library.
I spent some time teaching reactive programming (not with Raku, but with .Net). Things were easy with one asynchronous stream, but got more difficult when we started getting to cases with multiple of them. Some things fit naturally into combinators like "merge" or "zip" or "combine latest". Others can be bashed into those kinds of shapes with enough creativity - but it often felt contorted to me rather than expressive. And what happens when the problem can't be expressed in the combinators? In Raku terms, one creates output Suppliers, taps input supplies, writes logic that emits things from the inputs into the outputs, and so forth. Subscription management, error propagation, completion propagation, and concurrency control have to be taken care of each time - and it's oh so easy to mess it up.
Of course, the existence of supply blocks doesn't stop being taking the fragile path in Raku too. This is what I meant when I said:
while Supplier is often reached for, many times one would be better off writing a supply block that emits the values
I wasn't thinking here about the publish/subscribe case, where we really do want to broadcast values and are at the entrypoint to a reactive chain. I was thinking about the cases where we tap one or more Supply, take the values, do something, and then emit things into another Supplier. Here is an example where I migrated such code towards a supply block; here is another example that came a little later on in the same codebase. Hopefully these examples clear up what I had in mind.

Undesirable scope bleed between components in Ember

I have a component [ui-button] (github) where I'm adding a wrapping component called ui-buttons (demo here). The problem is that the wrapping component seems to receive registrations not only from its children but from ALL children that are on the page! Effectively this property is acting like a static variable across all instances of ui-buttons. I didn't even know you could do this and in this case its definitely an undesirable effect.
In the demo link above try clicking on the the "disable the group" button and notice that it disables ALL buttons. So what am I doing?
Structurally it looks like this:
{{#ui-buttons as |group|}}
{{ui-radio-button title='foo' group=group}}
{{ui-radio-button title='bar' group=group}}
{{ui-radio-button title='baz' group=group}}
{{/ui-buttons}}
In this process I have child elements (e.g., ui-radio-button) register themselves with ui-buttons. The item-level registration code is:
_registration: on('init', function() {
const group = this.get('group');
if(group) {
group._registerItem(this);
}
}),
and the group-level registration code is:
_registerItem: function(child) {
console.log('registering %o with %o', child, this.get('elementId'), this.get('_registeredItems.length'));
this.get('_registeredItems').pushObject(child);
},
If you note the group-level registration has a "console.log" statement and that produces very encouraging results (it recognizes the element ID of the group as being distinct) alongside worrisome results (the registry "length" continues to grow across all):
I suspect this is down to async or this complexities but I'm now at a loss on how to proceed.
Your problem is this line of code
_registeredItems: new A([]),
That creates a single array one time (I believe when the component is parsed - on application load) and all your components are using it..
Best bet is to change it to
_registeredItems: Ember.computed(function() {
return new A([]);
})
When _registeredItems is first accessed it creates the new array.

clarification of Ember's this.get() method

This is more of a general question than anything specific, but I'm new to ember and don't really understand when and how to use Ember's this.get('foo') (and similarly bar.get('foo')).
For example, in my route I have a user object on which there is a property called credits
user = this.store.find('user', userId)
console.log(user)
credits = user.get('credits')
console.log(credits)
my console.log shows me that user.content._data.credits has a value and also has a methods called get content and - more specifically - get credits. However, console.logging credits always returns undefined.
if i set the user as a model though, using this.get('user.credits') in my controller works fine.
I've read the docs about the advantages .get offers with computed properties, but could anyone concisely explain some ground rules of when to use this.get('foo') vs. bar.get('foo') and why it works in some places but not others.
Thanks!
You always need to use Em.get and Em.set for getting and setting properties of an Ember.Object. It's the basic rule. Without it you may find variety of bugs in observers/rendering and other places.
There is a misunderstanding of operations flow in your code: this.store.find always returns a promise object, not the actual data that you request. Detailed:
user = this.store.find('user', userId) // user - Em.RSVP.Promise object
console.log(user) // logs the Em.RSVP.Promise object
credits = user.get('credits') // gets property 'credits' of the Em.RSVP.Promise object (user)
console.log(credits) // always logs `undefined` because there is no property called 'credits' in Em.RSVP.Promise prototype
We must to rely on async nature of Promise and to rewrite this code like this:
this.store.find('user', userId).then(function(user) {
console.log(user) // logs the App.UserModel object with actual data
credits = user.get('credits') // gets property 'credits' of the App.UserModel instance (user)
console.log(credits) // logs real data from the model
});
There is another important part of getting properties from a model object, if you're using ember-data as data layer: you need to declare all fields of the model that you wish to get afterwards.

Fallback on geolocation

I'm having trouble with the most basic aspect of geolocation - no matter what I do, I don't seem to be able to get the fallback to trigger. Here's the code:
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(function(position){
var latitude = position.coords.latitude;
var longitude = position.coords.longitude;
var latNum = parseFloat(latitude);
var longNum = parseFloat(longitude);
This is immediately followed by a nested bunch of if...else if statements that trigger different functions based on the user's location within one of a number of defined areas and an else statement to catch the condition where the user is not in any of the defined locations. This part all works fine, including the 'else' condition at the end. Where it falls over is if the user's device does not have geolocation enabled, or the user denies access to location data when prompted.
The code supposed to capture this is simply:
} else {
function10();
}
I have tried this in FF, Safari and Chrome with the same results: if I disable location services or deny access when prompted, the final 'else' function does not trigger.
I've looked at countless examples of this sort of elegant failure on geolocation and can't see why it doesn't work.
I'd be truly grateful for any clues where I went wrong.
OK - problem solved! I'm not sure if I feel just silly or enlightened, but for the benefit of anyone else with the same problem, here's the solution:
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(function(position){
// Do something here if you get position data
},
function() {
// Do something else if you don't get any position data
}
);
}
Where i went wrong, I think, is that I needed to look for a failure of the function(position)rather than the absence of a geolocation enabled agent. The second function within the same if condition provides the action in the event of no position data being returned from the browser, no matter what the reason. The final 'else' statement in the original code (above) would only be triggered on a device with no geolocation capacity.
This all makes sense now, but I have to say the documentation on Google, and many of the tutorial sites was far from clear on this, with frequent references to my initial syntax covering the situation where geolocation capacity was not enabled (as distinct from not present).
Thanks to this answer on SO for pointing me in the right direction.