Ember data difference between find() and getById() - javascript

I know that there is a question named : Ember data: what is difference between find and findById?. However, correct if I'm wrong, but I think that it relates to an older version of Ember data since I can't find this method in the embet-data doc.
I was trying to insert a new category in my catalog. This wouldn't work:
newRecord: function() {
catalog = this.store.find('catalog', 1);
record = this.store.createRecord( 'category', {category_name_fr_sh: 'Nouvelle categorie'});
catalog.get('catalog_categories_ids').pushObject(record);
this.set('content', record);
},
But this work :
newRecord: function() {
catalog = this.store.getById('catalog', 1);
record = this.store.createRecord( 'category', {category_name_fr_sh: 'Nouvelle categorie'});
catalog.get('catalog_categories_ids').pushObject(record);
this.set('content', record);
},
The doc says
Get a record by a given type and ID without triggering a fetch.
This method will synchronously return the record if it's available. Otherwise, it will return null.
I really don't understand why "trggering the fetch" wouldn't work. I tought that the find() first look if it's in the store cache and only fetch if it doesn't find it. Can someone enlighten me?

this.store.find('catalog', 1); doesn't return the record, it return a DS.PromiseObject. Because, if your record is not present in the record cache, a request to the server is needed. If the record is already loaded, you still have the promise object, to keep the same method behavior, but no request is sent to the server.
this.store.getById('catalog', 1); return the object from the record cache if present. Probably this work because you already loaded the catalogs using this.store.find('catalog'); or this.store.find('catalog', 1);
You can get the catalog record from DS.PromiseObject using then method:
newRecord: function() {
var self = this;
var catalogPromise = this.store.find('catalog', 1);
catalogPromise.then(function(catalog) {
var record = this.store.createRecord( 'category', {category_name_fr_sh: 'Nouvelle categorie'});
catalog.get('catalog_categories_ids').pushObject(record);
self.set('content', record);
})
},

Related

CouchDB update handlers: document id must not be empty

I wrote simple update handler:
{
"handler": "function (doc, req) { if (req.userCtx.roles.indexOf('editor') < 0) return [req.userCtx, 'access error']; if (!doc) { return [doc, 'nothing']; } doc.date = new Date(); doc.edited_by = req.userCtx.name; return [doc, toJSON(doc)]; }"
}
When I'm trying to query from Mozilla HttpRequester http://192.168.0.34:5984/records/_design/records/_update/handler, i'm getting
with that's data
{"title":"testtest","content":"test"}
And getting
{"error":"bad_request","reason":"Document id must not be empty"}
I've added _id to JSON
{"_id":"testid","title":"testtest","content":"test"}
No luck.
Update: and running that query in browser returns nothing, which may indicate that there's no document sent, but that's not true. What i'm doing wrong?
In CouchDB update handler _id never been set automatically, so you must do it manually. The simplest solution is using uuids, which available at req.uuid.
One important thing about update handlers. Their syntax someplace strange: you can't take data and return doc, no. Your update handler taking doc and req objects, where first is a changing document (check document existence using !doc, if it returns true — you can create new) and the second is request data, including access info, uuid and body. We're getting data from server not in req, but in req.body, which is raw string. Is must be parsed first using JSON.parse(req.body). After doing some checks, we can return — but return also not easy. If we have a doc — we're changing it, if not — we're creating new object. Return must be done by array [data_to_be_written, return_data], where first item is document for database and the second is a string (and only string, use toJSON(object) to return objects).
In addition there's my update handler function example, there's a lot of dirty debug code and it can be wrong, but it working on creation (update still unchecked).
function (doc, req) {
if (req['userCtx']['roles'].indexOf('editor') < 0) {
return [null, 'Access denied'];
}
var rcv = JSON.parse(req.body);
if (!doc) {
if (!rcv['_id'] && !!rcv['title'] && !!rcv['content']) {
return [{ '_id':req.uuid,'title': rcv['title'], 'content': rcv['content'], 'date': new Date(), 'edited_by': req['userCtx']['name'] }, 'Created' + toJSON(req)];
}
return [null, 'Empty' + toJSON({ 'no_id': !rcv['_id'], 'title': !!rcv['title'], 'content': !!rcv['content'], 'body':req.body })];
}
doc['date'] = new Date();
doc['edited_by'] = req['userCtx']['name'];
return [doc, 'Edited' + toJSON(doc)];
}
P.S> and remember, that even update handler working over validation func, so if your data can't pass validation, it will not be written anyway.

