最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - How to create an Angular directive in the <head> section of a document? - Stack Overflow

programmeradmin4浏览0评论

I am new to angular.js. I am trying to create a directive to add some title and meta tags in the <head> section of html documents, but I am having some trouble.

My index.html document is as following:

<!DOCTYPE html>
<html ng-app="myApp">
<head>
    <meta charset="UTF-8">
    <base href="/">
    <seo-title></seo-title>
    <script src=".4.1/angular.min.js"></script>
    <script src=".4.1/angular-route.min.js"></script>
    <script src="/incl/js/myApp.js"></script>
</head>
<body >
    <div ng-view></div>
</body>
</html>

My javascript is:

var app = angular.module ('myApp', ['ngRoute']);

app.config(['$routeProvider', '$locationProvider', function($routeProvider, $locationProvider) {

    $routeProvider
        .when('/', { templateUrl: 'routes/home.html'})
        .when('/pageA', { templateUrl: 'routes/pageA.html'})
        .when('/pageB', { templateUrl: 'routes/pageB.html'})
        .otherwise({ redirectTo: '/' });

    $locationProvider.html5Mode({
        enabled: true
    });

}]);

app.directive('seoTitle', function() {
    return {
        restrict: 'E',
        template: '<title>{{seo.title}}</title>'
    };
});

When I open the inspector, the directive has been moved to the <body> and has not been replaced with the template:

How can I create directives in the header?

P.S.: A code example would be great!

I am new to angular.js. I am trying to create a directive to add some title and meta tags in the <head> section of html documents, but I am having some trouble.

My index.html document is as following:

<!DOCTYPE html>
<html ng-app="myApp">
<head>
    <meta charset="UTF-8">
    <base href="/">
    <seo-title></seo-title>
    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.1/angular.min.js"></script>
    <script src="https://code.angularjs.org/1.4.1/angular-route.min.js"></script>
    <script src="/incl/js/myApp.js"></script>
</head>
<body >
    <div ng-view></div>
</body>
</html>

My javascript is:

var app = angular.module ('myApp', ['ngRoute']);

app.config(['$routeProvider', '$locationProvider', function($routeProvider, $locationProvider) {

    $routeProvider
        .when('/', { templateUrl: 'routes/home.html'})
        .when('/pageA', { templateUrl: 'routes/pageA.html'})
        .when('/pageB', { templateUrl: 'routes/pageB.html'})
        .otherwise({ redirectTo: '/' });

    $locationProvider.html5Mode({
        enabled: true
    });

}]);

app.directive('seoTitle', function() {
    return {
        restrict: 'E',
        template: '<title>{{seo.title}}</title>'
    };
});

When I open the inspector, the directive has been moved to the <body> and has not been replaced with the template:

How can I create directives in the header?

P.S.: A code example would be great!

Share Improve this question asked Jul 7, 2015 at 19:28 Jérôme VerstryngeJérôme Verstrynge 59.6k95 gold badges295 silver badges466 bronze badges 4
  • Why would u need a directive to add meta tags? Just put them in from the start? And title can be set from a controller easily which can be assigned to the html tag and the head will have access to it. – Chrillewoodz Commented Jul 7, 2015 at 19:42
  • 1 Some page will have some meta tags and others won't. Besides, it is not only about title, it could be about setting a robots meta tag to noindex a page, etc... – Jérôme Verstrynge Commented Jul 7, 2015 at 19:48
  • 1 I'm not certain, but I think HTML spec doesn't allow for custom elements, like <seo-title> as a child of <head>. Regardless, the template doesn't replace the directive element - you could use replace: true (but that is being deprecated), or you could just use an attribute directive instead <title seo-title></title> – New Dev Commented Jul 7, 2015 at 19:51
  • 2 no need for a template or custom element , just use title with restrict:'E' for the directive – charlietfl Commented Jul 7, 2015 at 22:50
Add a comment  | 

5 Answers 5

Reset to default 5

Your directive does not need to go in the head to set the title. Just have your directive inject $window and set $window.document.title = 'your title'.

UPDATE This is how you can update meta tags.

For updating meta tags I would use a Directive like this:

