using require.js with FB SDK - javascript

I would like to load FB SDK using require.js.
my test case is something like this:
test.js:
require([
'libs/facebook/fb'
], function(FB){
FB.api("/me", function(){});
));
I would like to have test.js run only after FB SDK is loaded, and have FB ready for it.
Any thoughts on how this can be achieved? what should my wrapper (libs/facebook/fb.js) have?

It doesn't seem like the FB API is an AMD module, so it doesn't define itself in a manner to which RequireJS is accustomed to. You will need to shim the FB API using require.config. I'm assuming test.js is the script you have provided as the data-main value for RequireJS.
require.config({
shim: {
'facebook' : {
exports: 'FB'
}
},
paths: {
'facebook' : 'libs/facebook/fb'
}
});
require(['facebook'], function(FB){
FB.api('/me', function(){});
});

Or wrap the init code in a module (the sample uses Dojo):
define( 'facebook',
[ 'dojo/dom-construct',
'dojo/_base/window',
'https://connect.facebook.net/en_US/all/debug.js' ], // remove "/debug" in live env
function( domConstruct, win )
{
// add Facebook div
domConstruct.create( 'div', { id:'fb-root' }, win.body(), 'first' );
// init the Facebook JS SDK
FB.init( {
appId: '1234567890', // App ID from the App Dashboard
channelUrl: '//' + window.location.hostname + '/facebook-channel.html', // Channel File for x-domain communication
status: true, // check the login status upon init?
cookie: true, // set sessions cookies to allow your server to access the session?
xfbml: true // parse XFBML tags on this page?
} );
// Additional initialization code such as adding Event Listeners goes here
console.log( 'Facebook ready' );
return FB;
}
);

Here is a Documentation from Facebook:
https://developers.facebook.com/docs/javascript/howto/requirejs/
require.config({
shim: {
'facebook' : {
export: 'FB'
}
},
paths: {
'facebook': '//connect.facebook.net/en_US/all'
}
})
require(['fb']);
and then add the module like this:
define(['facebook'], function(){
FB.init({
appId : 'YOUR_APP_ID',
channelUrl : '//yourdomain.com/channel.html'
});
FB.getLoginStatus(function(response) {
console.log(response);
});
});

Building on voidstate's and Dzulqarnain Nasir's Answers, here is the code I ended up using on my project.
The part that tripped me up the most was that FB.init() is apparently asynchronous. In trying to envoke the callback() (without FB.getLoginStatus), FB was not yet initialized, and I was getting "An active access token must be used to query information about the current user." errors.
RequireJS Shim Config
require.config({
// paths: { 'facebookSDK': '//connect.facebook.net/en_US/all/debug' }, // development
paths: { 'facebookSDK': '//connect.facebook.net/en_US/all' }, // production
shim: { 'facebookSDK': { exports: 'FB' } }
});
AMD Module to Initialize Facebook JS SDK
define(['facebookSDK'], function (FB) {
'use strict';
return function (settings, callback) {
var args = {
appId: settings.appId,
channelUrl: settings.channelUrl,
status: true,
cookie: true,
xfbml: true
};
console.log('Calling FB.init:', args);
FB.init(args);
if (callback && typeof (callback) === "function") {
// callback() // does not work, FB.init() is not yet finished
FB.getLoginStatus(callback);
}
};
});
This still doesn't quite address the Original Question's desired usage.
OP's code could maybe be rewritten as:
require(['libs/facebook/fb'], // where fb.js holds my above Module
function(FBinit){
FBinit({
appId: appId,
channelUrl: channelUrl
}, function(){
FB.api("/me", function(){});
});
}
);
This isn't quite as cleanas OP's original concept, but its the best I could figure out. If anyone has any, I'd love some feedback or advice on how to improve my approach. I am still very much new to RequireJS.

Related

Use i18next with XHR backend in client-side javascript

