.fetch() on collection gives TypeError - javascript

<!DOCTYPE HTML>
<head>
<meta charset="utf-8">
<script src="http://code.jquery.com/jquery-2.1.1.min.js"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.5.2/underscore-min.js"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/backbone.js/1.0.0/backbone-min.js"></script>
</head>
<body>
<script>
var mod=Backbone.Model.extend({});
var col=Backbone.Collection.extend({model:mod, url: 'test.php'});
col.fetch();
</script>
</body>
</html>
If run this primitive code in Firefox, Firebug gives me the following error:
TypeError: col.fetch is not a function
col.fetch();
Why is that? I can't see no error or typo... Thanks.

You have few mistakes in your code:
You need to specify model and collection:
var Mod = Backbone.Model.extend({});
var Col = Backbone.Collection.extend({model: Mod, url: 'test.php'});
Create instance of collection with specified model:
var collectionInstance = new Col({model: new Mod()});
Fetch collection:
collectionInstance.fetch();
For more details, please, see Backbone docs.

If you are not familiar with a Class/Constructor/Prototype vs Instance here is an explanation:
Say we have a forum where every user has some posts. We want to represent the Posts a user has in a Collection called "Posts". Now here's the thing, you can can have multiple users, therefore you will need multiple instances of the collection (which is basically an array with some super-cool features that Backbone provides and you specify). We want to create a "blueprint" for how this array works and the cool things it can do (because we don't want to rewrite the functionality for every single array we create). A Class/Constructor/Prototype is that blueprint.
//Post is a Model Class/Constructor/Prototype. We can make multiple "instances" of Post.
var Post = Backbone.Model.extend({
starPost: function() {
//contains code to "star" a post
},
pinPost: function(){
//contains code to "pin" the post
},
//some other cool functions to do cool things to a post
});
//Posts is the Collection Class/Constructor/Prototype. We can make multiple "instances" of Posts for each User.
var Posts = Backbone.Collection.extend({
url: function() {
return '/api/posts/' + this.options.userID
},
model: Post,
findStarredPosts: function(){
//has code to return all Post models that are "starred"
},
findPinnedPosts: function(){
//has code to return all Post models that are "pinned"
}
});
//please note that the FIRST ARGUMENT is an empty array (meaning we are starting this collection with no models inside). the SECOND ARGUMENT is the options (which will be saved into the instances `this.options`). `options` is what makes this instance unique when going to the server. You need to write in your `url` function HOW you're going to use what you passed in `options` to communicate with the server
var user1Posts = new Posts([], {
userID: 123 //this is an option passed in which is specific to this INSTANCE. It will get read by the `url` function and generate an instance of this posts collection Specific to user1
});
var user2Posts = new Posts([], {
userID: 456 //this is an option passed in which is specific to this INSTANCE. It will get read by the `url` function and generate an instance of this posts collection Specific to user2
});
//this command actually tells Backbone to go our and request user1's posts from the server and put them into the `user1Posts` collection.
user1Posts.fetch();
Point being, you NEVER run functions on Constructors/Prototypes/Classes. You only act on their instances. The only thing you can do is create instances of those Constructors/Prototypes/Classes.
I hope this makes some sense. If not I can (and will) clarify.

Related

How do I get rid of model attached to view after using Backbone's model#destroy?

I'm seeing the success callback but in the debugger Chrome Dev Tools, I'm still seeing the model when I type in this.model. I know that it's destroyed on the server side, but can you explain why it's still attached to the view? How do I get rid of it?
delete: function () {
this.model.destroy({success: function () {console.log("success in destroy");}});
debugger;
}
What you are seeing is correct. Looking at the documentation on model.destroy (or looking to the code) we can see it basically does two things:
HTTP DELETEs the server representation of the model
Removes the model from any containing collections
Note that nothing happens to the model itself or to any objects the model may be attached to.
We can see this behavior with a simple example:
var foo = new Backbone.Model({foo: 'bar'});
var foos = new Backbone.Collection([foo]);
var fooView = new Backbone.View();
fooView.model = foo;
foo.destroy({success: function () {
console.log('success');
}});
console.log(foo, foos, fooView);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/backbone.js/1.3.3/backbone.js"></script>
Note that nothing happens to foo or fooView.model after executing this code, although foos no longer contains an instance of foo.
Removing view.model
If you want to remove the model from the view you can leverage the success callback. Just change your view's delete method (from your question) to something like the following:
delete: function () {
this.model.destroy({success: function () {
delete this.model;
}.bind(this)});
}
Alternatively, since we know from the docs that the model will fire a "destroy" event, we can also listen for that event and fire a callback that deletes our model.

