Skip to content

Instantly share code, notes, and snippets.

@iStefo
Created April 29, 2013 13:14
Show Gist options
  • Star 10 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save iStefo/5481507 to your computer and use it in GitHub Desktop.
Save iStefo/5481507 to your computer and use it in GitHub Desktop.
Ember-Meta module. See first comment for usage etc.!
// 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);
}
});
// 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');
// 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' :(
@thingista
Copy link

@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

@marlonmantilla
Copy link

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