EmberJS is not loading up the model correctly

At a loss on this one.
I'm using Ember and Ember data. I've got this extra implementation of ic-ajax to make GET, POST and PUT calls. Anyway, i'm trying to make a GET call then turn those results into model instances.
return this.GET('/editor')
.then((data) => {
return data.drafts.map((draftData) => {
let draft = this.store.find('draft',draftData.id);
console.log(draft.get('type'));
return draft;
});
});
My API returns proper data as data.drafts. This map is supposed to return an array of promises that resolve to draft models. It does not. It resolves to a draft model that has id, date, and title. But that's it. I have 25 others attributions.
In another part of the application i'm getting drafts using findAll on the model. And those models look fine. But when I try store.findRecord('draft',id) i get these fake objects.
-- edit
This is what my ReOpenClass method looks like for getting an array of objects from the server and turning them into ember objects
search(critera) {
let query = { search: critera };
let adapter = this.store.adapterFor('application');
let url = adapter.buildURL('article','search');
return adapter.ajax(url,'GET', { data: query }).then(response => {
let articleRecords = response.articles.map((article) => {
let record;
try {
record = this.store.createRecord('article', article);
} catch(e) {
record = this.store.peekRecord('article', article.id);
}
return record;
});
return articleRecords;
});
},
So far I can't find a better way to pull this off.

Kendo UI datasource sync() will not work

