Fetching Facebook Friends List with Meteor - facebook

I added the meteorhacks:npm package and installed fbgraph using:
$ npm install fbgraph
My server side code looks like this for now:
function Facebook(accessToken) {
this.fb = Meteor.npmRequire('fbgraph');
this.accessToken = accessToken;
this.fb.setAccessToken(this.accessToken);
this.options = {
timeout: 3000,
pool: {maxSockets: Infinity},
headers: {connection: "keep-alive"}
}
this.fb.setOptions(this.options);
}
Facebook.prototype.query = function(query, method) {
var self = this;
var method = (typeof method === 'undefined') ? 'get' : method;
var data = Meteor.sync(function(done) {
self.fb[method](query, function(err, res) {
done(null, res);
});
});
return data.result;
}
Facebook.prototype.getUserData = function() {
return this.query('me');
}
Facebook.prototype.getFriendsData = function() {
return this.query('/me/friendlists');
}
Meteor.methods({
getUserData: function() {
var fb = new Facebook(Meteor.user().services.facebook.accessToken);
var data = fb.getUserData();
return data;
},
getFriendsData: function() {
var fb = new Facebook(Meteor.user().services.facebook.accessToken);
var data = fb.getFriendsData();
return data;
}
});
Meteor.publish("getUserData", function () {
return Meteor.users.find({_id: this.userId});
});
Meteor.publish("getFriendsData", function(){
return Meteor.users.find({_id: this.userId});
});
My config.js is also in order I think:
Accounts.ui.config({
passwordSignupFields: "USERNAME_ONLY",
requestPermissions: {
facebook: ['email', 'user_friends'],
}
});
On the client side I have a template:
<template name="friends">
<div class="container">
{{friendlist}}
</div>
</template>
And I'm attempting to call 'getFriendsList' with:
Template.friends.helpers({
friendlist: function() {
Meteor.call("getFriendsData");
}
});
Finally, my packages.json looks like this:
{
"fbgraph": "1.1.0"
}
When I try to run my app, I get an error as follows:
Exception while simulating the effect of invoking 'getFriendsData
TypeError: Meteor.npmRequire is not a function
I apologize if this is a stupid question, I'm fairly new to Meteor. And I for the life of me can't figure this one out. I'd really appreciate some help.

You need to add the npm module. Integration of npm modules isn't native to meteor with the meteorhacks:npm module. Install it with this command:
meteor add meteorhacks:npm
Whenever you add a non-meteor package via npm, you will have to use Meteor.npmRequire(). If you install via meteor add foobar you won't need to require the package.
If you have problems, try this if you are using Meteor 1.2:
rm -rf packages/npm-container
meteor remove npm-container
meteor update meteorhacks:npm
Also your template needs fixing, as it's currently not going to update based on your Meteor.call(). If you use onCreated() or onRendered() you can trigger the Meteor.call() and set a session variable that will be used by one of your helpers to populate your template:
Template.friends.onCreated(function() {
Meteor.call("getFriendsData", function(error, friends) {
if (error) {
console.log(error);
} else {
Session.set('friends', friends);
}
});
});
Template.friends.helpers({
friendlist: function() {
return Session.get('friends');
}
});
If you aren't getting anything back, change this to check if you are getting data back on the server side:
getFriendsData: function() {
console.log(Meteor.user().services.facebook.accessToken);
var fb = new Facebook(Meteor.user().services.facebook.accessToken);
var data = fb.getFriendsData();
console.log(data);
return data;
}

Related

How to load routes from DB in Meteor before everything?

I have this code to create routes from database in Meteor:
if (Meteor.isClient) {
Meteor.subscribe("routes", function() {
Routes.find({}).map(function(route) {
try {
Router.route(route.path, {
name: route.name,
waitOn: function() {
var subscribes = [];
if (typeof route.subscriptions== 'object' &&
route.subscriptions
.length > 0) {
route.subscriptions.forEach(function(subscription) {
subscribes.push(Meteor.subscribe(
subscription));
});
}
return subscribes;
},
action: function() {
this.render(route.template);
}
});
} catch (e) {
console.log("Error: " + e);
}
});
});
}
So when I type in browser a path to a route from db gives this error:
Oops, looks like there's no route on the client or the server for url: "https://localhost:3000/menus."
I think its because this code runs after pageloads. How can I work around it?

