Parse.Collection.fetch doesn't take "parse: true" option into account - javascript

I have a model where I want to add a parse method in order to do extra data work (setting up a moment.js object for date fields).
But the function is never called (both model and collection).
collection :
class SomethingCollection extends Parse.Collection
model: SomethingModel
parse: ->
console.log('parse from collection')
model :
class SomethingModel extends Parse.Object
className: 'Something'
parse: ->
console.log('parse from model')
from a view :
#collection = new SomethingCollection()
#listenTo( #collection, 'add', -> console.log('fire add event') )
#collection.fetch(parse: true, silent: false, add: true)
EDIT :
It seems to happen in the Parse.Query.find callback, see below code comments.
So it cannot be done in the initialize method as well, but where else ? I suspect Parse.Object to be not so similar with Bakbone.Model
find: function(options) {
var self = this;
options = options || {};
var request = Parse._request({
route: "classes",
className: this.className,
method: "GET",
useMasterKey: options.useMasterKey,
data: this.toJSON()
});
return request.then(function(response) {
return _.map(response.results, function(json) {
var obj;
if (response.className) {
obj = new Parse.Object(response.className); // <- no attributes or options used, blank object
} else {
obj = new self.objectClass(); // <- no attributes or options used, blank object
}
obj._finishFetch(json, true); // <- magically do things out of any constructor or parse function
return obj;
});
})._thenRunCallbacks(options);
},

