Angular JS – Twitter/Facebook widget bootstrap directive + $Timeout

Let’s consider this scenario: we are using Angular to render a blog. The blog’s post are rendered within list through the ngRepeat directive and the posts’ content is binded (via ngHtmlBind) to some model’s field which contains the specific HTML markup.

Let’s consider now that some posts are containing Twitter/Facebook posts: we know that after rendered the raw HTML markup within a post we need to call the specific API to properly render it as a widget. So we need to execute some JS after the ngHtmlBind has rendered its content. How to do that?

With a custom directive, of course!

<div class="post-content" ng-bind-html="trustAsHtml(post.html)" bootstrap-widgets>
</div>

The ngBindHtml will produce the following raw HTML markup for out Twitter/Facebook posts:

<!--Twitter raw markup-->
<div class="tw_post">
    <blockquote class="twitter-tweet">
        This is the Twit text
        <a href="https://twitter.com/<postLink>" data-datetime="2014-10-28T17:48:07 00:00">
            28/10/2014 17:48
        </a>
    </blockquote>
</div>
 
<!--Facebook raw markup-->
<div class="fb-post" data-href='https://www.facebook.com/<postLink>'>
</div>

We can use the link property of our bootstrapWidgets directive to execute some JS after the binding has been completed. To be sure that the HTML markup has been entirely bound and rendered we can use a timeout (with 0 delay) just to decouple our custom JS from the Angular workflow, in which the link property is called. Because we are working with Angular we can use the $timeout object instead of the classic setTimeout() function. The behavior is the same, but in this way we have something more integrated with the Angular framework. A consequence of this integration, for example, is that using $timeout we don’t need to use the $scope.apply() method to re-enter in the Angular workflow from our callback!

With the following code we are properly calling the Twitter/Facebook API on our raw markup. We are managing the resize too: this is very important for the Facebook posts (that aren’t responsive), but could be useful as well for the Twitter posts, because their responsive behavior still has some issues, in particular conditions, when rendered on mobile devices such as the iPhone. As we can see from the example we are constraining the Facebook posts’ width between 750px and 350px (we are constraining the maximum width for the Twitter posts to 750px as well, but just to be aligned with the Facebook posts). The reason is that this is the allowed range supported by the Facebook’s API. We can’t go outside that. To have Facebook’s posts more narrow than 350px (which is needed for mobile) we are using the transform CSS rule to scale the posts’ content when the width is smaller that 350px.

angular.module("myApp.directives").directive('bootstrapWidgets', ["$timeout", "constants", function ($timeout, constants) {
    return {
        restrict: "A",
        link: function ($scope, element, attributes) {
            $timeout(function () {
                switch ($scope.post.typeName) {
                    case constants.POST_TYPE_NAME_TWITTER:
 
                        //Twitter
                        var t1 = null;
                        function setTwPostWidth() {
                            var width = element.width();
                            if (width > 750) width = 750;
                            var twPost = element.find(".twitter-tweet-rendered");
                            if (twPost.length > 0) {
                                if (t1 == null) {
                                    t1 = $timeout(function () {
                                        twPost.css({
                                            "min-width": "0", "max-width": "750px",
                                            "width": width + "px"
                                        });
                                        t1 = null;
                                    }, 1000);
                                }
                            }
                        }
                        var t2 = null;
                        $(window).resize(function () {
                            if (t2 == null) {
                                t2 = $timeout(function () {
                                    setTwPostWidth();
                                    t2 = null;
                                }, 1000);
                            }
                        });
                        element.bind("DOMSubtreeModified", setTwPostWidth);
                        twttr.widgets.load(element[0]);
                        break;
 
                    case constants.POST_TYPE_NAME_FACEBOOK:
 
                        //Facebook
                        function setFbPostWidth() {
                            var width = element.width();
                            if (width > 750) width = 750;
                            else if (width < 350) width = 350;
                            var fbPost = element.find(".fb-post");
                            if (fbPost.attr("data-width") != width) {
                                fbPost.attr("data-width", width);
                                FB.XFBML.parse(element[0]);
                            }
                            if (element.width() < width) {
                                var sVal = element.width() / width;
                                var scale = "scale(" + sVal + "," + sVal + ")";
                                fbPost.css({
                                    "-moz-transform": scale, "-ms-transform": scale,
                                    "-o-transform": scale, "-webkit-transform": scale,
                                    "transform": scale
                                });
                            }
                            else {
                                fbPost.css({
                                    "-moz-transform": "none", "-ms-transform": "none",
                                    "-o-transform": "none", "-webkit-transform": "none",
                                    "transform": "none"
                                });
                            }
                        }
                        var t = null;
                        $(window).resize(function () {
                            if (t == null) {
                                t = $timeout(function () {
                                    setFbPostWidth();
                                    t = null;
                                }, 1000);
                            }
                        });
                        setFbPostWidth();
                        break;
                }
            }, 0);
        }
    }
}]);
Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s