40

I am developing an application using AngularJS. I want to update meta tags on route change.
How can I update meta tags in AngularJS which can be shown in "view source" on the page?

here is a HTML code -

  <!DOCTYPE html>
    <html ng-app="app">
        <head>
            <meta name="viewport" content="width=device-width, initial-scale=1.0">
            <meta name="fragment" content="!" />
            <meta name="title" content="Test App">
            <meta name="description" content="Test App">
            <meta name="keywords" content="Test,App">

            <link rel="stylesheet" href="css/jquery-ui-1.10.2.custom.min.css" />
            <link rel="stylesheet" href="css/extra.css" />
            <script src="js/libs/jquery-1.8.3.min.js"></script>
            <script src="js/libs/jquery-ui-1.10.2.custom.min.js"></script>
            <script src="js/libs/angular.min.js"></script>
            <script src="js/controller.js"></script>
            <script src="js/routes.js"></script>
        </head>
        <body>
            <div ng-controller="mainCtrl" class="main-container" loading>
                <div class="container-holder">
                    <div class="container">
                        <div ng-include src='"elements/header.html"'></div>
                        <div ng-view class="clearfix"></div>
                    </div>
                </div>

                <div ng-controller="userCtrl" id="test">
                    <div class="container" class="login-container">
                        <div id="login-logo">
                            <img src="images/logo-300.png" alt="" class="login-img"/>
                            <br />
                            <div ng-view></div>
                        </div>
                    </div>
                </div>
        </body>
    </html>
Abrar Jahin
  • 11,997
  • 20
  • 91
  • 139
Amb
  • 3,301
  • 7
  • 25
  • 33
  • Are you using `ng-view`, as in is your web application a single page application that only changes the views? – callmekatootie Apr 20 '13 at 12:09
  • you can't update `view source`...that is what server sends. Explain in more detail what you are trying to do – charlietfl Apr 20 '13 at 12:14
  • yes i am using ng-view.. – Amb Apr 20 '13 at 12:15
  • here i uploaded my code.. in my code, meta title, meta description, meta keywords are described initially.. Now as my route changes, I want to change these values.. – Amb Apr 20 '13 at 12:24
  • 5
    if this is SEO related...you need a server side solution. Won't do any good to change them in browser with javascript – charlietfl Apr 20 '13 at 12:32
  • yeah it is seo related.. then how can i manage it at server side ? – Amb Apr 20 '13 at 12:37
  • @charlietfl Are you saying that updating meta tags is not possible or not the correct way to do it? Haven't tried it, but I assume that since contents of title tag can be changed, meta should also be possible. – callmekatootie Apr 20 '13 at 17:30
  • crawler bots don't run your script . Changing title is for user experience, not SEO – charlietfl Apr 20 '13 at 17:56
  • then what can be done to make app SEO effective ? – Amb Apr 22 '13 at 09:43
  • 2
    @charlietfl modern web crawlers (google,bing) support headless execution of JS and they do crawl web apps written in client-side JS. – Anurag Oct 28 '13 at 05:44
  • this url can be helper. http://www.cssfacts.com/simple-dynamic-meta-tags-in-angularjs/ – Kamuran Sönecek Mar 29 '16 at 13:41

5 Answers5

37
<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>
Muhammad Shahzad
  • 7,778
  • 20
  • 71
  • 123
Vijayabaskaran M
  • 613
  • 5
  • 12
9

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'
    });

}]);
georgeawg
  • 46,994
  • 13
  • 63
  • 85
Robo Rick
  • 416
  • 4
  • 7
  • 3
    +1, but it only worked for me when I changed `$window.document.getElementsByName('title')[0].content = tagData.title;` to `$window.document.title=tagData.title` – Amelse Etomer Mar 01 '16 at 09:23
8

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

kuceram
  • 3,395
  • 9
  • 30
  • 52
1

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);
georgeawg
  • 46,994
  • 13
  • 63
  • 85
Jeff Whelpley
  • 429
  • 1
  • 4
  • 11
  • We are looking into this issue as well, and one potential solution we are reviewing is to add a directive to the section that updates the corresponding META tags based on an $emit() call triggered during the $routeChangeSuccess event... – Joshua Barker Jul 31 '14 at 17:43
  • 1
    @dacopenhagen Its been a while since I was on that project, but if I recall correctly we implemented a custom directive, and also ended up using a 3rd party service (forget the name) to serve up each page as pre-rendered content to search engines for SEO purposes – Joshua Barker Aug 30 '15 at 00:07
  • Since this answer was written, Google does now run javascript when crawling pages, so changes to the meta tags made by Angular (which do not appear in "view source") will get picked up by Google. – Rich Aug 17 '18 at 14:38
1

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>
georgeawg
  • 46,994
  • 13
  • 63
  • 85
Engineer_Andrew
  • 522
  • 5
  • 9
  • Why title content ng-bind , others in brackets {{}} ? – ShibinRagh Oct 07 '16 at 08:16
  • @ShibinRagh I think I was originally using ng-bind and then switched it out for double curly braces. I must have just missed that one on the title. I double-checked and using the double curlies on title works just fine, too. However, I did find that this solution doesn't work with an "otherwise" setting. Maybe someday I'll figure out another way around that. – Engineer_Andrew Oct 12 '16 at 21:14