How to get an entity in marionette.js using an existing backend service? - marionette

Suppose I have the following entity:
MyApp.module('Entities', function(Entities, MyApp, Backbone, Marionette, $, _) {
Entities.SomeEntity = Backbone.Model.extend({});
var API = {
getSomeEntity: function(someEntityId) {
// What goes here?
}
};
MyApp.reqres.setHandler("someEntity:entities", function(someEntityId) {
return API.getSomeEntity(someEntityId);
});
});
And suppose I have a backend service /getInfo?id= which returns a JSON object {foo: "bar"}.
Question:
1) What should I place in the API.getSomeEntity method?
2) How to make it asynchronous?

Basically, Backbone considers that if a model has a non-null id attribute, it exists on the remote API. And if you call fetch on that model instance, it will contact the API requesting the data. So you'd do something like this:
getSomeEntity: function(someEntityId) {
var myModel = new Entities.SomeEntity({id: someEntityId});
myModel.fetch();
return myModel;
}
Since you're using a non-standard API, you'll need to define your API endpoint also:
Entities.SomeEntity = Backbone.Model.extend({
url: function(){
return "/getInfo?id=" + this.get('id');
}
});
As to your last question: Backbone fetches the data asynchronously by default/design.

Related

Parse Cloud Right Query to retrieve Friendslist and not (like Instagram)

I have the class "Users" the default of Parse Dashboad. I have also the class "Friends", where I store the friendships between users like this:
There are two columns, "toUser" and "fromUser", which are showing who of the two users sent the friend request. These two columns are Pointers(Pointer<_User>) to the Users Class.
My concept is to retrieve two lists:
1. Friendlist for the logged in user
2. Userlist of users (who are not friends with the logged in user)
What would be the appropriate queries for that?
The first one logically should not scan all the class Users because it may slow down the perfomance of the app which is using Ionic 3 and Angular 4
Thank you
you don't need another class to do so all you need is a relation column in the default user class
to add new friend you just need the following code
var friendtoadd
//this is the friend object from parse user class you want to add you can get it by anyway you want
var currentUser = Parse.User.current();
var relation = currentUser.relation("friends");
relation.add(friendtoadd)
currentUser.save();
to retrieve array of friends of logged in user you can use the following code
var currentUser = Parse.User.current();
var friendRelation = currentUser.relation('friends');
var env = this
friendRelation.query().find({
success: function(users) {
for (var i = 0; i< users.length; i++) {
var object = users[i];
env.friends.push(object)
console.log(env.friends)
}
}
});
// you should define friends = []; in the class
if I understood your question right you want to find the friend requests you sent, or the ones you received. because I don't see where you made a relation between the user and his friends.
this is the code if you want to do this using cloud code:
First I validated the parameters of the friendRequest being saved :
Parse.Cloud.beforeSave("Friends", function(request, response){
var friendRequest = request.object;
if (!friendRequest.has("toUser") || !friendRequest.has("fromUser")) {
response.error("Invalid parameters");
return;
}
response.success();
});
then I created two cloud functions, one for retrieving the sentRequests:
Parse.Cloud.define("getSentRequests", function(request, response){
var query = new Parse.Query("Friends");
query.equalTo("fromUser", request.user);
if (!request.master) {
query.find({ sessionToken: request.user.getSessionToken() }).then(function(friends){
response.success(friends);
});
}else{
query.find({useMasterKey:true}).then(function(friends){
response.success(friends);
});
}
});
and you can call this either from a logged in user or using the masterKey if you want, and the other query is for the recievedRequests:
Parse.Cloud.define("getRecievedRequests", function(request, response){
var query = new Parse.Query("Friends");
query.equalTo("toUser", request.user);
if (!request.master) {
query.find({ sessionToken: request.user.getSessionToken() }).then(function(users){
response.success(users);
});
}else{
query.find({useMasterKey:true}).then(function(users){
response.success(users);
});
}
});

Angular Meteor objects not acting as expected

