Backbone Collections - not able to set a 'rank' using set method - javascript

In my backbone.marionette app, I am trying to set a model attribute 'rank' as it's loop index. here is the code of my collection:
AngryCats = Backbone.Collection.extend({
model:AngryCat,
initialize: function(cats){
var rank = 1;
_.each(cats, function(cat){
cat.set('rank', rank);
rank++;
})
}
});
But I am getting error as :
TypeError: cat.set is not a function
cat.set('rank', rank);
any one tell me what is wrong here? ( please check the fiddle link for complete coding )
Live Demo

You are passing array of javascript objects. But the set is only available in Backbone.Model instance. Only after initialisation, every single object converted into Backbone.Model Object.
You have to do like
var rank = 1;
_.each(cats, function(cat){
cat.rank = rank;
rank++;
})

Related

Nested array into backbone model

I have the following backbone model:
var Info = Backbone.Model.extend({
urlRoot: 'http://localhost:8080/info',
defaults : {
nombre: '',
tipo : '',
telf : 0,
icono : '',
direccion:[{
direccion:'',
latitud:'',
longitud:''
}]
},
idAttribute:"_id"
});
I want to change the value of the "direccion" atribute of the array inside "direccion".
I have used the following code but is not working:
//clone the array
var direccionArray = _.clone(this.collection.get(idInfo).get('direccion'));
direccionArray.direccion = this.$('#dir-info-edit').val();
Here I obtain the array with the values changed and works fine:
console.log(direccionArray);
Now I set the array into my backbone model as follow and is not working (the model don't change) and I get the same model (changing other atributes like "nombre" or "tipo" works fine but not with the array):
this.collection.get(idInfo).set('direccion',direccionArray);
console.log(this.collection.get(idInfo));
Could someone help me please?
As stated direccion attribute of model is an array that contains object at index 0.
When you trying to do:
//clone the array
var direccionArray = _.clone(this.collection.get(idInfo).get('direccion'));
direccionArray.direccion = this.$('#dir-info-edit').val();
This will not update the direccionArray[0], which you want to update, and just will add new attribute to your array object.
What you want to do is:
direccionArray[0].direccion = this.$('#dir-info-edit').val();
Try console.dir(direccionArray) the old array and you will see the difference.
Update:
Please see this jsfiddle with the explanation of the issue. The main reason for your case can be that you are tring to get the value with jquery's val() method on an element that is not an input.

Efficient Marionette sorted CollectionView for multiple attributes

I am trying to find a way to efficiently display a sorted Marionette.CollectionView, sorted by multiple attributes without modifying the underlying Backbone collection. For this example, I am using 'name' and 'online' attributes, and want my CollectionView to be displayed in 2 parts:
online, alphabetically
offline, alphabetically
I have a single Collection of Backbone.Models and want to use this across my web application. So having a sort function on my Collection doesn't feel right to me.
My example code is as follows:
var MyCollection = Backbone.Collection.extend({
model:Backbone.Model
});
var myCollection = new MyCollection();
myCollection.set([
{ name : 'Freddy', online : true },
{ name : 'Zorro', online : false },
{ name : 'Charlie', online : false },
{ name : 'Alice', online : true }
]);
var MyView = ...
/*
omitted for brevity, though my template is like
<li>{{name}} {{#if online}}(Online){{/if}}</li>
*/
var MyCollectionView = Marionette.Collection.extend({
childView:MyView,
viewComparator: function (item1, item2) {
var item1Online = item1.get('online');
var item2Online = item2.get('online');
if (item1Online != item2Online)
return item1Online ? -1 : 1;
var item1Name = item1.get('name');
var item2Name = item2.get('name');
return item1Name.localeCompare(item2Name);
}
});
var myCollectionView = new MyCollectionView({collection:myCollection});
appView.getRegion('example').show(myCollectionView);
I would like this to be displayed as:
Alice (Online)
Freddy (Online)
Charlie
Zorro
This is fine when all of the data is added to the collection at once, or add/remove events but if one of the attributes is updated on a model that is already in the collection, the view does not update.
If Charlie's 'online' property changed to true - e.g by performing.
charlieModel.set('online', true)
I would like the CollectionView to have rendered automatically as:
Alice (Online)
Charlie (Online)
Freddy (Online)
Zorro
Any suggestions? Many thanks in advance.
From the backbone documentation
Collections with a comparator will not automatically re-sort if you later change model attributes, so you may wish to call sort after changing model attributes that would affect the order.
You can put somewhere convenient in your code a listener on a change in the model attributes your are targeting that will trigger a re-sort of your collection.
// for example in your collection
initialize: function() {
this.on('change:name', function() { this.sort() }, this);
}
The Marionette team advised having a separate Backbone collection behind the scenes, rather than using the viewComparator on the CollectionView.
Using reorderOnSort on the CollectionView made a huge difference (at least 10x speed up) in terms of render speed.
This option is useful when you have performance issues when you resort your CollectionView.
Without this option, your CollectionView will be completely re-rendered, which can be
costly if you have a large number of elements or if your ChildViews are complex. If this option
is activated, when you sort your Collection, there will be no re-rendering, only the DOM nodes
will be reordered.
My final example code:
var MyView = Backbone.Marionette.ItemView.extend({
modelEvents:{
'change:name change:online': 'render'
},
template:template
});
var MyCollection = Backbone.Collection.extend({
initialize : function(){
this.on('change:name change:online', this.sort, this);
},
comparator : function(item1, item2){
var item1online = item1.get('online');
var item2online = item2.get('online');
if (item1online != item2online)
return item1online ? -1 : 1;
return item1.get('name').localeCompare(item2.get('name'));
}
});
var myCollection = new MyCollection();
var MyCollectionView = Marionette.CollectionView.extend({
childView : MyView,
reorderOnSort : true
});
var myCollectionView = new MyCollectionView({
collection : myCollection
});
appView.region('example').show(myCollectionView);

Javascript matrix array issue

I'm creating a very simplified version of a drag and drop shopping cart with jqueryui.
My issue is regarding adding data(id, name, price) to an array.
I tried several methodes of adding the data (also an array) to the main container(array). But I keep getting this error: Uncaught TypeError: undefined is not a function
var data = [];
function addproduct(id,name,price){
//var d = [id,name,price];
data[id]["name"] = name;
data[id]["price"] = price;
data[id]["count"] = data[id]["count"]+1;
console.log(data);
}
the addproduct() function can be called by pressing a button
It is not entirely clear to me what type of data structure you want to end up with after you've added a number of items to the cart. So, this answer is a guess based on what it looks like you're trying to do in your question, but if you show a Javascript literal for what you want the actual structure to look like after there are several items in the cart, we can be sure we make the best recommendation.
You have to initialize a javascript object or array before you can use it. The usual way to do that is to check if it exists and if it does not, then initialize it before assigning to it. And, since you're keeping a count, you also will want to initialize the count.
var data = [];
function addproduct(id,name,price){
if (!data[id]) {
// initialize object and count
data[id] = {count: 0};
}
data[id]["name"] = name;
data[id]["price"] = price;
++data[id]["count"];
console.log(data);
}
And FYI, arrays are used for numeric indexes. If you're using property names like "name" and "price" to access properties, you should use an object instead of an array.
And, I would suggest that you use the dot syntax for known property strings:
var data = [];
function addproduct(id,name,price){
if (!data[id]) {
// initialize object and count
data[id] = {count: 0};
}
data[id].name = name;
data[id].price = price;
++data[id].count;
console.log(data);
}
It looks like what you want is an array of objects, although I would need a more detailed description of your problem to be clear.
var data = []
function addproduct(id, name, price)
{
data.push({'id': id, 'name':name, 'price': price, 'count': ++count});
console.log(data);
}

Loop through all Mongo collections and execute query

First of, I'm quite new to mongodb. Here's my question I've not been able to find a solution to.
Let's say I have 3 different collections.
mongos> show collections
collectionA
collectionB
collectionC
I want to create a script that iterates over all collections ind this database and find the last inserted timestamp in each of these collections. Here's what works inside mongos.
var last_element = db.collectionA.find().sort({_id:-1}).limit(1);
printjson(last_element.next()._id.getTimestamp());
ISODate("2014-08-28T06:45:47Z")
1. Problem (Iterate over all collections)
Is there any possibility to to sth. like.
var my_collections = show collections;
my_collections.forEach(function(current_collection){
print(current_collection);
});
Problem here, the assignment for my_collections does not work.
I get SyntaxError: Unexpected identifier. Do I need to quote the 'show' statement ? Is it even possible ?
2. Problem (storing collection in js var)
I can workaround Problem 1 by doing this:
var my_collections = ["collectionA", "collectionB", "collectionC"];
my_collections.forEach(function(current_collection){
var last_element = db.current_collection.find().sort({_id:-1}).limit(1);
print(current_collection);
printjson(last_element.next()._id.getTimestamp());
});
The last_element.next() produces the following error:
error hasNext: false at src/mongo/shell/query.js:124
It seems that last_element isn't saved correctly.
Any suggestions on what I'm doing wrong??
UPDATE
Neils answer lead me to this solution. In addition to his code I had to check if the function getTimestamp really exist. For some 'virtual' collections there seem to be no _id property.
db.getCollectionNames().forEach(function(collname) {
var last_element = db[collname].find().sort({_id:-1}).limit(1);
if(last_element.hasNext()){
var next = last_element.next();
if(next._id !== undefined && typeof next._id.getTimestamp == 'function'){
printjson(collname + " >> "+next._id.getTimestamp());
}else{
print(collname + " undefined!! (getTimestamp N/A)")
}
}
});
There is the db.getCollectionNames() helper method that does this for you. You can then implement your code:
db.getCollectionNames().forEach(function(collname) {
// find the last item in a collection
var last_element = db[collname].find().sort({_id:-1}).limit(1);
// check that it's not empty
if (last_element.hasNext()) {
// print its timestamp
printjson(last_element.next()._id.getTimestamp());
}
})
You probably also want a .hasNext() check in there to cater for possible empty collections.
Rename the collection name present in all the records using the following script:
db = db.getSiblingDB("admin");
dbs = db.runCommand({ "listDatabases": 1 }).databases;
dbs.forEach(function(database) {
db = db.getSiblingDB(database.name);
db.currentname.renameCollection("newname");
});

How to update a property in an Javascript Object with ko.mapping - Knockout?

Assume we have the following Object :
var gridViewModelJs =
{"GridViewModel":{"Rows":[{"RowNumber":"1","Id":"6","Name":"FullNameOfUser","NumberOfUsers":"12","RegistrationDate":"10/15/2013"}],"FoundItems":"4","CurrentPage":1,"TotalPages":1,"ItemsPerPage":50,"PagingLinks":""},
"EntityModel":{"Id":0,"PermissionIds":null,"Name":null,"NumberOfUsers":0,"PersianRegistrationDate":null,"RegistrationDate":"0001-01-01T00:00:00","Authorizations":null,"Users":null,"Contents":null}};
var KoEntityViewModel = ko.mapping.fromJS(gridViewModelJs);
ko.applyBindings(KoEntityViewModel);
Above code works, for updating the KoEntityViewModel. I used the following code :
// receivedData is data that returns from jQuery Ajax
// I'm dead sure `receivedData` is correct
var doneFunc = function (receivedData) {
ko.mapping.fromJS(receivedData, KoEntityViewModel.EntityModel);
showDetailsBlock();
};
But nothing update in KoEntityViewModel.EntityModel.
Please guide me how I can update KoEntityViewModel.EntityModel in above sample
When you update the mapping after applying bindings, use three parameters:
ko.mapping.fromJS(receivedData, {}, KoEntityViewModel);

Categories