18

How do you create an application/ld+json script tag with a dynamically built JSON object in AngularJS .

This is what I need the script tag to look like

<script type="application/ld+json">
{
  "@context": "http://schema.org",
  "@type": "Place",
  "geo": {
    "@type": "GeoCoordinates",
    "latitude": "40.75",
    "longitude": "73.98"
  },
  "name": "Empire State Building"
}
</script>

I have tried the following code but I cant get it to work:

HTML

<div ng-controller="TestController">
  <script type="application/ld+json">
    {{jsonId|json}}
  </script>
  {{jsonId|json}}
</div>

Controller

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

myApp.controller('TestController', ['$scope', function($scope) {
  $scope.jsonId = {
    "@context": "http://schema.org",
    "@type": "Place",
    "geo": {
      "@type": "GeoCoordinates",
      "latitude": "40.75",
      "longitude": "73.98"
    },
    "name": "Empire State Building"
  };
}]);

The expression inside the script tag does not execute. The expression outside the script tag executes correctly and displays the JSON

Please see jsfiddle

Tjaart van der Walt
  • 5,149
  • 2
  • 30
  • 50

2 Answers2

19

After a cup of coffee I remembered there is a $sce service with a trustAsHtml function.

I created a directive that accepts a json parameter for easy re-use

Please see updated and working code below:

HTML

<div ng-controller="TestController">
  <jsonld data-json="jsonId"></jsonld>
</div>

Javascript

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

myApp.controller('TestController', ['$scope', function($scope) {
  $scope.jsonId = {
    "@context": "http://schema.org",
    "@type": "Place",
    "geo": {
      "@type": "GeoCoordinates",
      "latitude": "40.75",
      "longitude": "73.98"
    },
    "name": "Empire State Building"
  };
}]).directive('jsonld', ['$filter', '$sce', function($filter, $sce) {
  return {
    restrict: 'E',
    template: function() {
      return '<script type="application/ld+json" ng-bind-html="onGetJson()"></script>';
    },
    scope: {
      json: '=json'
    },
    link: function(scope, element, attrs) {
      scope.onGetJson = function() {
        return $sce.trustAsHtml($filter('json')(scope.json));
      }
    },
    replace: true
  };
}]);

Here is a image of the script output

Please see updated jsfiddle

enter image description here

Tjaart van der Walt
  • 5,149
  • 2
  • 30
  • 50
  • 1
    How I can achieve same in the Angularjs 2. – Arvind Chavhan Jul 05 '16 at 03:31
  • @ArvindChavhan It seems like script tags are removed in angular2 if they are inside you template html, here is a question regarding the issue.http://stackoverflow.com/questions/38088996/adding-script-tags-in-angular2-component-template and here is the link to the bug/feature request https://github.com/angular/angular/issues/9695 – Tjaart van der Walt Jul 05 '16 at 09:44
  • @Tjaart van der Walt Hi, sorry my question might sound stupid but I have an app that lazy loads some jobs do I have to include all the jobs to the script for it to work? – Mohamed Mahmoud Jun 13 '19 at 10:47
  • @MohamedMahmoud If you need all the jobs to be displayed in the JSON-LD tag then yes you will need to include them all in the JSON output. I would rather show the latest jobs in the script tag or let the script tag show the same initial data that your page is showing on initial load. This way you don't abuse the tag with too much data. – Tjaart van der Walt Jun 13 '19 at 12:32
1

Tjaart van der Walt's answer did not work for me in the Google Test Tool. It did work with the real crawler. So I found another "old-school" solution which did the trick:

HTML

<script type="application/ld+json" id="json-ld-music-group"></script>

Angular

var schemaOrg = angular.toJson({
    '@context': 'http://schema.org',
    '@type': 'MusicGroup',
    ...
});

angular.element(document).ready(function() {
    var jsonLd = angular.element(document.getElementById('json-ld-music-group'))[0];
    jsonLd.innerHTML = schemaOrg;
});
Karens
  • 613
  • 5
  • 18
  • I disagrea as we use this solution in "real case scenario" as you stated. Please click on the following link https://search.google.com/structured-data/testing-tool#url=https%3A%2F%2Fjsfiddle.net%2Fvqz31net%2F1%2F this will launch googles structure data testing tool with the example jsfiddle in my answer. You will see Place is detected on the right hand side. Ignore the errors as that is jsfiddles website data and not the example jsfiddle. Are you sure you did not try and test a localhost website? – Tjaart van der Walt Jun 09 '16 at 08:56
  • You are right. It worked after all. Google had a delay in processing the results of 5 days. It just did not work in the Google Test tool for me. I edited my earlier comment. – Karens Jun 13 '16 at 10:30