Cannot delete array from object populated by sails.js - javascript

I cannot delete or change the value of books attribute of library object.
Library.findOne(12).populate('books').populate('createdBy').exec(
function(err,library) {
delete library.createdBy;
//worked
delete library.name;
//worked
delete library.books;
//no effect
library.books = [];
//worked
library.books = [{a:'any val'}];
//just like library.books=[]
console.log(library);
});
My model for library for books and createdBy is like
createdBy: {
model: "createdBy"
},
books: {
collection: "books",
via: "library",
dominant: true
}
I cannot figured out what is happening here.

delete library.books; does not work because associations are not fields in the model object. Associations actually live in an associations object and read/write operations are done through custom getters/setters. You can see more about this behaviour in waterline/model/lib/internalMethods/defineAssociations.js#L109:
Define.prototype.buildHasManyProperty = function(collection) {
var self = this;
// Attach to a non-enumerable property
this.proto.associations[collection] = new Association();
// Attach getter and setter to the model
Object.defineProperty(this.proto, collection, {
set: function(val) { self.proto.associations[collection]._setValue(val); },
get: function() { return self.proto.associations[collection]._getValue(); },
enumerable: true,
configurable: true
});
};
Hope that helps.
Is this causing a problem? This can be avoided by not populating associations in the first place. Doing model.toObject() or model.toJSON() and delete the association field afterwards should also work.

Related

How to get data out of ember objects

