Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angular JS can't render Social Media widgets on page/route change

I'm creating a Angular App which makes an ajax call to a Drupal CMS system to get some content. Some of this content includes widgets from Facebook, Twitter, Instagram and Youtube.

The ajax call is done with a postsController and postsService.

I'm having trouble getting these social media widgets to render correctly. I've got them to render correctly when some one goes directly to the page with the widgets but if they change the route (page view) the widgets won't load. I believe this is because the script the API uses only runs on the page load event.

At first angular didn't render the widgets at all but I did two things to get it working.

  1. I put the CDN link to the API javascript in the head for wach API e.g. /platform.twitter.com/widgets.js
  2. I made angular re-compile the data I fetched from Drupal once it changed with a custom directive I named compileAjax

What can I do to get these to render when the user changes view/route?

Below is a screenshot and sample of the code in a html doc in which the issue is replicated in a single html doc. The widgets render correctly on page load but not when you click one of the Go to Page links.

enter image description here

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>

    <link rel="stylesheet" type="text/css" href="//cdn.jsdelivr.net/jquery.slick/1.6.0/slick.css" />
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.9/angular.min.js"></script>
    <script src="https://code.angularjs.org/1.5.9/angular-sanitize.min.js"></script>
    <script src="https://code.angularjs.org/1.5.9/angular-route.min.js"></script>
    <script type="text/javascript" src="//cdn.jsdelivr.net/jquery.slick/1.6.0/slick.min.js"></script>

    <script>
        var app = angular.module('app', ['ngSanitize','ngRoute']);

        app.config(function($routeProvider) {
            $routeProvider.when('/page1', {
                controller: 'postsController as postsCtrl',
                templateUrl: 'page1.htm'
            }).when('/page2', {
                controller: 'postsController as postsCtrl',
                templateUrl: 'page2.htm'
            })
            .otherwise({ redirectTo: '/page1' });
        });

        app.controller('postsController', ['postsService',function(postsService) {
            var postsCtrl = this;
            postsCtrl.test = 'this is an expression from the controller'
            var promise = postsService.getPost(1);

            promise.then(function(data) {
                postsCtrl.data = data.data;
                // I can't access the API on SO so am replicating what I would get from it here.
                postsCtrl.twitter = "<div data-oembed-url=\"https:\/\/twitter.com\/NatGeo\/status\/811610711671656448\">\n<div style=\"max-width:320px;margin:auto;\">\n<blockquote align=\"center\" class=\"twitter-tweet\">\n<p dir=\"ltr\" lang=\"en\" xml:lang=\"en\">Polar bears are just one of the animals that will benefit from President Obamas\u00a0recent ban on oil drilling <a href=\"https:\/\/t.co\/MX4ZnX7TNw\">https:\/\/t.co\/MX4ZnX7TNw<\/a><\/p>\n\u2014 National Geographic (@NatGeo) <a href=\"https:\/\/twitter.com\/NatGeo\/status\/811610711671656448\">December 21, 2016<\/a><\/blockquote>\n<script async=\"\" charset=\"utf-8\" src=\"\/\/platform.twitter.com\/widgets.js\"><\/script><\/div>\n<\/div>";
                postsCtrl.facebook = "<div data-oembed-url=\"https:\/\/www.facebook.com\/natgeo\/posts\/10154212815083951\">\n<div id=\"fb-root\">\u00a0<\/div>\n<script>\n<!--\/\/--><![CDATA[\/\/ ><!--\n(function(d, s, id) {\n  var js, fjs = d.getElementsByTagName(s)[0];\n  if (d.getElementById(id)) return;\n  js = d.createElement(s); js.id = id;\n  js.src = \"\/\/connect.facebook.net\/en_US\/sdk.js#xfbml=1&version=v2.3\";\n  fjs.parentNode.insertBefore(js, fjs);\n}(document, 'script', 'facebook-jssdk'));\n\/\/--><!]]>\n<\/script><div class=\"fb-post\" data-href=\"https:\/\/www.facebook.com\/natgeo\/posts\/10154212815083951\" data-width=\"550\">\n<blockquote cite=\"https:\/\/www.facebook.com\/natgeo\/posts\/10154212815083951\" class=\"fb-xfbml-parse-ignore\">\n<p>Inhumane and unsafe, snake wine is often made by drowning a live snake in alcohol. Before you purchase a gift abroad, here are a few things to know.<\/p>\nPosted by <a href=\"https:\/\/www.facebook.com\/natgeo\/\">National Geographic<\/a> on\u00a0<a href=\"https:\/\/www.facebook.com\/natgeo\/posts\/10154212815083951\">Wednesday, December 21, 2016<\/a><\/blockquote>\n<\/div>\n<\/div>"
                postsCtrl.instagram = "<div data-oembed-url=\"https:\/\/www.instagram.com\/p\/BOTfVSFDgn-\/?taken-by=natgeo&amp;hl=en\">\n<div style=\"max-width:320px;margin:auto;\"><!-- You're using demo endpoint of Iframely API commercially. Max-width is limited to 320px. Please get your own API key at https:\/\/iframely.com. -->\n<blockquote class=\"instagram-media\" data-instgrm-version=\"7\" style=\" background:#FFF; border:0; border-radius:3px; box-shadow:0 0 1px 0 rgba(0,0,0,0.5),0 1px 10px 0 rgba(0,0,0,0.15); margin: 1px; max-width:658px; padding:0; width:99.375%; width:-webkit-calc(100% - 2px); width:calc(100% - 2px);\">\n<div style=\"padding:8px;\">\n<div style=\" background:#F8F8F8; line-height:0; margin-top:40px; padding:50.0% 0; text-align:center; width:100%;\">\n<div style=\" background:url(data:image\/png;base64,iVBORw0KGgoAAAANSUhEUgAAACwAAAAsCAMAAAApWqozAAAABGdBTUEAALGPC\/xhBQAAAAFzUkdCAK7OHOkAAAAMUExURczMzPf399fX1+bm5mzY9AMAAADiSURBVDjLvZXbEsMgCES5\/P8\/t9FuRVCRmU73JWlzosgSIIZURCjo\/ad+EQJJB4Hv8BFt+IDpQoCx1wjOSBFhh2XssxEIYn3ulI\/6MNReE07UIWJEv8UEOWDS88LY97kqyTliJKKtuYBbruAyVh5wOHiXmpi5we58Ek028czwyuQdLKPG1Bkb4NnM+VeAnfHqn1k4+GPT6uGQcvu2h2OVuIf\/gWUFyy8OWEpdyZSa3aVCqpVoVvzZZ2VTnn2wU8qzVjDDetO90GSy9mVLqtgYSy231MxrY6I2gGqjrTY0L8fxCxfCBbhWrsYYAAAAAElFTkSuQmCC); display:block; height:44px; margin:0 auto -44px; position:relative; top:-22px; width:44px;\">\u00a0<\/div>\n<\/div>\n\n<p style=\" color:#c9c8cd; font-family:Arial,sans-serif; font-size:14px; line-height:17px; margin-bottom:0; margin-top:8px; overflow:hidden; padding:8px 0 7px; text-align:center; text-overflow:ellipsis; white-space:nowrap;\"><a href=\"https:\/\/www.instagram.com\/p\/BOTfVSFDgn-\/\" style=\" color:#c9c8cd; font-family:Arial,sans-serif; font-size:14px; font-style:normal; font-weight:normal; line-height:17px; text-decoration:none;\" target=\"_blank\">A photo posted by National Geographic (@natgeo)<\/a> on <time datetime=\"2016-12-22T03:35:07+00:00\" style=\" font-family:Arial,sans-serif; font-size:14px; line-height:17px;\">Dec 21, 2016 at 7:35pm PST<\/time><\/p>\n<\/div>\n<\/blockquote>\n<script async=\"\" defer=\"defer\" src=\"\/\/platform.instagram.com\/en_US\/embeds.js\"><\/script><\/div>\n<\/div>\n\n<p>\u00a0<\/p>";
                postsCtrl.youtube = "<div data-oembed-url=\"https:\/\/youtu.be\/G51LtqmZKto\">\n<div style=\"max-width:320px;margin:auto;\"><!-- You're using demo endpoint of Iframely API commercially. Max-width is limited to 320px. Please get your own API key at https:\/\/iframely.com. -->\n<div>\n<div style=\"left: 0px; width: 100%; height: 0px; position: relative; padding-bottom: 56.2493%;\"><iframe allowfullscreen=\"\" frameborder=\"0\" src=\"https:\/\/www.youtube.com\/embed\/G51LtqmZKto?wmode=transparent&amp;rel=0&amp;autohide=1&amp;showinfo=0&amp;enablejsapi=1\" style=\"top: 0px; left: 0px; width: 100%; height: 100%; position: absolute;\" tabindex=\"-1\"><\/iframe><\/div>\n<\/div>\n<\/div>\n<\/div>\n\n<p>\u00a0<\/p>\n<\/div>\n      \n<\/div>"
                postsCtrl.slider = "<div class=\"slick-slider\">\n \n <div>Service Slide 1<\/div>\n \n <div>Service Slide 2<\/div>\n \n <div>Service Slide 3<\/div>\n \n <\/div>";
            });
}]);

