Why will my twitter widget not render if i change the view in angularjs?
Asked Answered
A

6

14

Hi and thanks for reading.

I have a angular app im making and ive stumbled on a problem. set up as so

index.html-

<html ng-app="myApp">
...
<div ng-view></div>
<div ng-include="'footer.html'"></div>
...
</html>

I wont bother putting my routes its pretty simple /home is shows the /home/index.html and so on...

/home/index.html (default view when you come to the site)

<div class="responsive-block1">
<div class="tweet-me">
    <h1> tweet me </h1>
</div>

<div class="twitter-box">
    <twitter-timeline></twitter-timeline>
</div>

twitter timeline directive

directives.directive("twitterTimeline", function() {
return { 
     restrict: 'E',
     template: '<a class="twitter-timeline" href="https://twitter.com/NAME" data-widget-id="XXXXXXXXXXXXXX">Tweets by @NAME</a>',
     link: function(scope, element, attrs) {

    function run(){
        (!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"));
        console.log('run script');
    };

    run();


     }
   };
});

So I have just created a basic twitter directive using the tag from twitter. But when I change the view example to /blog then go back to /home the twitter widget no longer renders at all.

Im also using an $anchorScroll and if i jump to anyway on the page with this the widget also disappears. Any info would be great thanks.

Addi answered 6/3, 2014 at 17:13 Comment(2)
Hi, did my solution below work for you? Would appreciate your acceptance if so.Oryx
This answers my question here: #22183054.Eliathas
E
11

See this post: https://dev.twitter.com/discussions/890

I think that you may be able to get the widget to re-render by calling twttr.widgets.load().

If you find that this does not work, you will need to wrap this code into $timeout in your controller:

    controller('MyCtrl1', ['$scope', '$timeout', function ($scope, $timeout) {
            $timeout = twttr.widgets.load();
    }])
Eliathas answered 13/3, 2014 at 21:11 Comment(2)
not sure why use $timeout = twttr.widgets.load(); instead of $timeout(function () { twttr.widgets.load(); }, 500); ? anyway, second approach works like charm!Centistere
I think, instead of a timeout, you could check if window.twttr exists, if yes is because it has been render before. Then, just rerender with twttr.widgets.load(), worked for me.Walden
E
4

To build on Sir l33tname's answer:

In services declaration:

    angular.module('app.services', []).
      service('tweetWidgets', function() {

        this.loadAllWidgets = function() {

            /* widgets loader code you get when 
             * declaring you widget with Twitter 
             * this code is the same for all widgets 
             * so calling it once will reference whatever
             * widgets are active in the current ng-view */

            !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");
         };

         this.destroyAllWidgets = function() {
            var $ = function (id) { return document.getElementById(id); };
            var twitter = $('twitter-wjs');
            if (twitter != null)
                twitter.remove();
         };  
      });

Then in controller declarations:

    angular.module('app.controllers', []).
      controller('view_1_Controller', tweetWidgets) {

         // load them all
         tweetWidgets.loadAllWidgets();

     }).

       controller('view_2_Controller', tweetWidgets) {

          // now destroy them :>
          tweetWidgets.destroyAllWidgets();

     });

Now whenever you leave view #1 to go to view #2, your controller for view #2 will remove the widgets associated with view #1 and when you return to view #1 the widgets will be re-instatiated.

Ethnography answered 14/5, 2014 at 17:16 Comment(0)
O
2

The problem is because when Angular switches views the script tag that was originally inserted is not removed from the document. I fixed this on my own website by removing the Twitter script element whenever my Twitter timeline directive is not in the view. See the code below with comments.

 function (scope, el, attrs) {
     el.bind('$destroy', function() {
         var twitterScriptEl = angular.element('#twitter-wjs');
         twitterScriptEl.remove();
     });

     // function provided by Twitter that's been formatted for easier reading
     function (d, s, id) {
         var js, fjs = d.getElementsByTagName(s)[0], p = /^http:/.test(d.location) ? 'http' : 'https';

         // If the Twitter script element is already on the document this will not get called. On a regular webpage that gets reloaded this isn't a problem. Angular views are loaded dynamically.
         if (!d.getElementById(id)) {
             js = d.createElement(s);
             js.id = id;
             js.src = p + "://platform.twitter.com/widgets.js";
             js.parentNode.insertBefore(js, fjs);
         }
     }(document, "script", "twitter-wjs");        
 }
Oryx answered 6/3, 2014 at 18:10 Comment(1)
I used this code as a template and noticed it was not working. The line js.parentNode.insertBefore(js, fjs); should be fjs.parentNode.insertBefore(js, fjs); Otherwise, the code works perfect!Ataxia
N
2

Basically it's what Loc Nguyen say.

So every time you recreate it you must remove it first.

var $ = function (id) { return document.getElementById(id); };
function loadTwitter() {!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");}

var twitter = $('twitter-wjs');
twitter.remove();
loadTwitter(); 
Naivete answered 11/5, 2014 at 11:7 Comment(1)
Appreciate this answer as it really simplifies the code to the essentials.Ethnography
R
2

Answer by @b1r3k works without problems :

put this in your controller:

$timeout(function () { twttr.widgets.load(); }, 500); 
Reckless answered 11/11, 2015 at 10:1 Comment(0)
E
0

For those trying to load twttr.widgets.load() inside their controller, you will most likely get an error that twttr is not defined AT SOME POINT in your UX, because the async call to load the twitter script may not be completed by the time you controller instantiates and references twttr.

So I created this TwitterService

.factory('TwitterService', ['$timeout', function ($timeout) {
    return {
        load: function () {
        if (typeof twttr === 'undefined') {
            (function() {
                !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');
            })();
        } else {
            $timeout = twttr.widgets.load();
        };
        }
    }
}])

and then call TwitterService.load() inside the controllers that require your widgets. This worked pretty well. It basically just checks if the twttw object exists and if it does, just reload the script... otherwise just reload the script.

Not sure if this is the best implementation, but it seems like all other solutions have edge cases where it will throw an error. I have yet to find one with this alternative.

Encrinite answered 21/1, 2015 at 18:16 Comment(2)
Doesn't matter whether the call to Twitter is completed. All that would happen is it takes some extra seconds for Twitter to load into your DOM, it's not going to throw an error unless you're doing something wrong in your controller. It seems all your script is doing is loading Twitter again over top of itself - this is sure to have unwanted behaviour.Ethnography
Why are you repeating the other error in the previous post with $timeout = twttr.widgets.load(); ? Please update the post to use $timeout correctly or you risk a downvote :(Uropod

© 2022 - 2024 — McMap. All rights reserved.