In the code below, the fetch() and sync() methods are not doing anything.
I am trying to see how the data in my localStorage gets updated and the methods are not updating it (example LS string is in the code)
Where am I going wrong?
function makeWorkingLS(collDesc, projDesc, Id, Description, ElapsedSeconds, ElapsedTime, WorkItemType){
//Create observable object from params
var activeTaskObject = kendo.observable ({
client: collDesc,
project: projDesc,
taskId: Id,
description: Description,
elapsedSeconds: ElapsedSeconds,
elapsedTime: ElapsedTime,
comment: WorkItemType
});
// example string in localStorage:
//{"client":"Morken Mindy","project":"Shazbat creation engine","taskId":183,"description":"Create the Shazbat 100% efficiency engine","elapsedSeconds":296803,"elapsedTime":"82h43m","comment":"Task"}
// Convert to JSON string for localStorage
var activeTask = JSON.stringify(activeTaskObject);
console.info(activeTask);
//Write to localStorage
window.localStorage.setItem("activeTask",activeTask);
//Set it as the active datasource for updating to webservice
var activeTaskDS = new kendo.data.DataSource({
transport: {
read: function(options){
taskItem = JSON.parse(localStorage["activeTask"]);
},
update: {
url: remUpd, //url var declared earlier in the process
dataType: "json"
}
},
schema: {
model: {
client: "client",
taskId: "taskId"
},
data: function(){
return taskItem;
}
}
});
activeTaskDS.fetch(function(){
activeTaskDS.data()[0].set("client", "NOBODY");
activeTaskDS.sync();
cosole.log("activeTaskDS.data()[0] : "+activeTaskDS.data()[0]); //should read 'NOBODY' but reads 'Morken Mindy'
});
}
Thanks in advance,
Neil.
I'm not sure what is the problem actually, but I have to point some important things:
AFAIK, when you customize any transport methods you have to pass the data into a callback in the options object:
transport: {
read: function(options){
taskItem = JSON.parse(localStorage["activeTask"]);
// Tells the widget to handle that collection
options.success(taskItem);
}
}
In schema.data it seems that you want to pass your data through this method(correct me if I'm wrong). But this method isn't for that purpose. It is used just to tell the widget which field to read(in case of passing a string to it) or to read a property from a response, which comes as a parameter that you are not using. Check the second example here. So this may not be right way to read the taskItem object as data;
Speaking about the taskItem object, it seems that its the base data of your dataSource but it isn't defined(at least on the snippet you posted). What I mean is, if you follow the step 1 you won't even need to read from that object no more.
Please let me know if this is helpful and if you need anyting more.

Load data into local storage once?

I am using backbone.js I need a very simple way to render a local json file into the users local storage only one time. I am building a cordova app and I just want to work with local storage data.
I have hard coded a decent size .json file (list of players) into my collection, and I just want to load the .json file into the local storage if local storage on that device is empty which will only be once, upon initialization of the app.
I could use ajax, but I don't know how to write it to only inject data one time as "starter" data. So if you know how to do this I can upload the json file to my server and somehow fetch it.
I can inject the data if I go through a series of tasks, I have to disable the fetch method and render this code below in an each statement, plus the json has to be hardcoded into the collection, with a certain format.
playersCollection.create({
name: player.get('name'),
team: player.get('team'),
team_id: player.get('team_id'),
number: player.get('number'),
points: player.get('points')
})
I am trying to finish this lol I need to use it tonight to keep stats, I am almost there the structure works, when data is loaded I can add stats etc, but I need to get that data loaded, I pray someone can help!
Edit: I was able to put together some sloppy code last minuet that at least worked, thanks to #VLS I will have a much better solution, but Ill post the bad code I used.
// I fire renderData method on click
events: {
'click .renderData':'renderData'
},
// Inside my render method I check if "players-backbone" is in local storage
render: function() {
var self = this;
if (localStorage.getItem("players-backbone") === null) {
alert('yup null');
//playersCollection.fetch();
this.$el.append('<button class="renderData">Dont click</button>')
} else {
alert('isnt null');
this.$el.find('.renderData').remove();
playersCollection.fetch();
}
this.teams.each(function(team) {
var teamView = new TeamView({ model: team });
var teamHtml = teamView.render().el;
console.log($(''))
var teamPlayers = this.players.where({team_id: team.get('id')})
_.each(teamPlayers, function(player) {
var playerView = new PlayerView({ model: player });
var playerHtml = playerView.render().el;
$(teamHtml).append(playerHtml);
}, this);
this.$el.append(teamHtml);
}, this);
return this;
},
// method that populates local storage and fires when you click a button with the class .renderData
renderData: function() {
var self = this;
this.teams.each(function(team) {
var teamPlayers = this.players.where({team_id: team.get('id')})
_.each(teamPlayers, function(player) {
playersCollection.create({
name: player.get('name'),
team: player.get('team'),
team_id: player.get('team_id'),
number: player.get('number'),
points: player.get('points')
})
}, this);
}, this);
playersCollection.fetch();
return this;
}
This is obviously not the best way to go about it, but it worked and I was in such a hurry. The caveats are you have to click a button that populates the data, the collection is hard coded in, it's just overall not very elegant (but it works) the app did what it needed.
So big thanks to #VLS, I appreciate the effort to explain your code, and create a fiddle. Sorry I was so late!
You can extend your collection's fetch method and use it in conjunction with Backbone.localStorage, so inside your collection you'd have something like:
localStorage: new Backbone.LocalStorage("TestCollection"),
fetch: function(options) {
// check if localStorage for this collection exists
if(!localStorage.getItem("TestCollection")) {
var self = this;
// fetch from server once
$.ajax({
url: 'collection.json'
}).done(function(response) {
$.each(response.items, function(i, item) {
self.create(item); // saves model to local storage
});
});
} else {
// call original fetch method
return Backbone.Collection.prototype.fetch.call(this, options);
}
}
Demo: http://jsfiddle.net/5nz8p/
More on Backbone.localStorage: https://github.com/jeromegn/Backbone.localStorage

IndexedDB DOM IDBDatabase Exception 11 even after using oncomplete

I am very new to IndexedDB Concepts. I am trying to Store a list of movies in the IndexedDB and retrieve it. But for some reason when i try to retrieve it there is a DOM IDBDatabase Exception 11 in chrome browser. I try to retrieve it by using a simple alert. I also tried to retrieve the data by putting the alert inside an onComplete event, but this too seems to be a failure. Could someone please let me know what wrong i am doing. Below is my code.
const dbName = "movies";
var request = indexedDB.open(dbName, 1);
request.onerror = function(event) {
alert("Seems like there is a kryptonite nearby.... Please Check back later");
};
request.onsuccess = function(event) {
var db = event.target.result;
var transaction = db.transaction(["movies"],"readwrite");
var objectStore = transaction.objectStore("movies");
var request1 = objectStore.get("1");
request1.result.oncomplete=function(){
alert("The movie is"+request1.result.name);//This is the place where i get the error
}
};
request.onupgradeneeded = function(event) {
db = event.target.result;
var objectStore = db.createObjectStore("movies", { keyPath: "movieid" });
objectStore.createIndex("name", "name", { unique: false });
objectStore.createIndex("runtime", "runtime", { unique: false });
for (var i in movieDataToStore) {
objectStore.add(movieDataToStore[i]);
}};
I still do not know what was wrong with the last program. i re-wrote the above program and it worked like a charm. here is the code. Hope this helps anyone who is stuck with this problem. Also if anyone figures out what went wrong the last time please share your thoughts.
var db; //database will be stored in this value when success is called
var movieDataToStore = [{ movieid: "1", name: "Keep my Storage Local", runtime:"60"},
{ movieid: "2", name: "Rich Internet Conversations", runtime:"45"},
{ movieid: "3", name: "Applications of the Rich and Famous", runtime:"30"},
{ movieid: "4", name: "All Jump All eXtreme", runtime:"45"}];
window.query = function() {
db.transaction("movies").objectStore("movies").get("1").onsuccess = function(event) {
alert("QUERY: CThe first movie is" + event.target.result.name);
};};
window.onload = function() {
if (!window.indexedDB) {
window.alert("Your browser doesn't support a stable version of IndexedDB. Such and such feature will not be available.")
}
else{
var request = indexedDB.open("movies", 1);
request.onerror = function(event) {
alert("Seems like there is a kryptonite nearby.... Please Check back later");
};
request.onsuccess = function(event) {
db = this.result;
query();
};
request.onupgradeneeded = function(event) {
var db = event.target.result;
if(db.objectStoreNames.contains("movies")) {
db.deleteObjectStore("movies");
}
var objectStore = db.createObjectStore("movies", { keyPath: "movieid"});
objectStore.createIndex("name", "name", { unique: false });
objectStore.createIndex("runtime", "runtime", { unique: false });
for (var i in movieDataToStore) {
objectStore.add(movieDataToStore[i]);
}
};
}
};
I think it is bad practice to insert data in the onupgradeneeded context. You should be doing this separately in an unrelated function at some other time. In fact, attempting to insert the data on a database whose version was incremented since last page load will automatically trigger the upgradeneeded event for you.
While many of the online examples shove the database connection handle (your db var) into some global scope variable, this is also a bad practice that will lead to errors down the road. Only access the db var within your callbacks as a parameter. In other words, your openRequest.onsuccess function should pass the db variable to the query function. This also reduces the chances of any garbage collection issues later and leaving database connections open (which the designers of indexedDB allow for, but should generally be avoided).
If your movie ids are integers, it isn't clear to me why you are storing and retrieving them as strings. You can store integer values. You can pass an integer to store.get.
You are using for...in inappropriately. It will work, but for...in is intended for looping over the keys of object literals (like var x = {key:value}). Use a normal for loop or use array.foreach when iterating over your movies array.
As you found out in your fixed code, it is better to use request.onsuccess and request.onerror. There is also a transaction.oncomplete. But I am not sure there is a request.oncomplete. What happens is you are setting the oncomplete property of an IDBRequestObject but this does nothing since the code never triggers it.
DOM 11 usually signals you tried to access a table that does not exist or is in an incorrect state. Usually this happens due to mistakes elsewhere, like onupgradeneeded never getting called when connecting. It is confusing, but given the way your code is setup, basically the db gets created the first time your page loads, but then never gets created again, so while developing, if you made changes, but do not increment your db version, you will never see them.

Categories