How can I update meta tags in AngularJS?

Solution 1:

<html ng-app="app">
    <title ng-bind="metaservice.metaTitle()">Test</title>
    <meta name="description" content="{{ metaservice.metaDescription() }}" />
    <meta name="keywords" content="{{ metaservice.metaKeywords() }}" />


<script>
    var app = angular.module('app',[]);
    app.service('MetaService', function() {
       var title = 'Web App';
       var metaDescription = '';
       var metaKeywords = '';
       return {
          set: function(newTitle, newMetaDescription, newKeywords) {
              metaKeywords = newKeywords;
              metaDescription = newMetaDescription;
              title = newTitle; 
          },
          metaTitle: function(){ return title; },
          metaDescription: function() { return metaDescription; },
          metaKeywords: function() { return metaKeywords; }
       }
    });

   app.controller('myCtrl',function($scope,$rootScope,MetaService){
      $rootScope.metaservice = MetaService;
      $rootScope.metaservice.set("Web App","desc","blah blah");
   });
</script>
 <body ng-controller="myCtrl"></body>


</html>

Solution 2:

I solved this very issue about 2 days ago, by creating a service, and using $window, plus some classic javascript.

In your html mark-up create whatever metatags you need... (feel free to leave them blank for now, or you can set them to a default value.)

<head> 
    <meta name="title"/>
    <meta name="description"/>
</head>

Then we'll need to create a service like so.

angular.module('app').service('MetadataService', ['$window', function($window){
 var self = this;
 self.setMetaTags = function (tagData){
   $window.document.getElementsByName('title')[0].content = tagData.title;
   $window.document.getElementsByName('description')[0].content = tagData.description;
 }; 
}]);

Now we'll need to use the "self.setMetaTags" service from within the controller (upon initialization)... you'll simply call the function anywhere on the controller.

angular.module('app').controller('TestController', ['MetadataService',function(MetadataService){

   MetadataService.setMetaTags({
       title: 'this',
       description: 'works'
    });

}]);

Solution 3:

If you use angular-ui-router, you can use ui-router-metatags.

Solution 4:

When you do 'view source' in most browsers, you see the document that was originally sent from the server before any JavaScript manipulation of the DOM. AngularJS apps typically do a lot of DOM manipulation but it never actually changes the original document. When you do something like right click -> inspect element in FireFox or Chrome (with dev tools) you will see the rendered DOM that includes all the JavaScript updates.

So, to answer your question there is no way to update the description meta tag with JavaScript such that the changes will be reflected in 'view source'. However, you can update the meta description tag so that any browser plugins (ex. bookmark apps, etc.) will see the updated description. To do that you would do something like this:

var metaDesc = angular.element($rootElement.find('meta[name=description]')[0]);
metaDesc.attr('content', description);

Solution 5:

I tweaked the answer found How to dynamically change header based on AngularJS partial view? to make this work on my site. You establish the meta content in the route config and then bind a function to the $routeChangeSuccess event to put the configured value into the $rootScope. As long as your meta tag is bound to the $rootScope value, everything will work out as planned.

angularApp.run(['$rootScope', function ($rootScope) {
    $rootScope.$on('$routeChangeSuccess', function (event, current, previous) {
        $rootScope.title = current.$$route.title;
        $rootScope.description = current.$$route.description;
        $rootScope.keywords = current.$$route.keywords;
    });
 }]);

angularApp.config(function ($routeProvider, $locationProvider) {
    $routeProvider
        .when('/About', {
            templateUrl: 'Features/About/About.html',
            title: 'Here\'s the Title',
            description: 'A Nice Description',
            keywords: 'Some, Keywords, Go, Here'
        });

    // use the HTML5 History API
    $locationProvider.html5Mode(true);
    $locationProvider.hashPrefix('!');
});

<head>
    <meta charset="utf-8" />
    <meta name="keywords" content="{{keywords}}"/>
    <meta name="description" content="{{description}}">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <meta name="fragment" content="!" />

    <title ng-bind="title">A Placeholder Title</title>
    <link rel="icon" href="/Images/Logo.ico">
    <base href="/" />
    @Styles.Render("~/Content/css")
</head>