Has anyone of you already figured out an elegant way on how to deal with meta tags with AngularJS in SPA mode?
In a base template I have some default meta tags. For each route, each controller is loading a different view with different contents. Very normal. But how can I alter the meta tags for each of these pages?
Furthermore some pages should have more meta tags, others less. For example pages with contents should have additional meta tags for social media sharing. Other sensitive pages should have the no-follow meta tags to forbid robots indexing the page.
Curious how the experienced are coping with this?
A pattern I have used comes from the ngBoilerplate project. In this pattern, there is an overall app controller which deals with a "global" scope, including meta tags.
You individual pages' controllers could emit an event when they are navigated to, $locationChangeSuccess
if you are using the default router.
The app controller listens for this event, and uses it to update the page title in the case of ngBoilerplate: https://github.com/ngbp/ngbp/blob/v0.3.2-release/src/app/app.js#L17-L21
However, there's nothing to stop you from emitting an object with whatever meta tag info you like, then having a listener for that event.
// on your individual app pages goes something like this
function SomePageController($scope) {
// the event gets fired when the page is loaded and the controller is called.
$scope.$emit('newPageLoaded', { 'title': 'Some Page', 'description': 'blah' });
}
// your overall app controller
function AppController($scope) {
$scope.metadata = {
'title': 'default title',
'description': 'default description',
};
// whenever a controller emits the newPageLoaded event, we update the app's metadata
$scope.$on('newPageLoaded', function(event, metadata) {
$scope.metadata = metadata
});
}
Then in your header you could do something like this:
<html ng-app="myApp" ng-controller="AppController">
<head>
<meta title="{{ metadata.title }}" ng-if="metadata.title" />
<meta description="{{ metadata.description}}" ng-if="metadata.description" />
<meta whatever="{{ metadata.whatever}}" ng-if="metadata.whatever" />
</head>
I've not tested this code, so there may be errors, but I think the general principle is sound.
A note re: Social Media sharing
In your question you mention the use-case of incorporating tags for social media sharing. I assume you mean something like open graph or twitter etc.
In this case it is important to know that currently the crawlers that these sites (facebook ,twitter, pinterest etc) use will not execute your JavaScript and will thus not be able to read any meta tags that you dynamically populate with Angularjs.
I have written a blog post about this issue, with a suggested solution: http://www.michaelbromley.co.uk/blog/171/enable-rich-social-sharing-in-your-angularjs-app/