Created
April 29, 2013 13:14
-
-
Save iStefo/5481507 to your computer and use it in GitHub Desktop.
Ember-Meta module. See first comment for usage etc.!
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// include this code in your application, you don't need to call any initialization manually! | |
Ember.Application.initializer({ | |
name: "meta", | |
initialize: function(container, application) { | |
// helper function to get tag from dom | |
var _getTag = function(tagname, property, value) { | |
var tags = document.head.getElementsByTagName(tagname), | |
tag, | |
i; | |
for (i = 0; i < tags.length; i++) { | |
tag = tags[i]; | |
if (tag[property] && tag[property] === value) { | |
return tag; | |
} | |
} | |
}; | |
// hold logic and information | |
var Meta = Ember.Object.extend(Ember.Evented, { | |
application: null, | |
// string values | |
title: null, | |
description: null, | |
// dom elements | |
_ogTitle: null, | |
_description: null, | |
_ogDescription: null, | |
// defaults | |
defaults: { | |
title: null, | |
description: null | |
}, | |
summary: Ember.computed(function() { | |
return '<title>' + this.get('title') + '</title>\n' + | |
this.get('_ogTitle').outerHTML + '\n' + | |
this.get('_description').outerHTML + '\n' + | |
this.get('_ogDescription').outerHTML; | |
}).property('_ogTitle', '_ogDescription'), | |
// propagate changes to dom elements | |
titleChanged: function() { | |
document.title = this.get('title'); | |
this.get('_ogTitle').setAttribute('content', this.get('title')); | |
this.notifyPropertyChange('_ogTitle'); | |
}.observes('title'), | |
descriptionChanged: Ember.observer(function() { | |
this.get('_description').setAttribute('content', this.get('description')); | |
this.get('_ogDescription').setAttribute('content', this.get('description')); | |
this.notifyPropertyChange('_ogDescription'); | |
}, 'description'), | |
init: function() { | |
this._super(); | |
this.on('reloadDataFromRoutes', this.reloadDataFromRoutes); | |
}, | |
reloadDataFromRoutes: function() { | |
var handlers = this.get('application').Router.router.currentHandlerInfos, | |
newTitle, | |
newDescription, | |
i = handlers.length; | |
// walk through handlers until we have title and description | |
// take the first ones that are not empty | |
while (i--) { | |
var handler = handlers[i].handler; | |
if (!newTitle) { | |
newTitle = Ember.get(handler, 'metaTitle'); | |
} | |
if (!newDescription) { | |
newDescription = Ember.get(handler, 'metaDescription'); | |
} | |
} | |
// save changes or snap back to defaults | |
if (newTitle) { | |
this.set('title', newTitle); | |
} else if (this.get('defaults.title')) { | |
this.set('title', this.get('defaults.title')); | |
} | |
if (newDescription) { | |
this.set('description', newDescription); | |
} else if (this.get('defaults.description')) { | |
this.set('description', this.get('defaults.description')); | |
} | |
this.trigger('didReloadDataFromRoutes'); | |
} | |
}); | |
var meta = Meta.create({application: application}); | |
meta.set('defaults.title', document.title); | |
// setup meta object | |
// are there any tags present yet? if not, create them | |
// ogTitle | |
var _ogTitle = _getTag('meta', 'property', 'og:title'); | |
if (!_ogTitle) { | |
_ogTitle = document.createElement('meta'); | |
_ogTitle.setAttribute('property', 'og:title'); | |
document.head.appendChild(_ogTitle); | |
} | |
meta.set('_ogTitle', _ogTitle); | |
// description | |
var _description = _getTag('meta', 'name', 'description'); | |
if (!_description) { | |
_description = document.createElement('meta'); | |
_description.setAttribute('name', 'description'); | |
document.head.appendChild(_description); | |
} else { | |
meta.set('defaults.description', _description.content); | |
} | |
meta.set('_description', _description); | |
// ogDescription | |
var _ogDescription = _getTag('meta', 'property', 'og:description'); | |
if (!_ogDescription) { | |
_ogDescription = document.createElement('meta'); | |
_ogDescription.setAttribute('property', 'og:description'); | |
document.head.appendChild(_ogDescription); | |
} else { | |
meta.set('defaults.description', _ogDescription.content); | |
} | |
meta.set('_ogDescription', _ogDescription); | |
// save object to app | |
application.set('meta', meta); | |
} | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// to update the information, a little push is required. | |
// you usually would want to do it like this: | |
App.Router.reopen({ | |
didTransition: function(infos) { | |
this._super(infos); | |
Ember.run.next(function() { | |
// the meta module will now go trough the routes and look for data | |
App.meta.trigger('reloadDataFromRoutes'); | |
}); | |
} | |
}); | |
// as you can see, there is a App.meta object that handles all the action | |
// you can even set title and description directly if you want to! | |
App.meta.set('title', 'New Site Title'); | |
App.meta.set('description', 'Somethign changed, so I update my meta data.'); | |
// you can as well ask for all the tags that were set by calling | |
App.meta.get('summary'); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// you can define static metadata in your root url | |
App.BlogRoute = Ember.Route.extend({ | |
metaTitle: "My awesome blog", | |
metaDescription: "Here you can read stuff from brushing my teeths to drying my hair." | |
}); | |
// or dynamic metadata for dynamic routes | |
App.PostRoute = Ember.Route.extend({ | |
model: function(params) { | |
// ... etc | |
}, | |
metaTitle: function() { | |
return this.get('context.title'); | |
}.property('context.title'), | |
metaDescription: function() { | |
return this.get('context.abstract'); | |
}.property('context.abstract') | |
}); | |
// unfortunately, it's not possible to do metaTitleBinding: 'context.title' :( |
@iStefo Very cool. Thanks for sharing this! I tested it on our app, but I ran into an issue. In my case, the _getTag function does not work properly. It seems the tag objects returned by getElementsByTagName are different from what you expect. There is an "attributes" indirection (e.g. tag.attributes[property]). I had to replace:
if (tag[property] && tag[property] === value) { ...
with
if (tag.attributes[property] && tag.attributes[property].value.toLowerCase() === value.toLowerCase()){ ...
I haven't tested thoroughly though, but I wanted to mention this to you in case you know what's going on.
But thanks again, great stuff!
PJ
Hi guys forget the ignorance here but I'm new to Ember, trying to make this work but getting App is undefined when calling App.meta.trigger('reloadDataFromRoutes');
inside my router. Thanks in advance
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Ember-Meta
This module takes care of keeping the html meta tags updated when the user navigates through the application.
Specifically, it updates
<title>...</title>
,<meta name="description" content="...">
,<meta property="og:title" content="...">
and<meta property="og:description" content="...">
.When received the 'reloadDataFromRoutes' event, it looks up the currently active route handlers and searches them from leave to root until it finds a set
metaTitle
andmetaDescription
.It then updates the page's title, description, opengraph title and opengraph description tags in the site's head. To do so, at initialization, it detects if there are any tags present yet and re-uses them or creates new tags if needed.
This is usefull if you are keeping static (plain html) versions of your site for crawlers etc. This way, the correct metadata is right at your hand!
See examples on how to use the module below and please feel free to tell me your improvements and things I missed when coding this module!