mmMetaTags.$inject = ['metaTags'];
function mmMetaTags(metaTags) {

    return {
        restrict: 'A',
        link: function(scope, element) {

            metaTags.metaTags.forEach(function(tag) {
                addMetaTag(tag.name, tag.content)
            });

            metaTags.subscribe(addMetaTag);

            function addMetaTag(name, content) {

                var tag = element[0].querySelector('meta[name="' + name + '"]'); 

                if (tag) {

                    tag.setAttribute('content', content);
                } else {

                    element.append('<meta name="' + name + '" content="' + content + '">');
                }
            }
        }
    }

}

directive('mmMetaTags', mmMetaTags);

Along with a service to set the metaTags:

function MetaTags() {

    // private
    this._tags = [];

    // private
    this._subscriber;

    var self = this;
    Object.defineProperty(this, 'metaTags', { get: function() {
        return self._tags;
     }});
}

MetaTags.prototype.addMetaTag = function(name, content) {
    this._tags.push({ name: name, content: content });
    this._updateSubscriber(name, content);
}

MetaTags.prototype.subscribe = function(callback) {
    if (!this.subscriber) {
        this._subscriber = callback;
    } else {
        throw new Error('Subscriber already attached. Only one subscriber may be added as there can only be one instance of <head>');
    }
}

// private
MetaTags.prototype._updateSubscriber = function(name, content) {
    this.subscriber(name, content);    
}

service('metaTags', MetaTags);

So in your head tag you would include the attribute mm-meta-tags. Then in your controller you would inject the metaTags service and call addMetaTag to update the tags.

You answer is here: Set Page title using UI-Router, implemented in your code it could be:

<!DOCTYPE html>
<html ng-app="myApp">
<head>
    <meta charset="UTF-8">
    <base href="/">
    <title seo-title>doesn't work</title>
    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.1/angular.min.js"></script>
    <script src="https://code.angularjs.org/1.4.1/angular-route.min.js"></script>
    <script src="/incl/js/myApp.js"></script>
</head>
<body >
    <div ng-view></div>
</body>
</html>

and you js:

app.directive('seoTitle', function() {
return {
    restrict: 'a',
    template: 'works'
};

you just need to add a controller or some logic to set the title you want

First things first: I was looking in the inspector and yes, somehow the title tag appears within the body. But it seems not to affect its function.

Now to the solution: At first glance it seems that only replace: true is missing in the declaration of the seoTitle directive. Adding it solves the problem and the seo-title is replaced with title tag as planned, but Angular wraps the content in an additional span element as a new scope is created (even if the scope for seoTag is declared isolated scope: {}).

I came up with following solution:

app.directive('seoTitle', function() {

    function compile(elem, attrs, transclude) {

        return function ($scope) {
            transclude($scope, function (clone) {
                elem.empty();
                elem.append(clone[0].innerText);
            });
        };
    }

    return {
        restrict: 'E',
        replace: true,
        transclude: true,
        scope: {},
        compile: compile,
        template: '<title ng-transclude></title>',
    };

});

Usage:

<seo-title>My Title</seo-title>

As already mentioned, with replace: true you can remove the wrapping seo-title tag.

In order to remove the additionally created span element, I provide the compile function with returns the postLink function.

I can't really explain, why I need to use the transclude function within the postLink function. It seems to be a quite common problem, that Angular creates an additional span element in this case. With a little bit try and error I found that the easiest way to get rid of the span, is to emtpy() the element and append only the innerText.

You can try metang library. Beside title it supports other meta tags(description, author, og:, twitter:, etc)

    angular.directive('ngHead', function () {
    let strHtml = '';
    strHtml += '<meta charset="utf-8">';
    strHtml += '<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">';
    strHtml += '<meta http-equiv="x-ua-compatible" content="ie=edge">';
    strHtml += '<meta name="google" content="notranslate" />';
    strHtml += '<title>';
    strHtml += '    title';
    strHtml += '</title>';
    return {
        restrict: 'A',
        link: function (scope, element, attrs) {
            element.append(strHtml);
        }
    }
});

in your html <head ng-head></head> work for me.

发布评论

评论列表(0)

  1. 暂无评论