Debugging Ember-cli-mirage when routes are not being called

I have successfully created one route in ember-cli-mirage, but am having trouble loading the related data.
The API should be returning JSON API compliant data.
I'm not really sure if there are any good methods or not for debugging mirage's request interception. Here is my config.js
export default function() {
this.urlPrefix = 'https://myserver/';
this.namespace = 'api/v1';
this.get('/machines', function(db, request) {
return {
data: db.machines.map(attrs => (
{
type: 'machines',
id: attrs.id,
attributes: attrs
}
))
};
});
this.get('/machines/:id', function(db, request){
let id = request.params.id;
debugger;
return {
data: {
type: 'machines',
id: id,
attributes: db.machines.find(id),
relationships:{
"service-orders": db["service-orders"].where({machineId: id})
}
}
};
});
this.get('/machines/:machine_id/service-orders', function(db, request){
debugger; // this never gets caught
});
}
Most of this is working fine (I think). I can create machines and service orders in the factory and see the db object being updated. However, where my application would normally make a call to the api for service-orders: //myserver/machines/:machine_id/service-orders, the request is not caught and nothing goes out to the API
EDIT:
This is the route that my Ember app is using for /machines/:machine_id/service-orders:
export default Ember.Route.extend(MachineFunctionalRouteMixin, {
model: function() {
var machine = this.modelFor('machines.show');
var serviceOrders = machine.get('serviceOrders');
return serviceOrders;
},
setupController: function(controller, model) {
this._super(controller, model);
}
});
And the model for machines/show:
export default Ember.Route.extend({
model: function(params) {
var machine = this.store.find('machine', params.machine_id);
return machine;
},
setupController: function(controller, model) {
this._super(controller, model);
var machinesController = this.controllerFor('machines');
machinesController.set('attrs.currentMachine', model);
}
});
Intuitively, I would think that machine.get('serviceOrders'); would make a call to the API that would be intercepted and handled by Mirage. Which does not seem to be the case

REST Routes with mongoose and express