I'm fairly new to ember and I'd like to know whats the fastest way to extract the data out of ember objects. I've loaded my model with a very large amount of records using this.store.find('modelName);` in my route.
I created a component on my view using {{kendo-ui.kendo-table descriptor=tableDescriptor data=model}}. My controller defined other arguments to be passed to my component (descriptor).
In my components.js I'm' getting the data passed over by using
export default Ember.Component.extend({
didInsertElement: function() {
var columns = this.get('descriptor.columns'); // this is right
var model = this.get('data')['content']; // this returns the objects of the model
var height = this.get('descriptor.height'); // this is ok too
Ember.$('#kendo-table').kendoGrid({
dataSource: {
data: model,
pageSize: 100
},
height: height,
scrollable: {
virtual: true
},
groupable: true,
sortable: true,
columns: columns
});
}
});
On the line var model = this.get('data')['content'];, this gives me an Array of Ember Classes. Inside each class, there is a _data object that holds the value of the actual class.
The easiest solutions is to just loop through and extract the _data but that is no good for larger model array. Is there a quick way to extract all the _data from my array of ember objects?
You could use getProperties method. http://emberjs.com/api/classes/Ember.Object.html#method_getProperties
To get the values of multiple properties at once, call getProperties with a list of strings or an array:
record.getProperties('firstName', 'lastName', 'zipCode');
// { firstName: 'John', lastName: 'Doe', zipCode: '10011' }
You could define computed property dataArray:
dataArray: function() {
return this.get('data').map( function(item) {
return item.getProperties('id', ... ); // your list of properties
});
}.property('data.[]'),
didInsertElement: function() {
//...
Ember.$('#kendo-table').kendoGrid({
dataSource: {
data: this.get('dataArray'),
//...
},
// ...
});
}
UPDATE:
for records (DS.Model) you could use toJSON method. Use DS.JSONSerializer to get the JSON representation of a record.
toJSON takes an optional hash as a parameter, currently supported options are:
includeId: true if the record's ID should be included in the JSON representation.
http://emberjs.com/api/data/classes/DS.Model.html#method_toJSON

How to make toJSON wait for find before returning the object in Sails.js?

I'm running a find inside the following toJSON in the model but it return returns the object before the find completes. How can I wait until the find completes before firing the return?
toJSON: function() {
var obj = this.toObject();
Comment.find({
postID: obj.id
}).limit(2).sort('createdAt DESC').exec(function(err, comments) {
obj.comments = comments; //this is not reflected in return obj
if (obj.isTrue) {
//yes
} else {
//no
}
});
return obj; // obj.comments not reflected :(
}
The goal is for obj.comments to be in obj when it is returned.
The solution ended up being to add an associate between posts and comments.
You can find documentation on that here: http://sailsjs.org/#/documentation/concepts/ORM/Associations
For example, in the post model add an attribute like so
comments: {
collection: 'comment',
via: 'postID'
},
And in the comments model add this attribute
postId: {
model: 'hacks',
type: 'STRING',
required: true
},
when adding new documents set a comment's postId to the id of the post you'd like to associate.
Then in the post controller when you are finding posts add populate like so
.populate('comments', {
limit: 3,
sort: 'createdAt DESC'
}).exec(...

How can I fake Collection data?

What I mean by this is I want to create it artificially.
This is for testing purposes.
But for models, it is quite simple. I just set defaults I instantiate the model object and from there I can use this.model.toJSON() to grab the created data.
I want to use this same trick with collections. Is there a similar way to do this with collections? What I would want to do is have the collection create x ( 8 in this case ) copies of Model defaults.
Basically what I was doing before for models but a little bit more complex as it applies to Collections.
Here is the actual use case. It should be simple.
/**Model
**/
// name, picture, time, tweet, h_file
var FeedRow = Backbone.Model.extend({
Name: 'FeedRow',
defaults: {
name: "default",
picture: 0,
time: "0",
tweet: "default",
h_file: "default"
}
});
/**Collection
**/
var FeedTable = Backbone.Collection.extend({
Name: 'FeedTable',
model: FeedRow
});
When your FeedTable collection is constructed you could set the model on it multiple times in the initialize method.
var FeedTable = Backbone.Collection.extend(
{
Name: 'FeedTable',
model: FeedRow,
initialize: function()
{
model = this.model;
models = [];
_.times(8, function(n)
{
models.push(new model({id: (n + 1)}));
});
this.set(models);
}
});

backbone.js - handling model relationships in a RESTful way

I'm using backbone.js
For example, let's suppose we have a "products" model and a "categories" model which have a many-to-many relationship. In one of my views, say I need to retrieve a list of all categories and know whether or not each one is related to the current product model.
Do I set up a "category" collection and have it be a property of my model and somehow give it access to the id of the model so that when it is fetched, it only gets the categories that are related? And then I could fetch all categories and cross examine them to see which ones are related while still having the ones which are not?
I have no idea what the best way to do this would be. I'm used to using an ORM which makes it easy on the server-side.
Check out backbone-relational.
There is a simple & customizable solution for it, although it may not be as robust as backbone-relational.
Backbone.ModelWithRelationship = Backbone.Model.extend({
mappings: {},
set: function(attributes, options) {
_.each(this.mappings, function(constructor, key) {
var RelationshipClass = stringToFunction(constructor);
      var model = new RelationshipClass();
/* New relational model */
  if (!this.attributes[key]) {
this.attributes[key] = (model instanceof Backbone.Collection) ? model : null;
  }
  /* Update relational model */
  if (attributes[key] && !(attributes[key] instanceof Backbone.Model || attributes[key] instanceof Backbone.Collection)) {
if (model instanceof Backbone.Model) {
this.attributes[key] = model;
this.attributes[key].set(attributes[key], options);
} else if (model instanceof Backbone.Collection) {
this.attributes[key].reset(attributes[key], options);
}
delete attributes[key];
  }
}, this);
return Backbone.Model.prototype.set.call(this, attributes, options);
}
});
You can declare the mapping just by creating a subclass of Backbone.ModelWithRelationship.
Models.Post = Backbone.ModelWithRelationship.extend({
mappings: {
'comments': 'Collection.CommentCollection',
'user': 'Models.User'
}
});
http://pathable.github.com/supermodel/ is fantastic. It let's you do stuff like:
Post.has().many('comments', {
collection: Comments,
inverse: 'post'
});
Comment.has().one('post', {
model: Post,
inverse: 'comments'
});
var post = Post.create({
id: 1,
comments: [{id: 2}, {id: 3}, {id: 4}]
});
post.comments().length; // 3
var comment = Comment.create({id: 5, post_id: 1});
post.comments().length; // 4
comment.post() === post; // true :D
Assuming you are using a join table on the backend:
Create a collection and model containing all the rows on your join table and add the following methods to the collection: productsByCategory and categoriesByProduct (using [join collection].where(...).)
Having data in Backbone mirroring your data in the backend seems to help keep things simple and you won't have to do anything complicated when setting URLs.

backbone.js parse 1 element (the Id)

For a id on a model in backbone, its just id and all lower cased. What if my Id on the server is called UserId. In the parse method for backbone, how do I change UserId to id and use the same names for all other properties?
For eg.
window.User = Backbone.Model.extend({
defaults:
{
UserId: 0, // <--can i just tell backbone that this is my id?
Name: '',
Age: 0
}
parse: function(response){
var model = response;
model.id = response.UserId;
return model;
}
});
Is there a better way to do this?
How about telling backbone model that my id is of type UserId.
You have to say Backbone what property is your id using the idAttribute in the model:
window.User = Backbone.Model.extend({
idAttribute: "UserId",
...
})
and everything works fine :). Backbone will create a id property for you and collections of this model will get() by your UserId.
Do it like so:
parse: function(response) {
var attrs = {};
attrs.id = response.UserId;
return attrs;
}
Parse has the responsibility to return an attributes hash, not a model. As such, you need only transform the response into an attributes hash versus a model as you are doing.
To set the id i would do as mauromartini says. To change any other property you don't need to create any private variables, response from the server is just an object so manipulate it and return it.
window.User = Backbone.Model.extend({
defaults: { UserId: 0 },
parse: function(response){
response.id = response.UserId;
return response;
}
});
you could also add the following before you return the object if you want to keep the model clean:
delete response.UserId;

Categories

Resources