Embere.js – Injecting Open Graph Tags For Facebook Sharing
I built a public content-oriented website as an Ember application recently. I used the ember-social gem to incorporate Twitter Tweet buttons and Facebook Share buttons.
This made building the buttons a fairly painless process:
{{facebook-share fb-layout='button' url=model.url}}
{{twitter-share url=model.url text=model.twitterShareText via='person' hashtags='great'}}
The Tweet button worked great, but the Facebook button was not working as expected – it wasn’t picking up any content. I then realized that Facebook does not allow data to be provided for the Share button, instead it uses curl or something like it to scrape the page.
At this point I looked into options – prerender.io, FastBoot, Phantom.js, etc. but they all seemed a bit complicated at the moment. I’m hoping that I may later be able to implement FastBoot when ready for the world, so I was willing to look at the simplest possible solution.
I remembered that in Luke Melia’s RailsConf Lightning Fast Deployments talk he mentioned one possible advantage of this technique is that data can be injected into the index.html as it is served. Since we’re deploying using ember-cli-deploy with Rails serving the index.html from Redis I wondered if that might solve the problem, and it did:
class SiteController < ApplicationController
def index
render text: inject_meta(get_html)
end
def get_html
app = "xxx"
redis = Redis.new
current = redis.get("#{app}:current")
redis.get(current)
end
private :get_html
def inject_meta(html)
html.gsub('</head>', "#{get_meta}</head>")
end
private :inject_meta
def get_meta
base_url = "http://www.example.com"
# event page
if request.fullpath =~ /events\/[0-9]*/
id = request.fullpath.split('events/')[1].to_i
item = Event.find(id).decorate
meta = %{<meta property="og:title" content="#{item.title}"/>}
meta = meta + %{<meta property="og:url" content="#{base_url}/events/#{item.id}"/>}
# news page
elsif request.fullpath =~ /news\/[0-9]*/
id = request.fullpath.split('news/')[1].to_i
item = NewsItem.find(id).decorate
meta = %{<meta property="og:title" content=" #{item.title}"/>}
meta = meta + %{<meta property="og:url" content="#{base_url}/news/#{item.id}"/>}
end
meta = meta + og_type + og_description(item) + og_image(item)
rescue => e
Rails.logger.info "ERROR: #{e}"
nil
end
private :get_meta
def og_type
%{<meta property="og:type" content="article" />}
end
def og_image(item)
%{<meta property="og:image" content="#{item.image_facebook}" />} if item.image_original.present?
end
def og_description(item)
%{<meta property="og:description" content="#{item.content}" />}
end
end
Facebook now curls the URL, which is handled by Rails. Rails gets the index.html file from Redis, and then injects whatever content we need it to into the index.html using gsub. We use request.fullpath to identify the section and item that Facebook is looking for, and we look that up and get the details we need to build open graph tags: image, title, description, url, etc.
Seems to be working like a charm.
In the process I wondered about Google result, but was happy to discover that Google crawls with javascript so our Ember site has been properly indexed.
I am available for Ember consulting work – get in touch to learn more.