Dexie.js not carrying out .add transaction

var db = new Dexie(app.settings.unpublishedBooksDb);
db.version(1).stores({
friends: "++id,name,shoeSize"
});
db.open();
db.close();
I have a precreated indexedDB database using the code above, and then on another view in the application, I need to add a row to a table.
var db = new Dexie('myDb');
db.open().then(function() {
console.log ('opened'); //this works
db.friends.add({name:"Fredrik"}); //this doesnt do anything and adding a catch doesn't throw an error either
}).finally(function () {
db.close();
});
I tried using .transaction but still the same. If I try using Chrome's console, I get an error : Cannot read property add of undefined
your second db instance contains no info about what tables it would contain. So the implicit table property (db.friends) is not there. What actually happens is that it throws TypeError: cannot read property 'add' of undefined. If you would catch the call (not just do a finally), you would get that TypeError catched.
What you can do is to reference the friends table by db.table('friends').add ({name: 'Fredrik'}) instead of db.friends.add({name: 'Fredrik'}).
Beware though that defining the database without specifying table schema is not as thorowgly tested and used, so I would recommend using it with a schema defined to avoid other pitfalls as well. If you for architectural reasons still need to do it your way, be aware that transaction scopes works a little different since you cannot use the dynamic implicit tale properties in the transaction scopes either and db.table() currently does not return a transaction-bound Table instance if you are in a transaction scope. You would have to use the old transaction API:
db.transaction('rw', 'friends', function (friends, trans) {
friends.put({name: 'Fredrik'});
});
...instead of:
db.transaction('rw', 'friends', function () {
db.friends.put({name: 'Fredrik'});
});
Best wishes,
David

FindMany overwritten doesn't work. Ember-data

In my project, if I ask my server for the records with id 1,2,3 like this (without spaces):
url?sites=id1 %2C id2 %2C id3
It will return a json file with the records for this ids.
So for this case I think I can have then cached if I manage to use findMany and make the RestAdapter make the call to the server in this way.
I have found the next, but it doesnt work, it continues calling:
GET topologymins/1,2
Adapter:
App.ApplicationAdapter = DS.RESTAdapter.extend({
findMany: function(store, type, ids) {
Ember.Logger.log("INSIDE THE findMany"); // NOT SHOWED
var url = type.url;
url = url.fmt(ids.join(','));
jQuery.getJSON(url, function(data) {
// data is an Array of Hashes in the same order as the original
// Array of IDs. If your server returns a root, simply do something
// like:
// store.loadMany(type, ids, data.people)
//store.loadMany(type, ids, data);
});
}
});
App.Topologymin.reopenClass({
url: '/something?ids=%#'
});
My call:
this.store.find('topologymin',[1, 2]);
Isn't this just because you're missing a return?
Oh... sorry your findMany isn't written correctly, I don't htink... it's missing a return... but that isn't actually what's wrong - it's not even calling the findMany because store.find() can't be passed an array of ids... you want this, I think: store.find('topologymin', { ids: [1,2] });
However, I think you'll have another problem in that findMany should have a return value in it... if you look at the default implementation, you'll see what I mean... it needs to return a promise, and you're not returning anything.

Using Meteor and javascript in an object oriented style