app.service("postsService", function($http, $q) {
    function getPost(postsId) {
        var deferred = $q.defer()
        var url = 'https://jsonplaceholder.typicode.com/albums/' + postsId;
        $http({
                    method: 'GET', // GET OPTIONS
                    cache: true,
                    url: url,
                    headers: {
                        'Content-Type': 'application/json;charset=UTF-8'
                    }
                }).then(function(response) {
                    //your code when success
                    deferred.resolve(response);
                }, function(response) {
                    //your code when fails
                    deferred.reject(response);
                });
                return deferred.promise;
            }
            this.getPost = getPost;
        });

app.directive('compileAjax', function($compile) {
    return {
        restrict: 'A',
        replace: true,
        link: function(scope, elem, attrs) {
            scope.$watch(attrs.compileAjax, function(html) {
                elem[0].innerHTML = html;
                $compile(elem.contents())(scope);
            });
        }
    }
});

app.directive('slickSlider', function() {
    return {
        restrict: 'C',
        link: function(scope, elem, attrs) {
            $(elem).slick({
                // settings
            });
        }
    }
});
</script>
<script src="//connect.facebook.net/en_US/sdk.js#xfbml=1&amp;version=v2.5" 
async></script>  
<script async="" defer="defer" src="//platform.instagram.com/en_US/embeds.js"></script>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>
</head>
<body>
    <body ng-app="app" ng-controller="postsController as postsCtrl">
        <script type="text/ng-template" id="page1.htm">
            <h2>You're on Page 1</h2>
            <a href="#page1">Go to page 1</a>
            <a href="#page2">Go to page 2</a>
            <div compile-ajax="postsCtrl.twitter"></div>
            <div compile-ajax="postsCtrl.facebook"></div>
            <div compile-ajax="postsCtrl.instagram"></div>
            <div compile-ajax="postsCtrl.youtube"></div>
            <div compile-ajax="postsCtrl.slider"></div>
        </script>
        <script type="text/ng-template" id="page2.htm">
            <h2>You're on Page 2</h2>
            <a href="#page1">Go to page 1</a>
            <a href="#page2">Go to page 2</a>
            <div compile-ajax="postsCtrl.twitter"></div>
            <div compile-ajax="postsCtrl.facebook"></div>
            <div compile-ajax="postsCtrl.instagram"></div>
            <div compile-ajax="postsCtrl.youtube"></div>
            <div compile-ajax="postsCtrl.slider"></div>
        </script>

        <div ng-view></div>

    </body>