I am working with Angular Meteor and am having an issue with my objects/arrays. I have this code:
angular.module("learn").controller("CurriculumDetailController", ['$scope', '$stateParams', '$meteor',
function($scope, $stateParams, $meteor){
$scope.curriculum = $meteor.object(CurriculumList, $stateParams.curriculumId);
$scope.resources = _.map($scope.curriculum.resources, function(obj) {
return ResourceList.findOne({_id:obj._id})
});
console.log($scope.resources)
}]);
I am attempting to iterate over 'resources', which is a nested array in the curriculum object, look up each value in the 'ResourceList' collection, and return the new array in the scope.
Problem is, sometimes it works, sometimes it doesnt. When I load up the page and access it through a UI-router link. I get the array as expected. But if the page is refreshed, $scope.resources is an empty array.
My thought is there is something going on with asynchronous calls but have not been able for find a solution. I still have the autopublish package installed. Any help would be appreciated.
What you're going to do is return a cursor containing all the information you want, then you can work with $meteor.object on the client side if you like. Normally, publishComposite would look something like this: (I don't know what your curriculum.resources looks like)
Use this method if the curriculum.resources has only ONE id:
// this takes the place of the publish method
Meteor.publishComposite('curriculum', function(id) {
return {
find: function() {
// Here you are getting the CurriculumList based on the id, or whatever you want
return CurriculumList.find({_id: id});
},
children: [
{
find: function(curr) {
// (curr) will be each of the CurriculumList's found from the parent query
// Normally you would do something like this:
return ResourceList.find(_id: curr.resources[0]._id);
}
}
]
}
})
This method if you have multiple resources:
However, since it looks like your curriculum is going to have a resources list with one or many objects with id's then we need to build the query before returning anything. Try something like:
// well use a function so we can send in an _id
Meteor.publishComposite('curriculum', function(id){
// we'll build our query before returning it.
var query = {
find: function() {
return CurriculumList.find({_id: id});
}
};
// now we'll fetch the curriculum so we can access the resources list
var curr = CurriculumList.find({_id: id}).fetch();
// this will pluck the ids from the resources and place them into an array
var rList = _.pluck(curr.resources, '_id');
// here we'll iterate over the resource ids and place a "find" object into the query.children array.
query.children = [];
_.each(rList, function(id) {
var childObj = {
find: function() {
return ResourceList.find({_id: id});
}
};
query.children.push(childObj)
})
return query;
});
So what should happen here (I didn't test) is with one publish function you will be getting the Curriculum you want, plus all of it's resourceslist children.
Now you will have access to these on the client side.
$scope.curriculum = $meteor.object(CurriculumList, $stateParams.curriculumId);
// collection if more than one, object if only one.
$scope.resources = $meteor.collection(ResoursesList, false);
This was thrown together somewhat quickly so I apologize if it doesn't work straight off, any trouble I'll help you fix.

Is it possible to retrieve data of all classes in Parse using single REST URL request?

just a scenario :
I have 4 classes created in Parse cloud database for a particular Application - ClassA, ClassB, ClassC, ClassD.
I can retrieve data related to ClassA using REST URL like - https://api.parse.com/1/classes/ClassA
Is it possible to retrieve data of all 4 classes using single REST URL ?
No, it's not possible to do this. You can query from a single class at a time, and a maximum of 1,000 objects.
A cloud function can make multiple queries and merge the results, meaning that a single REST call (to call the function) could return results from multiple classes (but a maximum of 1,000 objects per query). Something like this:
Parse.Cloud.define("GetSomeData", function(request, response) {
var query1 = new Parse.Query("ClassA");
var query2 = new Parse.Query("ClassB");
query1.limit(1000);
query2.limit(1000);
var output = {};
query1.find().then(function(results) {
output['ClassA'] = results;
return query2.find();
}).then(function(results) {
output['ClassB'] = results;
response.success(output);
}, function(error) {
response.error(error);
});
});

Backbone, rest, populate collection

I'm trying to do my first webapp with backbone/mvc3 and i would like to have some advices to populate a collection.
Here is a part of my collection
window.TaskList = Backbone.Collection.extend({
model: Task,
url: "../../api/Tasks";
},.......
I can use the crud methods to get/update the models but i've the following problem :
When i open the page, my collection is populated (calling the get method serverside) But i would like to have this kind of behavior :
Page 1 : put/delete/get methods => as usual but the collection has to be populated calling the getTasksByWorkshopId serverside method
Page 2 : put/delete/get methods => as usual but the collection has to be populated calling another serverside method to filter the list
...
(ie : i cant filter the collection client side because of the amount of data)
So, my question is : how to keep a generic collection url (as api/Tasks) and populate the collection with another method (do i have to override smth ?)
(sorry for this newbie question)
Thanks in advance
In a comment to the other answer you said that "When the collection is loaded, the url called is /api/Tasks/Workshop/1 (the good one) but, when i want to update a task, the url called is /api/Tasks/Workshop/1/141 instead of /api/Tasks/141."
In order to "update a task" (a task model, I assume) to a different URL, then your Collection & Model should have different URLs. If you define a collection without specifying the model property, the URL used when saving/fetching/deleting a model will be based off of the collection's URL. The same is also true if the collection's model has no defined url property. See below.
Also, JSFiddle example here.
var WorkshopModel = Backbone.Model.extend({
urlRoot: "api/tasks/"
});
var WorkshopCollection = Backbone.Collection.extend({
model: WorkshopModel,
urlRoot: "api/tasks/workshop",
url: function() { return this.urlRoot + '/' + this.id; },
initialize: function(models, options) {
this.id = options.id;
}
});
var c = new WorkshopCollection(null, { id: 1 });
c.fetch(); // GET => api/tasks/workshop/1
var m = c.add({ id: 300, color: 'red' });
m.save(); // PUT => api/tasks/300
m.destroy(); // DELETE => api/tasks/300
m.fetch(); // GET => api/tasks/300
If you remove the urlRoot property from the WorkshopModel, then the URL that the models use will be the collection.url() + '/' + model.id ( api/tasks/workshop/1/300 )
You can do like this :
window.TaskList = Backbone.Collection.extend({
model: Task,
urlRoot: "../../api/Tasks",
url: function() {
if (/*page 1*/) { // you can access this.options where you can pass parameters to distinct the 2 services, when calling the fetch function
return this.urlRoot + // getTasksByWorkshopId URL ;
} else {
return this.urlRoot + // the other service URL ;
}
} ...
}

AngularJS and "smart" Caching

I want to implement "smart" caching in my application. I want to always first return data from the cache (if none is available an empty object/array is returned), then always fetch the data from the server and replace the cached response with the updated server response. The objective is to always quickly show something to the user.
I want to do it in an "angular" fashion, i.e - adher to the promise paradigm.
I found a solution that uses the $resource service (http://www.bennadel.com/blog/2432-Applying-A-Cached-Response-To-An-AngularJS-Resource.htm), but $resource pretty much sucks if you don't use only the 4-5 default REST methods that it offers. Its custom method functionality is severly lacking. I'd really like to use the low level $http service, since it gives me better control over my requests, while keeping my controllers oblivious to the whole caching functionlity (i.e - avoid fetching data from the cache first in the controller itself and then querying the service).
Has anyone dealt with this problem and has a better solution?
Thanks :-)
http://jsfiddle.net/G23h7/
I created two services to accomplish what I think you're trying to accomplish. The first provides the core functionality to take in a promise and an object (or array) and updates said object or array when the promise resolves. There's a GUI for you to play around with it.
The second service integrates that into $http. Basically you can do smartHttp.forArray(config) and smartHttp.forObj(config) in lieu of $http(config). If you end up using this and want to use the $http shortcut methods then that should be straightforward to implement. This is untested - so consider it as pseudocode. If you're instantly returning a cached value/dud value it doesn't really make sense to use a promise for the return value of your smartHttp service (unless you were trying to make the service interchangeable with $http). If you'd like it to be a promise for that or whatever reason you can change:
var general = function (obj, methodName) {
// ...
return obj;
};
to the following:
var general = function (obj, methodName) {
// ...
return $q.when(obj);
};
And then ask for the $q service, of course. The real issue here is equality between requests - I assume $http does that nicely; I made a naive key - you may want to change that (as long as you have simple requests/same order for everything I don't think it should matter).
myApp.factory('smartCache', function () {
var service = {};
service.forArray = function (array, promise, clear) {
promise.then(function (promiseResult) {
if (clear) {
array.length = 0;
}
angular.forEach(promiseResult, function (promiseResultElement) {
array.push(promiseResultElement);
});
});
};
service.forObj = function (obj, promise, clear) {
promise.then(function (promiseResult) {
if (clear) {
for (var prop in obj) {
delete obj[prop];
}
}
for (var prop in promiseResult) {
obj[prop] = promiseResult[prop];
}
});
};
return service;
});
myApp.factory('smartHttp', function ($http, smartCache, $cacheFactory) {
var cache = $cacheFactory('smartHttp');
var service = {};
var general = function (config, methodName, initialValue) {
var obj;
var key = JSON.stringify([ config.url, config.method, config.params, config.data ]);
var cachedObj = cache.get(key);
if (cachedObj !== undefined) {
obj = cachedObj;
} else {
obj = initialValue;
}
var promise = $http(config);
var smartCachePromise = promise.then(function (result) {
return result.data;
});
smartCache[methodName](obj, smartCachePromise, true);
return obj;
};
service.forObj = function (config) {
return general(config, 'forObj', {});
}
service.forArray = function (config) {
return general(config, 'forArray', []);
}
return service;
});

Resources