Please bear with me as I'm new to JS and am having trouble implementing some things with Meteor. I implemented a class in JavaScript using
function Class() {
this.property = 0
this.method = function () {
return "method called"
}
}
I made a new Meteor Collection bu using new Meteor.collection and successfully retrieved the data on the client and can display Class.property in the html template. However, I am unable to access Class.method and was wondering if there's any way to make this happen and if using Meteor.methods to define functions that take the Class instance as input is the best way to go.
For anyone still looking at this, the reason the code doesn't work is because mongodb stores documents as bson. bson, just like json, does not support functions (http://bsonspec.org) so when the above class is saved by meteor into mongo, the method is not saved as part of the document.
There is no easy elegant solution I'm aware of. I have the same issue. In order to utilise the class method you would need to instantiate the class each time you needed it, which you could implement as part of a database model.
This is not really an answer but in meteor's package manager you can add libraries like backbone.js which gives you models, collection and views and a nice router which I find very handy when making meteor apps. Backbone works well with jQuery.
My other suggestion is using a library like Mootools which unlike jQuery doesn't try to change the way you write javascript but enhancing the experience of making object oriented javascript. (see: jqueryvsmootools). With mootools you can can make a class the following way...
var MyClass = new Class({
'Implements': [Options],
//default options
'options': {
'foo': null
},
'initialize': function(options) {
this.foo = options.foo;
},
'bar' : function() {
return this.foo;
}
});
var blub = new MyClass({'foo': 'Hello World'});
blub.bar(); // "Hello World"
I was looking to do the same thing.
I found a function called "transform" that is called when getting something from a meteor collection. You can use it to add a function to a meteor object just as you require.
Here is an example of adding an "endDate" function and "remaining" functions to a meteor object
Products = new Meteor.Collection("Products", {
transform: function (doc) {
doc.endDate = function () {
// SugarJS gives us minutesAfter() which gives us a nice syntax for
// creating new Date objects
// http://sugarjs.com/api/Number/unitAfter
return ((25).minutesAfter(this.startDate));
};
doc.remaining = function () {
return this.endDate().getTime() - Date.now();
};
return doc;
}
});
Read more here:
http://www.okgrow.com/posts/2014/05/19/meteor-transform/
This approach worked beautifully for me:
http://www.okgrow.com/posts/2014/05/19/meteor-transform/
I don't know anything about Meteor, but I see a problem with your code. You're missing a semi-colon after:
this.property = 0
Without that semi-colon, the javascript interpreter will not execute the this.method assignment.

How can you reference to javascript window element before initianization?

This example is borrowed from Backbone directory demo app
https://github.com/ccoenraets/backbone-directory/blob/master/web/js/utils.js#L11
// The Template Loader. Used to asynchronously load templates located in separate .html files
window.templateLoader = {
load: function(views, callback) {
var deferreds = [];
$.each(views, function(index, view) {
if (window[view]) {
deferreds.push($.get('tpl/' + view + '.html', function(data) {
window[view].prototype.template = _.template(data);
}, 'html'));
} else {
alert(view + " not found");
}
});
$.when.apply(null, deferreds).done(callback);
}
};
You initialize this with array of strings [views] and [callback] function.
My question is how window[view] (click above link to exact position in the code) can be checked if (as far as I see) wasn't be initialized previously? If I'm not precise please write this in comments.
If I've understood your question correctly, then when you call templateLoader.load you pass in 2 arguments; views and callback. We can assume that views is an array, since we then iterate over that array with the jQuery .each() method. The callback to .each() is passed the element of the views array that corresponds to the current iteration. That argument is named view.
So view is some arbitrary value that was stored in the views array. We then try to find a property of window with the identifier that matches the value of view. If view === "james" we are looking for window.james.
If you look at some of the views in that app you will see that they are defined like this:
window.ContactView = Backbone.View.extend({
// Some methods
});
So ContactView is a property of window, and we could call templateLoader.load like the following to load that template:
templateLoader.load(["ContactView"], someCallbackFn);
And you can see where that actually gets called in main.js.
So what's actually happening is a bunch of properties of window are defined in various other files, and then loaded by the template loader, by passing an array of identifiers to it.

Categories