I found no other way than to redeclare that cursed _finishFetch method :
original_finishFetch = _(Parse.Object.prototype._finishFetch)
Parse.Object.prototype._finishFetch = (serverData, hasData) ->
original_finishFetch.bind(#)(#parse(serverData), hasData)
That way the data is processed in the parse method, as it is expected to be with any Backbone Model, or any sdk which implements the Backbone Model interface.

Related

JavaScript: Should a function be able to create instances of itself with Object.create()

My use case is the following: I want to create a factory which produces various kinds of data transfer objects (DTOs). They must be easily serializable and they must have a few additional methods.
My current implementation looks like this (simplified):
window.Dto = function(type, properties)
{
var
self = this,
values = {},
object = Object.create(self);
properties.forEach(function(prop){
Object.defineProperty(object, prop, {
get: function() { return values[prop]; },
set: function(value) { values[prop] = value; },
enumerable: true
});
});
this.getType = function()
{
return type;
};
this.doSomeMagic = function()
{
// ...
};
return object;
};
// creating a DTO of the Transport.Motorized.Car class
var carObject = new Dto("Transport.Motorized.Car", ["vendor", "model", "color", "vmax", "price"]);
(Note: I do not want to create an explicit class for each of these objects, because there are hundets of them, and they are exported from the server side. Also, what you see as properties parameter above, is actually a map of meta data with validation constraints etc.)
I did a quick performance check with a loop where 50,000 of such objects were created. performance.now() tells me that it took a bit more than 1s – which looks ok, but not too impressive.
My question is mainly: Is it ok that the factory creates an instance from its own prototype (if I understand correctly what that code does) and returns it? What side effects can it have? Is there a better way?
As far as I understand factory functions, their whole point is not needing to create new instances of the function itself. Instead, it just returns a newly created object.
So instead of using instance properties (via this) of the newly created instance (via the new operator), I would just create an object (let's call it factoryProto) and assign all the "instance" methods to that object instead.
Then, you can use factoryProto as the [[Prototype]] for your new object:
window.Dto = function(type, properties) {
var factoryProto = {
getType: function() {
return type;
},
doSomeMagic: function() {
// ...
}
},
values = {},
object = Object.create(factoryProto);
properties.forEach(function(prop) {
Object.defineProperty(object, prop, {
get: function() { return values[prop]; },
set: function(value) { values[prop] = value; },
enumerable: true
});
});
return object;
};
// creating a DTO of the Transport.Motorized.Car class
var carObject = Dto("Transport.Motorized.Car", ["vendor", "model", "color", "vmax", "price"]);
If you want to fully profit from the prototype-chain, you could define the factoryProto outside of the factory function. To keep track of type, you could add it as a non-enumerable object property:
window.Dto = (function() {
var factoryProto = {
getType: function() {
return this.type;
},
doSomeMagic: function() {
// ...
}
};
return function(type, properties) {
var values = {},
object = Object.create(factoryProto);
properties.forEach(function(prop) {
Object.defineProperty(object, prop, {
get: function() { return values[prop]; },
set: function(value) { values[prop] = value; },
enumerable: true
});
});
Object.defineProperty(object, 'type', {
value: type,
enumerable: false
});
return object;
};
})();
// creating a DTO of the Transport.Motorized.Car class
var carObject = Dto("Transport.Motorized.Car", ["vendor", "model", "color", "vmax", "price"]);

Set a Backbone collection model with circular dependencies in requirejs

The thing is that I have a circular dependecy between some Backbone modules so I have to use "exports" as Requirejs scpecifies in its documentation http://requirejs.org/docs/api.html#circular. So the model 'A' will look like this:
define(function(require, exports) {
var B = require('B');
var A = Backbone.Model.extend({
});
exports.model = A;
});
And the collection 'B' like this:
define(function(require, exports) {
var A = require('A');
var B = Backbone.Model.extend({
model: A.model
});
exports.model = B;
});
The problem here is that by the time I have to specify the collection 'B' model property, the model 'A' isn't yet defined. This is the error I'm getting when I try to set the collection with models like this:
B.collection.set([{id: 1}, {id: 2}]);
Uncaught TypeError: 'undefined' is not an object (evaluating 'targetModel.prototype') (http://127.0.0.1:9999/bower_components/backbone/backbone.js:689)
Any ideas on how should I solve this problem?
From the example, it's not clear that B actually depends on A. If it's just a model:collection relationship, it might make sense to remove the dependency of the model on its collection. If it's at all possible to break the circular dependency, I would strongly encourage you to do so.
If the back-reference is truly required, though, one option might be to move the resources into the same module and do a sort of lazy export:
define(function() {
var lazyThings = {
A: null,
B: null
};
lazyThings.A = Backbone.Model.extend({
collection: things.B
});
lazyThings.B = Backbone.Collection.extend({
model: A
});
return lazyThings;
});
Alternatively, you could return lazyThings.B and later access the model from its prototype:
require('b', function (B) {
var A = B.prototype.model; // A
});
Finally, requirejs could be made to work by calling the respective dependencies lazily (i.e., after the modules are resolved):
// B
define(['a'], function (A) {
return function () {
return Backbone.Collection.extend({
model: A()
});
}
});
// A
define(['b'], function (B) {
return function () {
return Backbone.Model.extend({
model: B()
});
}
});
The following works for me, try to make it clear as possible.
You have a model, you have a collection. In order for them to both depend on each other + avoid a circular dependency, you need a 3rd "mediator" dependency. It's convenient in Backbone to have a model and easily lookup what collection it belongs to, and vice versa, but the problem of course is they have a circular dependency.
So before we had:
+model
+collection
__________
= circular
and after:
+model
+collection
+mediator
________
= OK
//collection
define([
'#allModels',
'#BaseCollection',
'#AppDispatcher',
'#allFluxConstants',
'app/js/flux/flux-helpers/collectionUpdater'
],
function (allModels, BaseCollection, AppDispatcher, allFluxConstants, collUpdater) {
var dispatchCallback = function (payload) {
return true;
};
var BaymaxComponentCollection = BaseCollection.extend({
model: allModels['BaymaxComponent'],
collectionName:'baymax-component',
url: '/baymax_component',
batchURL: '/batch/baymax_component',
initialize: function (models, opts) {
this.dispatchToken = AppDispatcher.register(dispatchCallback);
},
// collection is sorted by original insertion order.
comparator: 'order'
});
return new BaymaxComponentCollection();
});
//model
define([
'#BaseModel',
'#ModelCollectionMediator',
'#AppDispatcher'
],
function ( BaseModel, MCM) {
var BaymaxComponent = BaseModel.extend({
idAttribute: 'id',
urlRoot: '/baymax_component',
collectionName: 'baymax-component',
defaults: function () { //prevents copying default attributes to all instances of UserModel
return {}
},
initialize: function (attributes, opts) {
//*** the following line is crucial ***
this.collection = MCM.findCollectionByName(this.collectionName);
},
validate: function (attr) {
return undefined;
}
},
{ //class properties
});
return BaymaxComponent;
});
//mediator
define(function (require) {
return {
findCollectionByName: function (name) {
var allCollections = require('#allCollections');
return allCollections[name];
}
};
});

in javascript constructor, how to put paramterized function inside object scope?

have been using a pretty much loving Crockford's constructor, but, having problems adding scoped functions to the object:
'use strict';
var Constructor = function( params ) {
let config = params,
data = params.datum,
action = function(a,b) { return config.actions[a](b); };
return Object.freeze({
action: action
});
};
var cns = Constructor({
datum: 123,
actions: {
getData: function(b) { return data; }
}
});
cns.action('getData',0);
get Uncaught ReferenceError: data is not defined.
how do I have a function as an argument to the constructor and have that function have the scope of object?
If you are following Crockford's private members in JavaScript post, then getData should be a "privileged" function (a function that has access to private members such as data). Therefore, this function should follow the "privileged" pattern given in his post (JSFiddle example).
var Constructor = function (params) {
var config = params;
var data = params.data;
// Privileged function pattern:
// By using a closure, this method has access to private members.
this.getData = function (b) {
return data;
};
};
// Note: Changed to `new` in order to instantiate the class
var cns = new Constructor({
data: 123
});
console.log(cns.getData(0));
the easiest way seems to be to manually pass object guts to the function, either with call or as an extra argument. since I'm dodging this, am using the extra argument, here self. self is not exposed to the world at large, only to the functions that need to see it.
'use strict';
var Constructor = function( params ) {
let config = params,
data = params.datum,
self = { data: data },
action = function(a,b) { return config.actions[a](b,self); };
return Object.freeze({
action: action
});
};
var cns = Constructor({
datum: 123,
actions: {
getData: function(b,s) { return s.data; }
}
});
cns.action('getData',0);

normalizeTypeName is undefined

I am trying to get metadata back from my "contact" object to breeze so I can map the metadata to a "contactdto" object. On the server I have a Web API function called GetContactsMetadata
[HttpGet]
public IQueryable<Contact> GetContactsMetadata()
{
return _contextProvider.Context.Contacts.Take(1);
}
I'm sure I'll remove the IQueryable and/or list once I get this example running. on the client I have the following
//GetContactsMetadata
var myJsonResultsAdapter = new breeze.JsonResultsAdapter({
name: "GetContactsMetadata",
extractResults: function (json) {
return json.results;
},
visitNode: function (node, parseContext, nodeContext) {
var entityType = normalizeTypeName(node.$type);
var propertyName = nodeContext.propertyName;
var ignore = propertyName && propertyName.substr(0, 1) === "$";
return {
entityType: entityType,
nodeId: node.$id,
nodeRefId: node.$ref,
ignore: ignore
};
}
});
var dataService = new breeze.DataService({
serviceName: 'api/contacts',
jsonResultsAdapter: myJsonResultsAdapter
});
var manager = new breeze.EntityManager({ dataService: dataService });
It keeps erroring in chrome with: "normalizeTypeName is not defined". Am I calling the JsonResultsAdapter correctly?
I should have been clearer in the example.
The normalizeTypeName method is a method you as the dev would write that would take some property on the node and return a Breeze EntityType or a Breeze EntityType name. If you actually know the type name and are only using this adapter for a single type of query you can do something as simple as this:.
visitNode: function (node, parseContext, nodeContext) {
return {
entityType: "Contact" // or "ContactDTO" depending on what you are calling the type on the client.
};
}

How to prevent collection of removing model when destroyed?

Is there a way to prevent backbone.js collection of removing my model when it gets detroyed?
Everytime it happens I receive a "remove" trigger event from collection
collection.on("remove", this.handleRemove)
already tried passing some arguments but nothing
model.destroy({silent: false, remove: false})
The solution is to override the Backbone model destroy function. I made this on an abstract model with success and callback strategy:
Parameter "data" corresponds to the original parameter "resp".
destroy: function(successCallback, errorCallback)
{
var options = { wait: true };
var model = this;
successCallback = successCallback || function() {};
errorCallback = errorCallback || function() {};
var destroy = function()
{
model.trigger('destroy', model, model.collection, options);
};
options.success = function(data)
{
if ('SUCCESS' === data.responseCode)
{
if (options.wait || model.isNew())
destroy();
successCallback(data);
if (!model.isNew())
model.trigger('sync', model, data, options);
}
else
{
errorCallback(data);
}
};
if (this.isNew())
{
options.success();
return false;
}
var xhr = this.sync('delete', this, options);
if (!options.wait)
destroy();
return xhr;
}

Categories

Resources