The documentation at i18next-xhr-backend tells me to use import to load their module. But when I use the import-statement, nothing happens and Firefox gives me a SyntaxError in the developer console:
SyntaxError: import declarations may only appear at top level of a module
So how can I use i18next library with the XHR-backend? The following code example works if the .use(XHR)-line and the corresponding import is commented out (Warning: i18next::backendConnector: No backend was added via i18next.use. Will not load resources.). But it fails, if it is not: ReferenceError: XHR is not defined
//import Fetch from 'i18next-fetch-backend';
let t = null;
i18next
.use(XHR)
.init({
debug: true,
fallbackLng: ['en'],
preload: ['en'],
ns: 'translation',
defaultNS: 'translation',
keySeparator: false, // Allow usage of dots in keys
nsSeparator: false,
backend: {
loadPath: '/locales/{{lng}}/{{ns}}.json',
},
}, (err, _t) => {
if (err) {
reject(err);
return;
}
t = _t;
//resolve();
});
jqueryI18next.init(i18next, $, {
tName: 't', // --> appends $.t = i18next.t
i18nName: 'i18n', // --> appends $.i18n = i18next
handleName: 'localize', // --> appends $(selector).localize(opts);
selectorAttr: 'data-i18n', // selector for translating elements
targetAttr: 'i18n-target', // data-() attribute to grab target element to translate (if different than itself)
optionsAttr: 'i18n-options', // data-() attribute that contains options, will load/set if useOptionsAttr = true
useOptionsAttr: false, // see optionsAttr
parseDefaultValueFromContent: true // parses default values from content ele.val or ele.text
});
$(".nav").localize();
I needed to use i18nextXHRBackend instead of just XHR, since that is the name the class gets loaded as if no loader is used. As the README.md says:
If you don't use a module loader it will be added to window.i18nextXHRBackend
I didn't see that before, and I didn't know that this will happen automatically, but it seems that you have to find that out on your own if not using a module loader. Lesson learned, hopefully this will help some other newbies being stuck on how to use modules in javascript. Therefore, my complete localisation.js looks like this:
$(document).ready(function() {
i18next
.use(i18nextXHRBackend)
.use(i18nextBrowserLanguageDetector)
.init({
debug: true,
backend: {
loadPath: 'locales/{{lng}}/{{ns}}.json',
addPath: 'locales/add/{{lng}}/{{ns}}'
}
}, function(err, t) {
jqueryI18next.init(i18next, $);
$('.translatable').localize();
$('.language-button').click(function() {
i18next.changeLanguage(this.firstElementChild.alt).then(function(t) {
$('.translatable').localize();
$('#signupPassword').pwstrength("forceUpdate");
$('#signupPasswordConfirm').pwstrength("forceUpdate");
});
});
});
});

Facebook Canvas App: This method is only accessible to Games

