Goal - Have share buttons for Facebook
I've had various problems, I've managed to solve most issues but never all at the same time.
ReferenceError: FB is not defined (error in the console)
"FB init function gives wrong version error" (error in the console)
My button does not render after transitioned to another route.
My button renders but there is no spacing around the button to adjacent stuff on the initial route
About that rendering issue (the - / hyphen is there to notice the spacing issue)
Rendering for the 1st time:
Rendering for the 2nd+ time:
What I've learned:
Facebook wants a <div id="fb-root"></div> as the first element inside your <body>
FB.XFBML.parse() can be called after didInsertElement to render a component after a transition
I made a JSBin boilerplate attempt, it's currently stuck at a undefined FB error.
http://emberjs.jsbin.com/fevoyuhiso/2/edit
Partial answer I'm also interested in:
Understanding how complex the solution at least must me to achieve a good result ("it must include an initializer, and a view/component!" or "you can solve this by just having ...")
Parts that may be of use
A post about "after FB.init()"
How to detect when facebook's FB.init is complete
An initializer
/* global FB */
export default {
name: 'facebook',
initialize: function() {
var fbAsyncInit = function() {
FB.init({
appId : 123,
xfbml : true,
version : 'v2.2'
});
};
(function(d, s, id){
var js, fjs = d.getElementsByTagName(s)[0];
if (d.getElementById(id)) {return;}
js = d.createElement(s); js.id = id;
js.src = "//connect.facebook.net/en_US/sdk.js";
fjs.parentNode.insertBefore(js, fjs);
}(document, 'script', 'facebook-jssdk'));
window.fbAsyncInit = fbAsyncInit;
}
};
A component
/* global FB */
import Ember from 'ember';
export default Ember.Component.extend({
tagName: 'div',
classNames: 'fb-like',
attributeBindings: [
'data-href',
'data-layout',
'data-action',
'data-show-faces',
'data-share'
],
onDidInsertElement: function() {
Ember.run.schedule('afterRender', FB.XFBML.parse);
}.on('didInsertElement'),
init: function() {
Ember.run.schedule('afterRender', FB.XFBML.parse);
}
});
A script tag
<script type="text/javascript" src="//connect.facebook.net/en_US/sdk.js#xfbml=1&appId=123&version=v2.2"></script>
The root div facebook asks for
<div id="fb-root"></div>
Loading Facebook SDK
import ENV from "my-app/config/environment";
import setupOfflineMode from "my-app/utils/offline-mode";
export function initialize(container, application) {
// offline mode stubs `FB`
if (ENV.offlineMode) { return setupOfflineMode(); }
// Wait for Facebook to load before allowing the application
// to fully boot. This prevents `ReferenceError: FB is not defined`
application.deferReadiness();
var fbAsyncInit = function() {
initFacebook(window.FB);
application.advanceReadiness();
};
loadFacebookSDK();
window.fbAsyncInit = fbAsyncInit;
}
function loadFacebookSDK() {
(function(d, s, id){
var js, fjs = d.getElementsByTagName(s)[0];
if (d.getElementById(id)) {return;}
js = d.createElement(s); js.id = id;
js.src = "//connect.facebook.net/en_US/sdk.js";
fjs.parentNode.insertBefore(js, fjs);
}(document, 'script', 'facebook-jssdk'));
}
function initFacebook(FB) {
FB.init({
appId: ENV.FB_APP_ID,
status: true,
cookie: true,
xfbml: true,
version: ENV.GRAPH_API_VERSION
});
}
export default {
name: 'facebook',
initialize: initialize
};
Share Links
I think this is all I needed to do; I hope I'm not forgetting something...
I didn't set up a component, so this is just a regular view, but it should work about the same.
<div class="fb-share-button" {{bind-attr data-href=link}} data-type="button"></div>
export default Ember.View.extend({
setupSocialNetworks: function() {
Ember.run.scheduleOnce('afterRender', this, function() {
FB.XFBML.parse();
});
}.on('didInsertElement')
});
UPDATE: Alternate solutions
I think which solution you use really depends on your needs. I'm focusing on a faster time-to-first-render, so I've changed my project to not deferReadiness for the Facebook SDK.
I've been playing with two solutions, again I think it totally depends on your needs.
Load the Facebook SDK in an initializer, but set a global promise for access.
This starts the loading on boot, but allows your application to continue booting without having to wait for Facebook. All calls to the Facebook API need to be accessed through the promise.
I'll share the details of this one if requested, but for now I'll just focus on the next solution:
Load the Facebook SDK only on demand in a service.
As before, all access to the Facebook API will need to go through a promise, but this time it is nicely encapsulated in a service and is only loaded on demand:
// app/services/facebook.js
import Ember from 'ember';
import ENV from "juniper/config/environment";
var computed = Ember.computed;
var RSVP = Ember.RSVP;
var _facebookSDKDeferrable = Ember.RSVP.defer();
var fbAsyncInit = function() {
_initFacebook(window.FB);
_facebookSDKDeferrable.resolve(window.FB);
};
window.fbAsyncInit = fbAsyncInit;
export default Ember.Service.extend({
// Resolves when the Facebook SDK is ready.
//
// Usage:
//
// facebook: Ember.inject.service(),
// foo: function() {
// this.get('facebook.SDK').then(function(FB) {
// // Facebook SDK is ready and FB is a reference to the SDK
// });
// }
SDK: computed.alias('FB'),
FB: computed(function() {
_loadFacebookSDK();
return _facebookSDKDeferrable.promise;
})
// I've also put promisified helpers for Facebook SDK
// methods here.
});
function _loadFacebookSDK() {
(function(d, s, id){
var js, fjs = d.getElementsByTagName(s)[0];
if (d.getElementById(id)) {return;}
js = d.createElement(s); js.id = id;
js.src = "//connect.facebook.net/en_US/sdk.js";
fjs.parentNode.insertBefore(js, fjs);
}(document, 'script', 'facebook-jssdk'));
}
function _initFacebook(FB) {
FB.init({
appId: ENV.FB_APP_ID,
status: true,
cookie: true,
xfbml: false,
version: ENV.GRAPH_API_VERSION
});
}
Note also that I've set xfbml to false in FB.init. If your share links have been rendered before the SDK gets loaded, FB.init({ xfbml: true }) will "parse" them, and your FB.XFBML.parse() call will do it again. By setting xfbml to false, you ensure that FB.XFBML.parse() will only get called once.
Related
Hello I am using gem 'facebook-messenger' and follow all the steps same given in gem. My requirement is to add send to messenger button in my app I had included following code
<script>
window.fbAsyncInit = function() {
FB.init({
appId : 'APP_ID',
xfbml : true,
version : 'v2.6'
});
};
(function(d, s, id){
var js, fjs = d.getElementsByTagName(s)[0];
if (d.getElementById(id)) {return;}
js = d.createElement(s); js.id = id;
js.src = "//connect.facebook.net/en_US/sdk.js";
fjs.parentNode.insertBefore(js, fjs);
}(document, 'script', 'facebook-jssdk')
);
</script>
<div class="fb-messenger-checkbox"
origin=PAGE_URL
page_id=PAGE_ID
messenger_app_id=APP_ID
user_ref= <%= #order.number%>
prechecked="true"
allow_login="true"
size="large"></div>
It gives me error
Refused to display
'https://www.facebook.com/v2.6/plugins/messenger_checkbox.php?allow_login=true&app_id='APP_ID'&channel=https%3A%2F%2Fstaticxx.facebook.com%2Fconnect%2Fxd_arbiter%2Fr%2FXBwzv5Yrm_1.js%3Fversion%3D42%23cb%3Df379bc53d47b1%26domain%3D3aa1d074.ngrok.io%26origin%3Dhttps%253A%252F%252F3aa1d074.ngrok.io%252Ff2ea378cba3d818%26relation%3Dparent.parent&container_width=1273&locale=en_US&messenger_app_id=219206268600034&origin=https%3A%2F%2Fwww.facebook.com%2Fmy-test-716683128523791%2F&page_id='PAGE_ID'&prechecked=true&sdk=joey&size=large&user_ref=S906106687' in a frame because an ancestor violates the following Content Security
Policy directive: "frame-ancestors https://www.facebook.com".
Please guide me how to resolve this error.
Adding this answer in case anyone else comes across this in the future.
You're getting a CSP error and need to whitelist your domain. Covered here
I'm trying to allow a user to sign in via facebook, and not be redirected off of the current page. I'm currently using Rails with omniauth-facebook and devise for authentication. I'm assuming the best way to do this is via ajax once I've received authentication from facebook via the Javascript api. However, I'm not sure what I need to pass to the callback url for omniauth to verify the authentication. Here's what I currently have (I'm trying to avoid using jquery for the time being)
:javascript
window.fbAsyncInit = function() {
// init the FB JS SDK
FB.init({
appId : 'app-id', // App ID from the app dashboard
channelUrl : '//localhost:3000/channel.html', // Channel file for x-domain comms
status : true, // Check Facebook Login status
cookie : true,
xfbml : true // Look for social plugins on the page
});
// Additional initialization code such as adding Event Listeners goes here
document.getElementById('facebook-login').onclick = function(event) {
FB.login(function(response) {
if (response.authResponse) {
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function() {
console.log(xhr);
if (xhr.readyState == 4) {
console.log(xhr.responseText);
}
}
xhr.open('GET', 'http://localhost:3000/users/auth/facebook/callback', true);
xhr.send(null);
} else {
console.log("Something when horrible wrong");
}
}, {scope: ''});
}
};
// Load the SDK asynchronously
(function(d, s, id){
var js, fjs = d.getElementsByTagName(s)[0];
if (d.getElementById(id)) {return;}
js = d.createElement(s); js.id = id;
js.src = "//connect.facebook.net/en_US/all.js";
fjs.parentNode.insertBefore(js, fjs);
}(document, 'script', 'facebook-jssdk'));
The thing I'm most unsure about is if I'm calling my own endpoint (users/auth/facebook/callback) correctly, or if I need to pass anything to it. Any help would be greatly appreciated.
For me, the problem was making a GET request instead of a POST request.
Changed to POST and the problem is gone.
I would have left it as a comment, as I'm not leaving further information to justify this solution, but i'm new at this (StackOverflow); I don't have enough points to be able to comment.
Anyway, Hope it helps!
I'm having a hard time trying to load fast the javascript Facebook SDK into my rails 4 application. Is there a good way to make it work correctly with turbolinks?
if i add this code on my JS application assets.
It's not working properly due to turbolinks:
<div id="fb-root"></div>
<script>
window.fbAsyncInit = function() {
// init the FB JS SDK
FB.init({
appId : 'YOUR_APP_ID', // App ID from the app dashboard
channelUrl : '//WWW.YOUR_DOMAIN.COM/channel.html', // Channel file for x-domain comms
status : true, // Check Facebook Login status
xfbml : true // Look for social plugins on the page
});
// Additional initialization code such as adding Event Listeners goes here
};
// Load the SDK asynchronously
(function(d, s, id){
var js, fjs = d.getElementsByTagName(s)[0];
if (d.getElementById(id)) {return;}
js = d.createElement(s); js.id = id;
js.src = "//connect.facebook.net/en_US/all.js";
fjs.parentNode.insertBefore(js, fjs);
}(document, 'script', 'facebook-jssdk'));
</script>
thanks
You will find the proper solution for integrating the Facebook SDK with Turbolinks here :
http://reed.github.io/turbolinks-compatibility/facebook.html
I was having a problem with the Like Box not loading when I navigate between pages using turbo links. My solution was to create a social.js.coffee in my assets/javascripts folder. In this file it simply has
$(document).on 'page:change', ->
FB.init({ status: true, cookie: true, xfbml: true });
I know its a lot to put in it's own file ;-), but the idea was that I know google analytics, twitter, and others will have the same conflicts and this will be a nice place to house those solutions as well.
Quoting this answer
If you prefer to use native Turbolinks 5 events, you can add this script to your Rails assets:
// FacebookSDK
// https://developers.facebook.com/docs/plugins/page-plugin/
(function(d, s, id) {
var js, fjs = d.getElementsByTagName(s)[0];
if (d.getElementById(id)) return;
js = d.createElement(s);
js.id = id;
js.src = "//connect.facebook.net/ja_JP/sdk.js#xfbml=1&version=v2.8";
fjs.parentNode.insertBefore(js, fjs);
}(document, 'script', 'facebook-jssdk')); // Replace 'facebook-jssdk' with your page id.
// Compatibility with Turbolinks 5
(function($) {
var fbRoot;
function saveFacebookRoot() {
if ($('#fb-root').length) {
fbRoot = $('#fb-root').detach();
}
};
function restoreFacebookRoot() {
if (fbRoot != null) {
if ($('#fb-root').length) {
$('#fb-root').replaceWith(fbRoot);
} else {
$('body').append(fbRoot);
}
}
if (typeof FB !== "undefined" && FB !== null) { // Instance of FacebookSDK
FB.XFBML.parse();
}
};
document.addEventListener('turbolinks:request-start', saveFacebookRoot)
document.addEventListener('turbolinks:load', restoreFacebookRoot)
}(jQuery));
From: https://gist.github.com/6temes/52648dc6b3adbbf05da3942794b97a00
If you're putting your Facebook JS code at the end of your view, use flush in your controller action to ensure it's fully reloaded properly with Turbolinks:
def show
# ...
render :show, flush: true
end
Worked like a charm.
I followed the solution suggested by #gallonallen with some small modification. just created a file called turbo.js with following content:
$(document).on('turbolinks:load', function() {
FB.init({ status: true, cookie: true, xfbml: true });
});
And added //= require turbo in application.js before //= require query and it started working for me. I am using rails 4.2.6 and ruby 2.3.1 with turbo links 5. For me, prior to fix, turbo links was working on my local but not when deployed to staging or prod.
I am trying to use the Facebook Javascript SDK, and right now I am just pasting this code into my erb template that has the like button:
<div id="fb-root"></div>
<script>(function(d, s, id) {
var js, fjs = d.getElementsByTagName(s)[0];
if (d.getElementById(id)) return;
js = d.createElement(s); js.id = id;
js.src = "//connect.facebook.net/es_LA/all.js#xfbml=1";
fjs.parentNode.insertBefore(js, fjs);
}(document, 'script', 'facebook-jssdk'));</script>
However, in the Facebook developer docs, it is said that something like this is prefered direct right after the body open tag:
<div id="fb-root"></div>
<script>
window.fbAsyncInit = function() {
// init the FB JS SDK
FB.init({
appId : 'YOUR_APP_ID', // App ID from the app dashboard
channelUrl : '//WWW.YOUR_DOMAIN.COM/channel.html', // Channel file for x-domain comms
status : true, // Check Facebook Login status
xfbml : true // Look for social plugins on the page
});
// Additional initialization code such as adding Event Listeners goes here
};
// Load the SDK asynchronously
(function(d, s, id){
var js, fjs = d.getElementsByTagName(s)[0];
if (d.getElementById(id)) {return;}
js = d.createElement(s); js.id = id;
js.src = "//connect.facebook.net/en_US/all.js";
fjs.parentNode.insertBefore(js, fjs);
}(document, 'script', 'facebook-jssdk'));
</script>
So, my question is:
-> Where should I place this code? In my layout or in a Javascript file that using jQuery does all this logic?
put the code into bottom of your main templates eg application.html.erb or base.html.erb[if your have base controller that inherited from application controlller]
I have a Facebook connect site and am having issues with the Facebook login part on mobile browsers (ie: iPhone Safari, etc). Whenever I click the login button, it opens a new tab in Safari which takes me to a URL like this: https://s-static.ak.fbcdn.net/connect/xd_proxy.php... followed by a bunch of extra hashes.
The problem is, this page doesn't auto close and go back to my app. I have to manually close it and then go back. The login process works fine and the other page has already refreshed and loaded, but I can't figure out why it's not auto closing the XD Proxy page.
I've followed all the tutorials on Facebook for setting up the javascript code. I'm using FB.login() to actually login. It works flawlessly on the desktop. I'm wondering if this is a bug in Facebook's javascript or not, but I can't seem to figure it out. Any ideas would be very helpful.
Here is the code I'm using, for reference:
<script>
window.fbAsyncInit = function() {
FB.init({
appId: "APP_ID",
status: true,
cookie: true,
oauth: true,
xfbml: true
});
};
(function(d, s, id) {
var js, fjs = d.getElementsByTagName(s)[0];
if (d.getElementById(id)) return;
js = d.createElement(s); js.id = id;
js.src = "//connect.facebook.net/en_US/all.js";
fjs.parentNode.insertBefore(js, fjs);
}(document, 'script', 'facebook-jssdk'));
</script>
<button onclick="loginToFB();" type="button" class="fb-login-button">Login with Facebook</button>
<script type="text/javascript">
function loginToFB(next) {
FB.login(function(response) {
if (response.authResponse) {
window.location = '/facebook';
}
}, {scope: 'SCOPE_STRING'});
}
</script>
Thanks!
Try first calling the getLoginStatus() function and don't do FB.login() if the user is already logged in.