javascriptangularjsangularjs-scopeseo

AngularJS service to store SEO metadata


I'm followed this tutorial and tried to use the service for dynamic seo metadata. https://weluse.de/blog/angularjs-seo-finally-a-piece-of-cake.html

However, run into an issue - Seems like the service is not accessable outside of controller's view. <div ui-view></div>

Here is the service i'm trying to implement:

app.service('SeoMetaService', function() {

        var metaDescription = '';
        var metaKeywords = '';
        var title = '';
        return {
            metaDescription: function() { return metaDescription; },
            metaKeywords: function() { return metaKeywords; },
            title: function() { return title; },

            reset: function() {
                metaDescription = '';
                metaKeywords = '';
                title = '';
            },
            setMetaDescription: function(newMetaDescription) {
                metaDescription = newMetaDescription;
            },
            appendMetaKeywords: function(newKeywords) {
                for(var i=0;i<newKeywords.length;i++){
                        if (metaKeywords === '') {
                            metaKeywords += newKeywords[i];
                        } else {
                            metaKeywords += ', ' + newKeywords[i];
                        }
                }
            },
            setTitle: function(newTitle) { title = newTitle; }
        };
    });

usage in controller:

app.controller('WelcomeController',['$scope', 'SeoMetaService', function($scope, SeoMetaService) {
$(document).ready(function() {
    var keywords = ["bla bla","bla bla blah"];
    SeoMetaService.setTitle("title bla bla bla");
    SeoMetaService.setMetaDescription("description bla bla bla");
    SeoMetaService.appendMetaKeywords(keywords);

    console.log(SeoMetaService.metaDescription());
    console.log(SeoMetaService.metaKeywords());
});
}]);

on the main page (one-page-app), simplified:

<html ng-app="MainPage">
<head>
    <title>{{SeoMetaService.title()}}</title>

    <meta name="description" content="{{ SeoMetaService.metaDescription() }}">
    <meta name="keywords" content="{{ SeoMetaService.metaKeywords() }}">
    <base href="/">
</head>

<body>
 <div ui-view></div>
</body>

So the problem is - i thought that angular services are singletons. But, after running the controller and setting the data, it doesn't appear in HTML.

How to fix/what to do?


Solution

  • I imagine that you defined the WelcomeController at level of View.

    So SeoMetaService is visible only in the inner html present in the <div ui-view></div>.

    You need to define the controller WelcomeController at level of <html> or <head>, because SeoMetaService is accessible only inside the tag defining the controller WelcomeController.

    Additionally you need to remove the reference to JQuery and expose the SeoMetaService in the $scope.

    The code should be something like that.

    The HTML:

    <head ng-controller='WelcomeController'>
    <title>{{SeoMetaService.title()}}</title>
    
        <meta name="description" content="{{ SeoMetaService.metaDescription() }}">
        <meta name="keywords" content="{{ SeoMetaService.metaKeywords() }}">
        <base href="/">
    </head>
    

    The controller updated:

    app.controller('WelcomeController',['$scope', 'SeoMetaService', function($scope, SeoMetaService) {
    
        var keywords = ["bla bla","bla bla blah"];
        SeoMetaService.setTitle("title bla bla bla");
        SeoMetaService.setMetaDescription("description bla bla bla");
        SeoMetaService.appendMetaKeywords(keywords);
    
        console.log(SeoMetaService.metaDescription());
        console.log(SeoMetaService.metaKeywords());
    
        // Added to the scope SeoMetaService
        $scope.SeoMetaService = SeoMetaService;
    
    
    }]);
    

    Note that you can change the content of SeoMetaService also in another controller, so for example changing a view you can update title, meta description and meta keywords reflecting the content of the new view. Here is a possible example for the controller MyViewController

    app.controller('MyViewController',['$scope', 'SeoMetaService', function($scope, SeoMetaService) {
    
        var keywords = ["xxx","yyy"];
        SeoMetaService.setTitle("title my view");
        SeoMetaService.setMetaDescription("description my view");
        SeoMetaService.appendMetaKeywords(keywords);
    
    }]);
    

    When you enter the view using the controller MyViewController the keywords, title, meta description and meta keywords will be updated.