Ember.computed not updating with Ember.Array - ember.js

I'm currently working on a application that works with with websockets to implement a real-time chat (along with many other things).
However, I have some issues with a Ember.computed member that observes a Ember.NativeArray.
I tried a few things found while googling the issue, but none of them seem to work!
My array is a Ember.NativeArray defined in a service like this:
chatMessages: Ember.A()
My computed property, in my component, is defined as such:
messages: Ember.computed('liveSocket.chatMessages', function() {
return this.get('liveSocket').get('chatMessages').toArray().reverse();
}),
And I set objects to the array in my service as follows:
this.get('chatMessages').set(data.id, null);
If I observe the array directly, nothing changes (I may be confused with that, but isn't that the point of embedding an array in a Ember.Array object?). Now, I can easily watch the array for new elements, by observing the liveSocket.chatMessages.length property. Everything works well and new messages are correctly added. The problem is when I try to remove messages. The websocket server is badly engineered, and changing it is a non-possibility: it doesn't remove the indexes of deleted messages, and returns null objects instead.
So I not only need to watch changes to the array's length, but also to its elements. I tried adding liveSocket.chatMessages.#each to my observed elements list, but that doesn't work either. To sum it up:
// Never updates
Ember.computed('liveSocket.chatMessages', ...)
// Updates only on push/pop (as expected)
Ember.computed('liveSocket.chatMessages.length', ...)
// Still only updates on push/pop
Ember.computed('liveSocket.chatMessages.length', 'liveSocket.chatMessages.#each', ...)
Am I missing something here? Is there a way to observe an array's length and all the elements it contains? Is my approach to this wrong? I am using Ember 2.6 and Ember Data 2.6.1.
Thanks!

I assume in chatMessages you have objects with property title.
So if you want to trigger every time title of any objects changed, you need to compute on :
Ember.computed('liveSocket.chatMessages.#each.title', ...);

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.

peekRecord() is not working but peekAll() is working

My backend always responds with all available data and it took a considerably amount of time. So I'm reloading store periodically and I plan to use peekAll() and peekRecord().
My code is:
model: function() {
return Ember.RSVP.hash({
'clusters': this.store.peekAll('cluster'),
'single': this.store.peekRecord('cluster', 'cluster::My')
});
When code is executed, at first I can see that both of these items do not contain content. After few seconds data are loaded to store and I can see content 'clusters' on template as expected. But 'single' is still completely without content ({{model.single}} does not return nothing in template). But when I have a button with action:
alert(this.store.peekRecord('cluster', 'cluster::My'));
I can see that the record was found. Records are also available via Ember Inspector. What am I doing wrong that only peekAll() works in model for me.
The semantics of both methods are:
store.peekAll returns a live array that is updated as the store is updated.
store.peekRecord returns the corresponding object in the current cache, or null, and it does not update.
So the behaviour you're observing is the expected one. If you want to use the peek methods, my advise is to make sure that the initial request has finished loading before fetching any data from the store.

Polymer not unlistening properly

I use Polymer.Templatizer to stamp templates of paper-input collections into a custom-element which has a listener 'change':'_doStuff'.
Basically when I stamp 20 paper-inputs via Polymer.dom(this).appendChild(template.root) a bunch of listeners are added , as you can see in the graph.
Then I call another function that goes through all of those elements and does Polymer.dom(paperInput.parentNode).removeChild(paperInput) and adds another set of inputs. But it just doesn't detach listeners on those for some reason and the heap is growing with every iteration...
The listener change on the host element, I believe, is neither detached.
What am I doing wrongly?
EDIT: I know what it is, it's not garbage collection problem, but Polymer creates anonymous Polymer.Base instances when templatizing and actually puts all template's children into those. of course the instances are not removed in any way. I wish I knew how to kill those not to reduce performance of the app. By defining custom elements instead? Looks like an overhead to me...
A way to free your memory could be to unlisten all of your listeners before you detach your elements, in order them to be unreferenced first. For example :
for (var i = 0; i < inputList.length; i++) {
this.unlisten(inputList[i], 'change', 'doStuff');
Polymer.dom(inputList[i].parentNode).removeChild(inputList[i]);
}

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.

Ember.js Data how to clear datastore

I am experiementing with Ember.js and have setup a small app where users can login and logout. When the user logs out I want to clear all of the currently cached records in the Data Store.
Is there a way to do this or would I have to force the browser to reload the page?
I know this question is from 2013. But since Ember Data 1.0.0-beta.17 (May 10, 2015) there's a straightforward way of clearing the datastore:
store.unloadAll()
(More here: http://emberigniter.com/clear-ember-data-store/)
It looks like, as of today, there is still no generic way of fully forcing a store cleanup. The simplest workaround seems to loop through all your types (person, ...) and do:
store.unloadAll('person');
As seen here
Clearing the data store is not yet supported in Ember-Data. There is an open issue concerning this on the Github tracker.
A cleaner & generic approach. Just extend or reopen store & add a clear method like this.
DS.Store.extend({
clear: function() {
for(var key in this.typeMaps)
{
this.unloadAll(this.typeMaps[key].type);
}
}
});
There is:
App.reset();
But that does more than clear out the data store and we've occasionally seen errors where store.pushPayload tries to push data onto an object marked destroyed from calling App.reset();.
Been we've been using:
store.init();
Which just creates a new empty store and works great but unfortunately is a private method.
This can now be done with store.destroy(). It unloads all records, but it also available for immediate use in reloading new records. I have confirmed this as of 1.0.0-beta.15. It doesn't appear to be in the documentation, but it's been working for me.
The alternative would be iterating the store's typeMaps and running store.unloadAll(typeMap.typeName), but I'm not sure it's entirely necessary.
Deleting record by record in model. deleteOrgs of this jsBin:
deleteOrgs: function(){
var len;
while(len = this.get('model.length')) {
// must delete the last object first because
// this.get('model.length') is a live array
this.get('model').objectAt(len-1).deleteRecord();
}
this.get('store').commit();
}
( As of August 2013, there is currently a problem with lingering deleted data. )