Generate static SEO pages for AngularJS + Spring MVC website Generate static SEO pages for AngularJS + Spring MVC website angularjs angularjs

Generate static SEO pages for AngularJS + Spring MVC website


Actually it's my Angular/SEO experience.
You have to made lots of changes!!


1) Removing # from url

app.config(['$locationProvider', function ($locationProvider) {    $locationProvider.html5Mode({        enabled: true,        requireBase: false    });}]);

2) Review your MVC Routing

Till now maybe you had one HomeController for returning index.cshtml and booting up your Angular App.
After removing # from Angular routing, you have to set MapRoute for all of your routes.
Because in this situation the first time you try to visit routes like www.site.com/any_route Angular App not loaded yet so it tries to get page from MVC Routing. But after that $routeProvider do its duties.


3) Use MVC variables for meta tags

For better indexing and being friend with crawlers and bots we have to use MVC variables for initializing website meta tags.
If you set your page title by Angular bindings like <title>{{title}}</title> whenever you want to share your page through social networks you will see {{title}} because social networks can't render sites.

<title>@ViewBag.title</title><meta name="Description" content="@ViewBag.description"><meta name="Keywords" content="@ViewBag.keywords"><meta property="og:title" content="@ViewBag.title" /><meta property="og:description" content="@ViewBag.description" />

4) Replace Angular binding for meta tags

Our app is SPA, so after loading Angular we are out of MVC playground. We have to replace Angular variables with MVC variables.

angular.element('title').remove();angular.element('meta[name="Description"]').remove();angular.element('meta[name="Keywords"]').remove();angular.element('meta[property="og:title"]').remove();angular.element('meta[property="og:description"]').remove();var description = angular.element('<meta name="Description" content="{{meta.description}}">');angular.element('head').prepend(description);    var keyword = angular.element('<meta name="Keywords" content="{{meta.keywords}}">');angular.element('head').prepend(keyword);    var titleOg = angular.element('<meta property="og:title" content="{{meta.title}}" />');angular.element('head').prepend(titleOg);    var descriptionOg = angular.element('<meta property="og:description" content="{{meta.description}}" />');angular.element('head').prepend(descriptionOg);var title = angular.element('<title ng-bind="meta.title"></title>');angular.element('head').prepend(title);  $rootScope.$applyAsync(function () {    $compile(title)($rootScope);    $compile(description)($rootScope);    $compile(keyword)($rootScope);    $compile(titleOg)($rootScope);    $compile(descriptionOg)($rootScope);});

5) use JSON-lD for dynamic contents

If you are familiar with SCHEMA.org you better to use JSON-LD instead of others, because search engines bots can catch and analyse <script type="application/ld+json"></script>s that inserted dynamically after page loaded.
You have to check Schema Dictionary to find the type that is most closer to your data structure.
For example it's my company json-ld:

<script type="application/ld+json">    {        "@context" : "http://schema.org",        "@type" : "Organization",        "name" : "داده کاوان امیرکبیر",        "alternateName" : "ADM | Amirkabir Data Miners",        "description": "شرکت داده کاوان امیرکبیر | تولید کننده نرم افزارهای تحت وب، از قبیل حسابداری آنلاین 'کاج سیستم' ، سیستم مدیریت پروژه 'تسک من' و ...",        "url" : "https://adm-co.net",        "email": "info@adm-co.net",        "logo": {            "@type": "ImageObject",            "url": "http://khoonamon.com/images/ADM_Logo.png",            "caption": "لوگو داده کاوان امیرکبیر",            "width": "2480px",            "height": "1459px"        },        "telephone": "+98-21-44002963",        "address": "تهران، خیابان آیت ا... کاشانی، نبش خیابان عقیل، پلاک 380، طبقه دوم",        "contactPoint" : [{            "@type" : "ContactPoint",            "telephone" : "+98-21-44002963",            "contactType" : "customer service",            "contactOption" : "TollFree",            "areaServed" : "IR",            "availableLanguage" : "Persian"        }],        "sameAs" : [            "https://google.com/+ADMcoNet-GPlus",            "https://www.linkedin.com/company/adm-amirkabir-data-miners-?trk=biz-companies-cym",            "https://instagram.com/AmirkabirDataMiners/",            "https://www.facebook.com/AmirkabirDataMiners",            "http://www.pinterest.com/AmirkabirDM/",            "https://twitter.com/AmirkabirDM",            "https://www.youtube.com/channel/UCQxP0vZA05Pl9GlyXXQt14A/about"        ]    }</script>


Have you tried tools like SEO.js (http://getseojs.com/) and prerender.io (https://prerender.io/). Have you tried those?


I haven't tried it myself but PhantomJs would likely be the best option to be able to do this.

You'll need a dictionary of the endpoints your want to render and their corresponding static filepath names. You'd then iterate over each endpoint, rendering the given path with PhantomJS and then saving the output into the static file.

From what I gather from your question, you haven't actually used these paths on the front-end in your angular app as yet? If this is the case then I'd say that the other option is to actually render them server side via just Spring.

The issue here is that angular is not made with isomorphism (client and server side rendering) in mind. Any proper rendering you want done on the server side that hasn't been built yet, the best option is to use Spring to render it.

Another option is updating to Angular2 which is isomorphic with the help of angular universal. If Spring is not used for rendering and only serves as an API for your app this option will work well.