</body>
</html>
like image 954
Holly Avatar asked Nov 09 '22 04:11

Holly


1 Answers

You need to manually initialize the javascript for the embedded widgets after loading them, since the widgets are being added after page load and therefore not being initialized.

For Twitter:

twttr.widgets.load();

If content is dynamically inserted into a page (such as lazy-loading content or using a pushState technique to navigate between articles) it’s necessary to request Twitter’s widgets JavaScript scan for new buttons and widgets using the twttr.widgets.load() function.

For Instagram:

instgrm.Embeds.process();

If you want to load the library separately from the HTML code, you can call the oEmbed endpoint with the omitscript parameter. This is useful for websites that want to handle the loading of the embeds.js script by themselves. To manually initialize the embed code, you can then call the JavaScript function instgrm.Embeds.process().

For Facebook:

FB.XFBML.parse(); 

You can use this function to re-render social plugins using either the XFBML syntax (e.g. <fb:like>) or the HTML5 syntax (e.g. <div class="fb-like">)

You'll have to track down the init code for the other social media embeds...

Compiling the html doesn't make any sense unless there are specific angular bindings/directives in the html that you want to pick up.

Here's a basic demo.

like image 89
Douglas Ludlow Avatar answered Nov 15 '22 10:11

Douglas Ludlow