I get the following error when trying to access a user's invitable friends list through a Facebook Canvas App:
"error":{"message":"(#15) This method is only accessible to Games."
I have searched all over the web and the consensus to get past this error is that your app has to be :
classified as a 'Game' within your app's settings
have a Canvas presence - which means enabling the Canvas platform within your app's settings
So my Facebook app was already set as a Canvas app. I did this under 'Settings' and just added my Secure Canvas URL.
Next I had to classify my app as a 'game'. OK, less clear, but I changed the Category of my app to 'Game' under 'App Details' and I also set a 'Sub Category' for my game.
But the error persists. I am still getting: "error":{"message":"(#15) This method is only accessible to Games."
The call that generates this error is just the stock standard invitable friends javascript that I got from the Facebook website:
/* make the API call */
FB.api(
"/me/invitable_friends",
function (response) {
if (response && !response.error) {
/* handle the result */
}
}
);
Please help!
this works perfectly with yours conf like the tutorial:
$( document ).ready(function() {
FB.init({
appId: appId ,
frictionlessRequests: true,
status: true,
version: 'v2.4'
});
FB.AppEvents.activateApp();
FB.Event.subscribe('auth.authResponseChange', onAuthResponseChange);
FB.Event.subscribe('auth.statusChange', onStatusChange);
});
function onStatusChange(response) {
if( response.status != 'connected' ) {
login(loginCallback);
} else {
getMe(function(){
getPermissions(function(){
if(hasPermission('user_friends')) {
getFriends(function(){
getInvitablefriends();
});
} else {
console.log("without permissions")
}
});
});
}
}
function getInvitableFriends(callback) {
FB.api('/me/invitable_friends', {fields: 'id,name,first_name,picture.width(120).height(120)'}, function(response){
if( !response.error ) {
console.log(response.data);
callback();
} else {
console.error('/me/invitable_friends', response);
}
});
}

facebook login with javascript - server related issues?

I'm experiencing issues with login into FB account with javascript from our website.
The login is simple, we defined the scope: email, user_birthdate, ... what we need.
On one server I can all the info we need, on another only basic information, ie: birthdate is missing and hometown as well.
Is there any way to prevent this? It might be I missed something out.
Thanks for your suggestions.
   window.fbAsyncInit = function() {
   FB.init({
   appId : fb_app_id, // App ID
   apiKey : fb_app_secret,
channelURL : '', // Channel File, not required so leave empty
   status : false, // check login status
cookie : true, // enable cookies to allow the server to access the session
oauth : true, // enable OAuth 2.0
xfbml : true, // parse XFBML
frictionlessRequests : true
   });
    function login(){
FB.getLoginStatus(function(response){
if(response.status === 'connected'
|| response.authResponse != null){
FB_response(response);
}
else{
FB.login(function(response) {
if(response.authResponse) {
//alert('connected' );
FB_response(response);
}
else {
// user is not logged in
}
},{scope:'email, user_about_me, user_birthday, user_hometown, user_location'});
}
});
}
I guess you try it with different users... All admin/tester/developer users of an app can give their permissions without the app being reviewed yet. All other users can't, and therefore you only see the basic info.
See
https://developers.facebook.com/docs/apps/review/login#do-you-need-review

method won't trigger when using requireJS

I have some issue with some requireJS setup. I posted a question before but the scope of the latest changed now.
I have some
requirejs.config({
paths: {
'tmpl': 'vendor/upload/tmpl.min'
}
});
require({
paths: {
'videoupload': 'vendor/upload/jquery.ui.videoupload'
}
}, ['js/main_video.js'], function (App) {
App.initial_video_upload();
});
and finally in main_video.js :
define(['tmpl', 'videoupload'], function () {
function initial_video_upload(tmpl, videoupload) {
'use strict';
$('#videoupload').videoupload({
//...some code
});
}
return{
initial_video_upload: initial_video_upload
}
}
);
This code works perfectly if I don't use requireJS (loading classically each file). In fact, when this code is triggered, I keep on having a message Uncaught TypeError: Object [object Object] has no method 'tmpl', this method is defined in tmpl.min.js. And this method is invoked in vendor/upload/jquery.ui.videoupload, as so
$.widget('videoupload', {
//...
_renderVideo: function (video) {
this._templateElement().tmpl({
id: video.id,
name: video.title
}).appendTo(this._listElement()).find(
this.options['delete-selector']
);
return this;
},
//...
How can I manage that ? (I had earlier an error time out message for this method tmpl, but it disappeared now, so I don't think this is it)
In the configuration object, the path is not the full path to the JS file BUT the path to the directory containing the JS file, so you may want to do something like this in the main_video.js file:
requirejs.config({
paths:{
'upload': 'vendor/upload'
}
});
define(['upload/tmpl','upload/jquery_videoupload'],function(tmpl, videoupload) {
function initial_video_upload(tmpl,videoupload){
'use strict';
$('#videoupload').videoupload({
//...some code
});
}
return{
initial_video_upload: initial_video_upload
}
}
);
And in the main app:
requirejs.config({
paths:{
'js': 'path/to/your/js/folder'
}
});
require(['js/main_video'], function(App) {
App.initial_video_upload();
});
There's a problem in the questions code, so this:
define(['tmpl', 'videoupload'], function () {
should become this:
define(['tmpl', 'videoupload'], function (tmpl, videoupload) {
The first one doesn't expose loaded dependencies to local variables of closure function, so that's might be a problem, although it's not very clear if it's the only one, from the provided code.
I would also like to mention, that it's not a good thing to use multiple requre.js configs, if you're intended to use optimizer. The configs will be overwritten by the last one, so it's a good idea actually to have only one config for the whole project.
Like this:
requirejs.config({
paths: {
'tmpl': 'vendor/upload/tmpl.min',
'videoupload': 'vendor/upload/jquery.ui.videoupload'
}
});

Ember.application.create arguments

I am exploring one of Ember.js demo application and I find lot of arguments being passed to Ember.Application.create() in addition to usual VERSION and rootElement. The demo uses require.js which I understood is for AMD.
Does Ember.js does not take care by itself in its v1.0 to load javascripts based on need ? If it does so, I don't fully understand why should require.js be used with it..
Second, if require.js has a use case, what is the use of passing as many arguments like controller names and view names to Ember.Application.create() to bootstrap application.
// Define libraries
require.config({
paths:{
jquery:'lib/jquery-1.8.0.min',
handlebars:'lib/handlebars',
ember:'lib/ember_1.0pre',
ember_data:'lib/ember-data5',
text:'lib/require/text',
md5:'lib/md5',
//domready:'lib/require/domReady',
spin:'lib/spin'
},
shim:{
'ember':{
deps:[ 'jquery', 'handlebars'],
exports:'Ember'
},
'ember_data':{
deps:[ 'ember'],
exports:'DS'
}
},
waitSeconds:15,
urlArgs:"bust=" + (new Date()).getTime() //cancel caching for network requests,for development.
});
// Define application
define('application', [
'routes/app_router',
'controllers/application_controller',
'controllers/contacts_controller',
'controllers/contact_controller',
'controllers/edit_contact_controller',
'controllers/login_controller',
'views/application_view',
'views/contact_in_list_view',
'views/contacts_view',
'views/contact_view',
'views/edit_contact_view',
'views/login_view',
'models/contact',
'jquery',
'handlebars',
'ember',
'ember_data',
// 'domready',
'spin'
], function (Router,
ApplicationController,
ContactsController,
ContactController,
EditContactController,
LoginController,
ApplicationView,
Contact_In_List_View,
ContactsView,
ContactView,
EditContactView,
LoginView,
Contact )
{
return Ember.Application.create({
VERSION: '1.0.0',
rootElement:'#main',
// Load router
Router:Router,
//Load Controllers
ApplicationController:ApplicationController,
ContactsController:ContactsController,
ContactController:ContactController,
EditContactController:EditContactController,
LoginController:LoginController,
//Load associated Views
ApplicationView:ApplicationView,
Contact_In_List_View:Contact_In_List_View,
ContactsView:ContactsView,
ContactView:ContactView,
EditContactView:EditContactView,
LoginView:LoginView,
//Load Contact Model
Contact:Contact,
//Persistence Layer,using default RESTAdapter in ember-data.js.
store:DS.Store.create({
revision:5,
adapter:DS.RESTAdapter.create({
bulkCommit:false,
serializer:DS.Serializer.create({
primaryKey:function (type) {
return type.pk;
}
}),
mappings:{
contacts:Contact
},
namespace:'api' //you should change the first segment according to the application's folder path on the server.
})
}),
ready:function () {
}
});
}
);
Ex - application_controller.js
define('controllers/application_controller',
['ember' ],
function () {
return Ember.Controller.extend({
loggedin:false
});
}
);

Categories

Resources