I have a backbone collection:
var user = new Backbone.Collection.extend({
url: '/user',
parse: function (response) {
return response.lunch;
return response.dinner;
}
});
which returns a json like this:
[
{
lunch: [{
appetizer : 'bagel',
maincourse: 'steak',
desert: 'sweets'
}]
},
{
dinner: [{
appetizer : 'chips',
main: 'rice',
desert: 'sweets'
}]
}
]
I want to combine both response.lunch and response.dinner and have a common collection: I tried:
parse: function (response) {
var collection1 = response.lunch;
var collection2 = response.dinner;
return collection1.add(collection2.toJSON(), {silent : true});
}
But it doesnot work. Also how do i do a each function to override all main with maincourse? I tried:
this.collection.each(function(model) {
var a = model.get("main");
model.set({a: "maincourse"});
}
Any help would be appreciated. Thanks!
I'm guessing that you want to merge lunch and dinner so that your collection ends up with { appetizer : 'bagel', ... } and { appetizer : 'chips', ... } inside it. If so, then simply concat the two arrays together:
parse: function(response) {
return response.lunch.concat(response.dinner);
}
If you want to rename all the main attributes to maincourse then you'd want to use get to pull out the mains, unset to remove them, and then set to put them back in with the new name:
var maincourse = model.get('main');
model.unset('main', { silent: true });
model.set('maincourse', maincourse, { silent: true });
or just edit attributes directly:
model.attributes.maincourse = model.attributes.main;
delete model.attributes.main;
or better, just rename the attribute in your parse method.
Related
In knockout JS I want to find out 1st duplicate object from my collection and return that object as modal. I have to check for 1st duplicate object from first array aginst 2nd Array based on my condition. Tried _findWhere & _.Some & _.each nothing worked. Can someone help
Here -- MyMainModal is my Moda which will have multiple objects
self.dupRecord= function (MyMainModal) {
var Modaldata= ko.mapping.toJS(MyMainModal);
return _.some(Modaldata, function (MD1) {
return _.some(Modaldata, function (MD2) {
if ((MD1.ID!== MD2.Id) &&
(MD1.Name === MD2.name));
});
});
};
How about incorporating the check for first duplicate into the mapping? Something like:
function Child(data) {
ko.mapping.fromJS(data, {}, this);
};
var model = {
children: [{
id: '1',
name: 'Billy'
}, {
id: '2',
name: 'Susy'
}]
};
var mapping = {
children: {
key: function(data) {
return ko.utils.unwrapObservable(data.id);
},
create: function(options) {
console.log('creating ' + options.data.name, options.parent);
var newChild = new Child(options.data);
if(options.parent.firstDuplicate() === undefined)
options.parent.children().forEach(function(child) {
if(child.name() === newChild.name())
options.parent.firstDuplicate([child, newChild]);
});
return newChild;
},
update: function(options) {
console.log(' updating ' + options.data.name);
return options.target;
}
}
};
var vm = {
children: ko.observableArray(),
firstDuplicate: ko.observable()
};
ko.mapping.fromJS(model, mapping, vm);
ko.applyBindings(vm);
model.children.push({
id: 3,
name: 'Billy'
});
setTimeout(function() {
console.log('--remapping--');
ko.mapping.fromJS(model, mapping, vm);
}, 2000);
I read that as, "if we're not updating the record, potentially set the first duplicate." Here's a fiddle: http://jsfiddle.net/ge1abt6a/
I want to pass a param by the router
I have been trying :
Router.route('/someURL/:id', {
name: 'someTemplate',
data: function() {
var myData = someCollection.findOne({_id:this.params.id});
myData.someParam = true;
return myData;
}
});
Router.route('/anotherURL', {
name: 'someTemplate',
data: function() {
return {someParam:false};
}
});
but it doesn't work
This is my error:
Error: Handler with name 'someTemplate' already exists.
How can i solve it.?
Note: I need this "someParam"
The name is a name for the route not the template you want to use. Route names are a unique identifier per route much like the url and can be used to call the route without using the full url. You want something like:
Router.route('/someURL/:id', {
name: 'someName',
template: 'someTemplate',
data: function() {
var myData = someCollection.findOne({_id:this.params.id});
myData.someParam = true;
return myData;
}
});
Router.route('/anotherURL', {
name: 'someOtherName',
template: 'someTemplate',
data: function() {
return {someParam:false};
}
});
I want to return a single document with the fields joined together. That is, a result like as follows
{
_id: "someid",
name: "Odin",
profile: {
game: {
_id: "gameid",
name: "World of Warcraft"
}
}
}
I have a route controller which is fairly simple.
UserController = RouteController.extend({
waitOn: function () {
return Meteor.subscribe('users');
},
showAllUsers: function () {
this.render('userList', {
data: Meteor.users.find()
})
}
});
I've tried changing my data like so:
this.render('userList', {
data: Meteor.users.find().map(function (doc) {
doc.profile.game = Games.findOne();
return doc;
})
});
However, this does not have the intended effect of adding "game" to the user. (and yes, Games.findOne() has a result)
How can you transform the results of a cursor in meteor and iron:router?
Try defining your data as a function so it can be dynamically re-executed when needed.
UserController = RouteController.extend({
waitOn: function () {
return Meteor.subscribe('users');
},
showAllUsers: function () {
this.render('userList', {
data: function(){
return Meteor.users.find().map(function (doc) {
doc.profile.game = Games.findOne();
return doc;
});
}
});
}
});
Given your use of easy search, what might be simpler is just to define a template helper for profile
Template.userList.helpers({
profile: function(){
var game = Games.findOne({_id: this.gameId});
return { game: { _id: game._id, name: game.name }};
}
});
This assumes a single game per user. If you have more than one then you can iterate over a cursor of Games instead.
I'm having a problem where my backbone model isn't parsing something correctly. Here is the listing.js:
SpendYourSavings.Models.Listing = Backbone.Model.extend({
urlRoot: "api/listings/",
images: function() {
this._images = this._images || new SpendYourSavings.Collections.Images([], { listing: this });
return this._images;
},
reviews: function() {
this._reviews = this._reviews || new SpendYourSavings.Collections.Reviews([], { listing: this });
return this._reviews;
},
shop: function() {
this._shop = this._shop || new SpendYourSavings.Models.Shop([], { listing: this });
return this._shop;
},
parse: function(data) {
if(data.images) {
this.images().set(data.images, { parse: true });
delete data.images;
}
if(data.reviews) {
this.reviews().set(data.reviews, { parse: true });
delete data.reviews;
}
if(data.shop) {
this.shop().set(data.shop, { parse: true });
delete data.shop;
}
return data;
}
});
Images and reviews work, but shop doesn't quite work. It sets the attributes of shop correctly, but it doesn't set the image properly.
Here is the shop.js:
SpendYourSavings.Models.Shop = Backbone.Model.extend({
urlRoot: "/api/shops",
reviews: function() {
this._reviews = this._reviews || new SpendYourSavings.Collections.Reviews([], {});
return this._reviews;
},
listings: function() {
this._listings = this._listings || new SpendYourSavings.Collections.Listings([], {});
return this._listings;
},
user: function() {
this._user = this._user || new SpendYourSavings.Models.User([], {});
return this._user;
},
image: function() {
this._image = this._image || new SpendYourSavings.Models.Image([], {});
return this._image
},
parse: function(data) {
console.log("shop parse data: " + data);
debugger
if(data.listings) {
this.listings().set(data.listings, { parse: true });
delete data.listings;
}
if(data.reviews) {
this.reviews().set(data.reviews, { parse: true });
delete data.reviews;
}
if(data.user) {
this.user().set(data.user, { parse: true });
delete data.user;
}
if(data.image) {
debugger
this.image().set(data.image, { parse: true });
delete data.image;
}
return data
}
});
The parse function in the shop.js never even when I receive a shop in the listing.js parse function! shop.image() doesn't get set to an image model properly, so I have to call something wonky like shop.get('image').url to get the url.
Presumably, the reason you're memoizing the image model in the shop is to maintain listeners and keep a single instance of that model around.
Collection#set takes a parse option that tells it to call parse on all the models that were set on the collection. Model#set is the method called immediately after calling parse using the attributes returned from parse.
In this case, we want to call #set on the associated shop model using the parsed attributes. So first lets call parse. It should look something like this:
SpendYourSavings.Models.Listing = Backbone.Model.extend({
urlRoot: "api/listings",
images: function() {
this._images = this._images || new SpendYourSavings.Collections.Images([], { listing: this });
return this._images;
},
reviews: function() {
this._reviews = this._reviews || new SpendYourSavings.Collections.Reviews([], { listing: this });
return this._reviews;
},
shop: function() {
// Notice the first argument is an object when initializing models.
this._shop = this._shop || new SpendYourSavings.Models.Shop({}, { listing: this });
return this._shop;
},
parse: function(data) {
if(data.images) {
this.images().set(data.images, { parse: true });
delete data.images;
}
if(data.reviews) {
this.reviews().set(data.reviews, { parse: true });
delete data.reviews;
}
if(data.shop) {
var shopParams = this.shop().parse(data.shop);
this.shop().set(shopParams);
delete data.shop;
}
return data;
}
}
});
Your issue is that parse: true on set only really applies to collections.
These lines
this.images().set(data.images, { parse: true });
this.reviews().set(data.reviews, { parse: true });
work, because you are saying "add whole new models from this JSON".
This line
this.image().set(data.image, { parse: true });
however, is trying to say, parse these params, and set values, but that is weird on a model. Should it literally only parse the attributes that were passed in? Should it merge the attributes that the model already has? What if there were dependencies between the things already in the model and the things being parsed?
Instead, you might try restructuring your top-level parsing, e.g
SpendYourSavings.Models.Listing = Backbone.Model.extend({
urlRoot: "api/listings/",
images: function() {
return this.get('images');
},
reviews: function() {
return this.get('reviews');
},
shop: function() {
return this.get('shop');
},
parse: function(data) {
if (data.images){
data.images = new SpendYourSavings.Collections.Images(data.images, { listing: this, parse: true});
}
if (data.reviews){
data.reviews = new SpendYourSavings.Collections.Reviews(data.reviews, { listing: this, parse: true});
}
if (data.shop){
data.shop = new SpendYourSavings.Models.Shop(data.shop, { listing: this, parse: true});
}
return data;
}
});
I have the following JSON that represents my blog post data (just example):
var post = {
id: 123,
title: 'Sterling Archer',
comments: [
{text: 'Comment text', tags: ['tag1', 'tag2', 'tag3']},
{text: 'Comment test', tags: ['tag2', 'tag5']}
]
}
Each unit of this JSON has appropriate Backbone's Model or Collection representation:
var PostModel = Backbone.Model.extend({
parse: function (response) {
if (response.comments) {
response.comments = new Backbone.Collection(response.comments, {
model: CommentModel
});
}
return response;
}
});
var CommentModel = Backbone.Model.extend({
parse: function (response) {
if (response.tags) {
response.tags = new Backbone.Collection(response.tags);
}
return response;
}
});
var post = new PostModel(post, {parse: true});
I need to store 'path' for each collection/model to be possible to get them something like the following:
post.get('/comments/0'); // comment model: {text: 'Comment text', tags: ['tag1', 'tag2', 'tag3']}
post.get('/comments/1/tags/1') // tag model with 'tag5'
It would be cool if possible get absolute path of model and/or colleciton
That is the best approach to make this pissible?
You can override Backbone.Model Class, add custom _get method which support absolute path. here is override code
Backbone.Model = Backbone.Model.extend({
_get : function(path)
{
if(!path)
return;
var array = path.split('/');
var that = this;
var result = that;
_.each(array, function(o, i){
if(o && result)
{
if(result instanceof Backbone.Collection)
result = result.at(o);
else if(result instanceof Backbone.Model)
result = result.get(o);
else if(_.isArray(result) || _.isObject(result))
result = result[o];
else
result = undefined;
}
});
return result;
}
});
this will support all data structure like, Model, Collection, Array, Object
you can use it by
post._get('/comments/0');
post._get('/comments/1/tags/1');