When I try to add a review to my product from the front-end I am getting a 404 error for PUT http://localhost:3000/products. But I am to add/update data using the following curl command using my routes:
curl --data "name=Product 1&description=Product 1 Description&shine=10&price=29.95&rarity=200&color=blue&faces=3" http://localhost:3000/products
My products router
// This handles retrieving of products
// Includes Express
var express = require('express');
// Initialize the router
var router = express.Router();
var moment = require('moment');
var _ = require('underscore');
var color = require('cli-color');
var mongoose = require('mongoose');
var Product = mongoose.model('Product');
var Review = mongoose.model('Review');
// Route middleware
router.use(function(req, res, next) {
console.log("Something is happening in products!!");
next();
});
// GET route for all Products
router.get('/', function (req, res, next) {
Product.find( function (err, products) {
if (err) {
return next(err);
}
res.json(products);
});
});
// POST route for adding a Product
router.post('/', function (req, res, next) {
var product = new Product (req.body);
product.save( function (err, post) {
if(err) {
return next(err);
}
res.json(product);
});
});
// Pre-loading product object
router.param('product', function (req, res, next, id) {
var query = Product.findById(id);
query.exec( function (err, product) {
if (err) {
return next(err);
}
if(!product) {
return next(new Error('can\'t find product'));
}
req.product = product;
return next();
})
});
// GET route for retrieving a single product
router.get('/:product', function (req, res) {
req.product.populate('reviews', function (err, product) {
if (err) {
return next(err);
}
res.json(req.product);
});
});
// POST route for creating a review
router.post('/:product:reviews', function (req, res, next) {
var review = new Review(req.body);
review.product = req.product;
review.save( function (err, review){
if (err) {
return next(err);
}
req.product.reviews.push(review);
req.product.save( function (err, review) {
if (err) {
return next(err);
}
res.json(review);
});
});
});
This code is taken from a tutorial on thinkster for [MEAN stackl2
Original Post
I am having trouble figuring out how to update an existing entry in my mongodb database using a service I defined with ngResource in my Angular app. So far I have been unable to create a function that will update the back-end after a user clicks my submit button. I have been looking around for a solution for about 2 days but so far have not found a solution. I know the solution is similar to how I delete users in My User's Controller, but nothing I have tried has worked.
My Product Service
angular.module('gemStoreApp.productService',['ngResource'])
.factory('productsService', function($resource) {
return $resource('/products/:id', {},{
'update': { method: 'PUT'}
});
});
My Product Detail
angular.module('gemStoreApp')
.controller("ReviewCtrl", ['$scope', '$resource', 'productsService', function ($scope, $resource, productsService) {
this.review = {};
this.addReview = function(product){
product.reviews.push(this.review);
productService.save({id: product._id}, function() {
// I have tried .update, .$update, and .save methods
});
this.review = {};
};
}]);
I have verified that the products.review variable contains the update. Here is a sample of my JSON output from my console before and after adding the review:
Before the review is added to the front end
{"_id":"product_id","name":"Product 1","description":"Product 1 Description",...,"reviews":[{}]}
After the review is added to the front end
{"_id":"product_id","name":"Product 1","description":"Product 1 Description",...,"reviews":[{"stars":4,"body":"An Awesome review!","author":"user#domain.com","createdOn":1436963056994}]}
And I know that my productsService.save() function is being called as well, as I can put a console log in and see it run when I view in the browser.
My User's Controller
angular.module('gemStoreApp')
.controller('UsersCtrl', ['$scope', '$http', 'usersService', function ($scope, $http, usersService) {
$scope.users = {};
$scope.users = usersService.query();
$scope.remove = function(id) {
var user = $scope.users[id];
usersService.remove({id: user._id}, function() {
$scope.users.splice(user, 1);
});
};
}]);
My full source code is available on my Github page. Any help will be greatly appreciated.
I actually put it into work in this plunker
Took the same factory :
app.factory('productsService', function($resource) {
return $resource('product/:id', {id:"#id"},{
'update': { method: 'PUT'}
});
});
here is my controller :
$scope.products = productsService.query();
$scope.saveProduct = function(product){
product.$update();
}
and how i pass the value in the HTML :
<div ng-repeat="product in products">
<input type="text" ng-model="product.text">
<button ng-click="saveProduct(product)">Update</button>
</div>
If you track the networks request in the javascript console you will see a request : PUT /product/id with the updated data.
Hope it helped. If you have anymore question fell free to ask.

Add new data from restful api to angularjs scope

I'm trying to create a list with endless scroll in angularjs. For this I need to fetch new data from an api and then append it to the existing results of a scope in angularjs. I have tried several methods, but none of them worked so far.
Currently this is my controller:
userControllers.controller('userListCtrl', ['$scope', 'User',
function($scope, User) {
$scope.users = User.query();
$scope.$watch('users');
$scope.orderProp = 'name';
window.addEventListener('scroll', function(event) {
if (document.body.offsetHeight < window.scrollY +
document.documentElement.clientHeight + 300) {
var promise = user.query();
$scope.users = $scope.users.concat(promise);
}
}, false);
}
]);
And this is my service:
userServices.factory('User', ['$resource',
function($resource) {
return $resource('api/users', {}, {
query: {
method: 'GET',
isArray: true
}
});
}
]);
How do I append new results to the scope instead of replacing the old ones?
I think you may need to use $scope.apply()
When the promise returns, because it isnt
Part of the angular execution loop.
Try something like:
User.query().then(function(){
$scope.apply(function(result){
// concat new users
});
});
The following code did the trick:
$scope.fetch = function() {
// Use User.query().$promise.then(...) to parse the results
User.query().$promise.then(function(result) {
for(var i in result) {
// There is more data in the result than just the users, so check types.
if(result[i] instanceof User) {
// Never concat and set the results, just append them.
$scope.users.push(result[i]);
}
}
});
};
window.addEventListener('scroll', function(event) {
if (document.body.offsetHeight < window.scrollY +
document.documentElement.clientHeight + 300) {
$scope.fetch();
}
}, false);

Creating new Meteor collections on the fly

Is it possible to create new Meteor collections on-the-fly? I'd like to create foo_bar or bar_bar depending on some pathname which should be a global variable I suppose (so I can access it throughout my whole application).
Something like:
var prefix = window.location.pathname.replace(/^\/([^\/]*).*$/, '$1');
var Bar = new Meteor.Collection(prefix+'_bar');
The thing here is that I should get my prefix variable from URL, so if i declare it outside of if (Meteor.isClient) I get an error: ReferenceError: window is not defined. Is it possible to do something like that at all?
Edit : Using the first iteration of Akshats answer my project js : http://pastie.org/6411287
I'm not entirely certain this will work:
You need it in two pieces, the first to load collections you've set up before (on both the client and server)
var collections = {};
var mysettings = new Meteor.Collection('settings') //use your settings
//Startup
Collectionlist = mysettings.find({type:'collection'});
Collectionlist.forEach(function(doc) {
collections[doc.name] = new Meteor.Collection(doc.name);
})'
And you need a bit to add the collections on the server:
Meteor.methods({
'create_server_col' : function(collectionname) {
mysettings.insert({type:'collection', name: collectionname});
newcollections[collectionname] = new Collection(collectionname);
return true;
}
});
And you need to create them on the client:
//Create the collection:
Meteor.call('create_server_col', 'My New Collection Name', function(err,result) {
if(result) {
alert("Collection made");
}
else
{
console.log(err);
}
}
Again, this is all untested so I'm just giving it a shot hopefully it works.
EDIT
Perhaps the below should work, I've added a couple of checks to see if the collection exists first. Please could you run meteor reset before you use it to sort bugs from the code above:
var collections = {};
var mysettings = new Meteor.Collection('settings')
if (Meteor.isClient) {
Meteor.startup(function() {
Collectionlist = mysettings.find({type:'collection'});
Collectionlist.forEach(function(doc) {
eval("var "+doc.name+" = new Meteor.Collection("+doc.name+"));
});
});
Template.hello.greeting = function () {
return "Welcome to testColl.";
};
var collectionname=prompt("Enter a collection name to create:","collection name")
create_collection(collectionname);
function create_collection(name) {
Meteor.call('create_server_col', 'tempcoll', function(err,result) {
if(!err) {
if(result) {
//make sure name is safe
eval("var "+name+" = new Meteor.Collection('"+name+"'));
alert("Collection made");
console.log(result);
console.log(collections);
} else {
alert("This collection already exists");
}
}
else
{
alert("Error see console");
console.log(err);
}
});
}
}
if (Meteor.isServer) {
Meteor.startup(function () {
// code to run on server at startup
Collectionlist = mysettings.find({type:'collection'});
Collectionlist.forEach(function(doc) {
collections[doc.name] = new Meteor.Collection(doc.name);
});
});
Meteor.methods({
'create_server_col' : function(collectionname) {
if(!mysettings.findOne({type:'collection', name: collectionname})) {
mysettings.insert({type:'collection', name: collectionname});
collections[collectionname] = new Meteor.Collection(collectionname);
return true;
}
else
{
return false; //Collection already exists
}
}
});
}
Also make sure your names are javascript escaped.
Things got much easier:
var db = MongoInternals.defaultRemoteCollectionDriver().mongo.db;
db.createCollection("COLLECTION_NAME", (err, res) => {
console.log(res);
});
Run this in your server method.