Cracking open results returned by Ember peekAll - ember.js

I'm playing around with peekAll(), trying to understand how it works for the ultimate purpose of iterating through the results.
In a route's model hook, I have:
var peekAllResults = this.store.peekAll('position');
console.log("peekAllResults = ", peekAllResults);
var peekAllResultsContent = peekAllResults.get('content');
console.log("peekAlLresultsContent = ", peekAllResultsContent);
This is returning data, as expected based on what I've got in my app.
In particular, here's what shows in the console:
So far so good. There are 8 records as expected based on what I've got going on.
But when when I add:
console.log("peekAllResultsContent.length=", peekAllResultsContent.length)
I get: peekAllResultsContent.length = 0
Same thing if I do peekAllResultsContent.get("length")
What is going on there?
I thought peekAll was a synchronous call that returned an array. Is there some trick to cracking it open and seeing what's actually in the array? I can't even get the length, so I figure I'm not on the right track.

Everything is wrapped into Ember.Model objects so you won't see clear results from console.log.
But there is no magic behind it. If the entities are already loaded into store you can get them via peekAll.
const positions = this.get('store').peekAll('position');
console.log('positions length', positions.get('length');
//we can iterate over them:
positions.forEach(position => {
console.log(position.get('name'));
};
//we can filter them:
const southOnlyPositions = positions.filter(position => position.get('direction') === 'south');
and so on...
Btw: even for promises you are not supposed to access content. You get the result like this:
const promises = this.get('store').findAll('position');
promises.then(positions => {
// positions here behave same as before
});

Related

How to remove elements from an Ember.ArrayController

In JavaScript, one can remove selected elements from an array by traversing it in reverse order and using splice(index, 1) to remove undesired elements. I'm trying to figure out how to do the same thing in Ember.js (without Ember Data).
I have an ArrayController and the associated Route's model function simply returns a JavaScript array. There an action in the controller, along the following lines:
removeElements: function () {
var i, arr = this.get('content'),
i = arr.length;
while (i) {
i -= 1;
if (arr[i].get('flag')) {
array.replace(i, 1);
}
}
This first appears to work in the browser. For example, if I have three elements and mark the first and third to be removed, the browser will leave the second item displayed. However, if I later try to mark the latter, Ember complains with Uncaught Error: Can't remove an item that has never been added.
I used replace() because Ember arrays don't have a splice method, but the docs also say that replace must be implemented in order to be used, but I don't quite understand where I'm supposed to implement it and I haven't found any sample implementations to guide me.
I've also tried various other methods, such as removeObject, removeObjects and more, but none did what I need.
You'd need to create your own array collection and implement replace on that collection (probably extending Ember.Array to get you started).
removeObject should work just fine (granted a little inefficient, though if the size of this list is small it's negligible):
removeElements: function () {
var controller = this,
list = this.toArray();
list.forEach(function(item){
if(item.get('flag')){
controller.removeObject(item);
}
});
}
using removeAt should give you the results you're looking for
removeElements: function () {
var i = this.get('length');
while (i--) {
if (this.objectAt(i).get('flag')) {
this.removeAt(i);
}
}
}

Returning an array from the model

I am very new to ember and what I am trying to do is get the data from the model into an array.
Currently I am doing:
var playerList = App.Player.find().toArray();
But it's not returning me an array of players it's returning an array of the objects? ex:
<App.Player:ember311:1>,<App.Player:ember332:2>,<App.Player:ember338:3>,<App.Player:ember344:4>,<App.Player:ember350:5>,<App.Player:ember356:6>,<App.Player:ember362:7>,<App.Player:ember368:8>
Any help would be appreciated. Thanks!
App.Player.find() returns a promise, and therefore you should wait until your records are fully loaded before doing operations on them. From your question it's not entirely clear how you player objects do look like. But to get to your players, and especially to your player's properties, you could do something like:
var playerList = App.Player.find().then(function (result) {
// This callback will fire when array is loaded
// and this is the correct way to get to the records
result.objectAt(0).get('name'); // assuming "name" is a property of your model
// here you can then loop over your obejcts
result.forEach(function(item) {
console.log(item.get('name'));
});
});
Hope it helps.

If statement for cookie - WebMatrix/Razor

I have set a cookie that I want to use to populate a form, so that users don't need to keep filling out the same form (it's submitting an inquiry to owners of holiday villas).
I've got it working fine if the cookie is already set, but it errors out if there is no cookie set.
I'm guessing I'll need to use an "if" statement, but don't quite know how to write the code.
Here is the code that sets the cookie...
Response.Cookies["BookingEnquiry"]["ReqName"] = Request["BookingReqName"];
Response.Cookies["BookingEnquiry"]["ReqEmail"] = Request["BookingReqEmail"];
Response.Cookies["BookingEnquiry"]["ReqPhone"] = Request["BookingReqPhone"];
Response.Cookies["BookingEnquiry"]["NumAdults"] = Request["BookingNumAdults"];
Response.Cookies["BookingEnquiry"]["NumChildren"] = Request["BookingNumChildren"];
Response.Cookies["BookingEnquiry"]["ReqMessage"] = Request["BookingReqMessage"];
Response.Cookies["BookingEnquiry"].Expires = DateTime.Now.AddHours(4);
}
Here are the variables that collect info from the cookie...
var reqname = Request.Cookies["BookingEnquiry"]["ReqName"];
var reqemail = Request.Cookies["BookingEnquiry"]["ReqEmail"];
var reqphone = Request.Cookies["BookingEnquiry"]["ReqPhone"];
var numadults = Request.Cookies["BookingEnquiry"]["NumAdults"];
var numchildren = Request.Cookies["BookingEnquiry"]["NumChildren"];
var reqmessage = Request.Cookies["BookingEnquiry"]["ReqMessage"];
and here is a sample input from the form...
<label>Name</label>
<input type="text" name="BookingReqName" id="BookingReqName" placeholder="full name…" value="#reqname">
In WebMatrix C#.net, I think you are looking for something like this:
if(Request["BookingReqName"] != null)
{
Response.Cookies["BookingEnquiry"]["ReqName"] = Request["BookingReqName"];
}
else
{
Response.Cookies["BookingReqName"] = ""; //<--Whatever default value you want (I've used an empty string here, so you, at least, won't get a null reference error).
}
Or you can use the same code as a one liner (to not clutter up your code, however this will decrease readability, obv.).
if(Request["BookingReqName"] != null){Response.Cookies["BookingEnquiry"]["ReqName"] = Request["BookingReqName"];}else{Response.Cookies["BookingReqName"] = ""; //<--Whatever default value you want (I've used an empty string here, so you, at least, won't get a null reference error).}
You'll just have to do that for all of your lines requesting cookie values.
The point is, though, that anything can go in the "else" block that helps you handle what to do when the cookie values have been cleared/expired (which you must always expect). You could redirect to a page that requests information from the user to reset any "forgotten" configurations, or, if you want to persist the data no matter what, consider storing these values in a database, instead, as those values won't clear/expire.
One last thing, if this doesn't help:
If you find yourself wondering what value to store in the cookie (the default value you wish to specify), because you need to know, right then and there, what it was supposed to have remembered, then I am afraid it is time to reconsider how you have structured the flow of data.
Sorry, but I have done that, once upon a time, only with Session variables, and it wasn't pretty :)
If you need any help with the best way(s) to transfer data between web pages, check this very helpful, concise link from Mike Brind's website: http://www.mikesdotnetting.com/Article/192/Transferring-Data-Between-ASP.NET-Web-Pages
It should just be the following
if(Request.Cookies["BookingEnquiry"] == null)
{
return; // <- if BookingEnquiry is null we end this routine
}
// Normal code flow here...
or something similar

How would I modify this ember.js function to return an Enumerable or Array

I have just begun learning ember.js, I have followed some tutorials and created a working example here:
App.Track.reopenClass({
find: function() {
var tracks = [];
$.ajax({
url: 'http://ws.spotify.com/lookup/1/.jsonuri=spotify:album:6J6nlVu4JMveJz0YM9zDgL&extras=track',
dataType: 'json',
context: this,
success: function(data, textStatus, jqXHR) {
$.each(data.album.tracks, function(index, value) {
track_id = value.href.replace("spotify:track:", "");
tracks.addObject(App.Track.create(value));
// I would rather do something like:
// tracks[track_id] = App.Track.create(value)
});
}
})
return tracks;
}
});
This function hits an API and loops through the returned data to populate the tracks object (tracks.addObject(App.Track.create(value));) and return it.
Rather than getting an ordinary object back from this function, I would like to get an Enumerable / Array so I can manipulate it with filterProperty or pull out tracks by id (There is a track_id which I would like to use as the array index).
All of my attempts to use an array have broken ember's magical ability to update the view when the ajax call populates the tracks.
Can anyone modify http://jsfiddle.net/ZEzwn/ to return an Enumerable (preferably an Array) but still update the view automatically?
As your method already returns an Array (because you have Ember prototype extension enabled), doing:
var tracks = [];
is equivalent to
var tracks = Ember.A();
On ajax request success, you're just populating the array, so you could use Ember.Array methods like filterProperty.
Just one thing about using id as array key, you really SHOULD NOT, as Ryan Bigg says in its blog:
However, if the variant’s id is [something a little higher, like] 1,013,589,413, then you start to run into problems.
In that case, JavaScript would create a one billion, thirteen million, five hundred and eighty-nine thousand, four hundred and fourteen element array. All to store one value in, right at the end.
Ok this is now working, as louiscoquio pointed out, tracks IS an enumerable object and I can do stuff like
tracks.filterProperty('href', 'spotify:track:7x7F7xBqXqr0L9wqJ3tuQW')
tracks.getEach('name')
tracks.get('firstObject')

Adding item to filtered result from ember-data

I have a DS.Store which uses the DS.RESTAdapter and a ChatMessage object defined as such:
App.ChatMessage = DS.Model.extend({
contents: DS.attr('string'),
roomId: DS.attr('string')
});
Note that a chat message exists in a room (not shown for simplicity), so in my chat messages controller (which extends Ember.ArrayController) I only want to load messages for the room the user is currently in:
loadMessages: function(){
var room_id = App.getPath("current_room.id");
this.set("content", App.store.find(App.ChatMessage, {room_id: room_id});
}
This sets the content to a DS.AdapterPopulatedModelArray and my view happily displays all the returned chat messages in an {{#each}} block.
Now it comes to adding a new message, I have the following in the same controller:
postMessage: function(contents) {
var room_id = App.getPath("current_room.id");
App.store.createRecord(App.ChatMessage, {
contents: contents,
room_id: room_id
});
App.store.commit();
}
This initiates an ajax request to save the message on the server, all good so far, but it doesn't update the view. This pretty much makes sense as it's a filtered result and if I remove the room_id filter on App.store.find then it updates as expected.
Trying this.pushObject(message) with the message record returned from App.store.createRecord raises an error.
How do I manually add the item to the results? There doesn't seem to be a way as far as I can tell as both DS.AdapterPopulatedModelArray and DS.FilteredModelArray are immutable.
so couple of thoughts:
(reference: https://github.com/emberjs/data/issues/190)
how to listen for new records in the datastore
a normal Model.find()/findQuery() will return you an AdapterPopulatedModelArray, but that array will stand on its own... it wont know that anything new has been loaded into the database
a Model.find() with no params (or store.findAll()) will return you ALL records a FilteredModelArray, and ember-data will "register" it into a list, and any new records loaded into the database will be added to this array.
calling Model.filter(func) will give you back a FilteredModelArray, which is also registered with the store... and any new records in the store will cause ember-data to "updateModelArrays", meaning it will call your filter function with the new record, and if you return true, then it will stick it into your existing array.
SO WHAT I ENDED UP DOING: was immediately after creating the store, I call store.findAll(), which gives me back an array of all models for a type... and I attach that to the store... then anywhere else in the code, I can addArrayObservers to those lists.. something like:
App.MyModel = DS.Model.extend()
App.store = DS.Store.create()
App.store.allMyModels = App.store.findAll(App.MyModel)
//some other place in the app... a list controller perhaps
App.store.allMyModels.addArrayObserver({
arrayWillChange: function(arr, start, removeCount, addCount) {}
arrayDidChange: function(arr, start, removeCount, addCount) {}
})
how to push a model into one of those "immutable" arrays:
First to note: all Ember-Data Model instances (records) have a clientId property... which is a unique integer that identifies the model in the datastore cache whether or not it has a real server-id yet (example: right after doing a Model.createRecord).
so the AdapterPopulatedModelArray itself has a "content" property... which is an array of these clientId's... and when you iterate over the AdapterPopulatedModelArray, the iterator loops over these clientId's and hands you back the full model instances (records) that map to each clientId.
SO WHAT I HAVE DONE
(this doesn't mean it's "right"!) is to watch those findAll arrays, and push new clientId's into the content property of the AdapterPopulatedModelArray... SOMETHING LIKE:
arrayDidChange:function(arr, start, removeCount, addCount){
if (addCount == 0) {return;} //only care about adds right now... not removes...
arr.slice(start, start+addCount).forEach(function(item) {
//push clientId of this item into AdapterPopulatedModelArray content list
self.getPath('list.content').pushObject(item.get('clientId'));
});
}
what I can say is: "its working for me" :) will it break on the next ember-data update? totally possible
For those still struggling with this, you can get yourself a dynamic DS.FilteredArray instead of a static DS.AdapterPopulatedRecordArray by using the store.filter method. It takes 3 parameters: type, query and finally a filter callback.
loadMessages: function() {
var self = this,
room_id = App.getPath('current_room.id');
this.store.filter(App.ChatMessage, {room_id: room_id}, function (msg) {
return msg.get('roomId') === room_id;
})
// set content only after promise has resolved
.then(function (messages) {
self.set('content', messages);
});
}
You could also do this in the model hook without the extra clutter, because the model hook will accept a promise directly:
model: function() {
var self = this,
room_id = App.getPath("current_room.id");
return this.store.filter(App.ChatMessage, {room_id: room_id}, function (msg) {
return msg.get('roomId') === room_id;
});
}
My reading of the source (DS.Store.find) shows that what you'd actually be receiving in this instance is an AdapterPopulatedModelArray. A FilteredModelArray would auto-update as you create records. There are passing tests for this behaviour.
As of ember.data 1.13 store.filter was marked for removal, see the following ember blog post.
The feature was made available as a mixin. The GitHub page contains the following note
We recommend that you refactor away from using this addon. Below is a short guide for the three filter use scenarios and how to best refactor each.
Why? Simply put, it's far more performant (and not a memory leak) for you to manage filtering yourself via a specialized computed property tailored specifically for your needs