简体   繁体   中英

Issues with rendering HTML from a JSON string with AngularJS?

I am attempting to take HTML markup within a JSON string and render it as functioning HTML with AngularJS, using $sce.trustAsHtml. I'm pretty confident that the problem is an issue with my markup, as it was not entirely clear to me how to go about pulling information from my JSON rather than putting the intended HTML straight into my controller, as shown on the Angular website.

From my details.html partial:

    <div ng-bind-html="$sce.trustAsHtml(whichItem.item1.widget)"></div>

From my controller.js file:

hashControllers.controller('DetailsController', ['$scope', '$http','$routeParams', '$sce', function($scope, $http, $routeParams, $sce) {
  $http.get('js/list_data.json').success(function(data) {
    $scope.hash = data;
    $scope.$sce = $sce;
    $scope.whichItem = $routeParams.itemId;

    if ($routeParams.itemId > 0) {
      $scope.prevItem = Number($routeParams.itemId)-1;
    } else {
      $scope.prevItem = $scope.hash.length-1;
    }

    if ($routeParams.itemId < $scope.hash.length-1) {
      $scope.nextItem = Number($routeParams.itemId)+1;
    } else {
      $scope.nextItem = 0;
    }

  });
}]);

From my JSON file:

[
  {
    "tag":"#StandWithPP",
    "shortName":"StandWithPP",
    "longName":"Stand With Planned Parenthood",
    "firstUse":"January 2012",
    "numUses":"55.7",
    "origin":"On a Tuesday in January 2012, the Susan G. Komen Foundation, which annually contributed $680,000 to Planned Parenthood, announced in a largely political move that it planned to cut off that funding. By the Friday of that week, more than 100,000 people had tweeted in outrage, and Komen restored funding. The hashtag has only grown from there, taking over all major forms of social media, extending as far as dating apps such as OKCupid, which allows users to post a badge on their profile with the tag.",
    "category":"feminism",
    "widget":"<a class='twitter-timeline' data-dnt='true' href='https://twitter.com/hashtag/StandWithPP' data-widget-id='964987220951265280'>#StandWithPP Tweets</a><script>!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0],p=/^http:/.test(d.location)?'http':'https';if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src=p+'://platform.twitter.com/widgets.js';fjs.parentNode.insertBefore(js,fjs);}}(document,'script','twitter-wjs');</script>"
  }
]

I've gone through several iterations of ways to call the information from the JSON and "trust" it, but none of the syntax I've found worked.

Update: I realized that the Twitter widget has enough consistency for me to use Angular markup to fill it in for each item in the JSON file.

Here's the new HTML:

<a class="twitter-timeline" data-dnt="true" href="https://twitter.com/hashtag/{{hash[whichItem].shortName}}" data-widget-id="{{hash[whichItem].widget}}">{{hash[whichItem].tag}} Tweets</a>
            <script>!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0],p=/^http:/.test(d.location)?'http':'https';if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src=p+"://platform.twitter.com/widgets.js";fjs.parentNode.insertBefore(js,fjs);}}(document,"script","twitter-wjs");</script>

And an example of one item from the associated JSON:

[
  {
    "tag":"#StandWithPP",
    "shortName":"StandWithPP",
    "longName":"Stand With Planned Parenthood",
    "firstUse":"January 2012",
    "numUses":"55.7",
    "origin":"On a Tuesday in January 2012, the Susan G. Komen Foundation, which annually contributed $680,000 to Planned Parenthood, announced in a largely political move that it planned to cut off that funding. By the Friday of that week, more than 100,000 people had tweeted in outrage, and Komen restored funding. The hashtag has only grown from there, taking over all major forms of social media, extending as far as dating apps such as OKCupid, which allows users to post a badge on their profile with the tag.",
    "category":"feminism",
    "widget":"964987220951265280"
  }
]

The good news is, it works! The bad news is, it doesn't load when you first load the partial. You have to wait several minutes and then refresh the page to get the actual timeline widget, rather than the simple link that is the error default. (And once you return to the list view, you have to repeat the process over again.)

At first, I assumed it was a problem with bringing in the JSON data, and tried with a simple copy-paste of the original widget markup to check. But even a script that wasn't using AngularJS expression binding markup failed initially. I realized that was a problem with Angular's built-in jquite not rendering inline script tags, so I added the jQuery library to fix it.

Now, the widget without the expression binding works fine on initial loading of the partial, but I'm still having to wait several minutes and reload in order for the customized widget to work! I assume this has something to do with the nature of AngularJS and the way it calls data. Is this a lost cause? Would fixing it defeat the purpose of using AngularJS?

Amanda, you are on a slippery road ;) Your code is getting more and more complicated and requires complex solutions.

Most likely to solve your issue you would add a directive and your code will look like:

hashControllers.controller('DetailsController', function($scope) {
  $scope.whichItem =  {
    "tag":"#StandWithPP",
    "shortName":"StandWithPP",
    "longName":"Stand With Planned Parenthood",
    "firstUse":"January 2012",
    "numUses":"55.7",
    "category":"feminism",
    "widget":"<a class='twitter-timeline' data-dnt='true' href='https://twitter.com/hashtag/StandWithPP' data-widget-id='964987220951265280'>#StandWithPP Tweets</a><script>!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0],p=/^http:/.test(d.location)?'http':'https';if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src=p+'://platform.twitter.com/widgets.js';fjs.parentNode.insertBefore(js,fjs);}}(document,'script','twitter-wjs');</script>"
  }
});

hashControllers.directive('widget', ['$compile', function($compile) {
    return function(scope, elem, attrs) {
        var el = angular.element(scope.whichItem.widget); //create markup
        var compiled = $compile(el); //compile the view into a function.
        elem.append(el); // add it to view
        compiled(scope); // adding scope, you can actually pass variables to you templete here
    };
}]);

And template:

<div widget="{{whichItem.widget}}"></div>

I hope that will help, happy coding!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM