在单页面应用程序中使用Disqus的最佳方法是什么?

时间:2013-04-04 22:48:11

标签: unit-testing angularjs disqus angularjs-directive single-page-application

在单页面应用程序中使用Disqus的最佳方法是什么? 我看到角度js文档已经成功实现了它。

目前我们的方法在我们的AngularJS应用程序中看起来是这样,但它似乎不稳定,难以测试,并且加载了错误的线程ID(几乎所有地方都加载了相同的线程)。

'use strict';

angular.module('studentportalenApp.components')
    .directive('disqusComponent',['$log', '$rootScope', function($log, $rootScope) {

    var _initDisqus = function _initDisqus(attrs)
    {
        if(window.DISQUS) {
            DISQUS.reset({
                reload: true,
                config: function () {
                    this.page.identifier = attrs.threadId;
                    this.disqus_container_id = 'disqus_thread';
                    this.page.url = attrs.permalinkUrl;
                }
            });
        }
        else
        {
            $log.error('window.DISQUS did not exist before directive was loaded.');
        }
    }

    //Destroy DISQUS bindings just before route change, to properly dispose of listeners and frame (postMessage nullpointer exception)
    $rootScope.$on('$routeChangeStart', function() {
            if(window.DISQUS) {
                DISQUS.reset();
            }           
    });


    var _linkFn = function link(scope, element, attrs) {
            _initDisqus(attrs);
        }


    return {
        replace: true,
        template: '<div id="disqus_thread"></div>',
        link: _linkFn
    };
}]);

3 个答案:

答案 0 :(得分:7)

我还想在我的AngularJS支持的博客上加入Disqus。我发现现有的解决方案有点笨拙,所以我写了自己的指令:

.directive('dirDisqus', function($window) {
    return {
        restrict: 'E',
        scope: {
            disqus_shortname: '@disqusShortname',
            disqus_identifier: '@disqusIdentifier',
            disqus_title: '@disqusTitle',
            disqus_url: '@disqusUrl',
            disqus_category_id: '@disqusCategoryId',
            disqus_disable_mobile: '@disqusDisableMobile',
            readyToBind: "@"
        },
        template: '<div id="disqus_thread"></div><a href="http://disqus.com" class="dsq-brlink">comments powered by <span class="logo-disqus">Disqus</span></a>',
        link: function(scope) {

            scope.$watch("readyToBind", function(isReady) {

                // If the directive has been called without the 'ready-to-bind' attribute, we
                // set the default to "true" so that Disqus will be loaded straight away.
                if ( !angular.isDefined( isReady ) ) {
                    isReady = "true";
                }
                if (scope.$eval(isReady)) {
                    // put the config variables into separate global vars so that the Disqus script can see them
                    $window.disqus_shortname = scope.disqus_shortname;
                    $window.disqus_identifier = scope.disqus_identifier;
                    $window.disqus_title = scope.disqus_title;
                    $window.disqus_url = scope.disqus_url;
                    $window.disqus_category_id = scope.disqus_category_id;
                    $window.disqus_disable_mobile = scope.disqus_disable_mobile;

                    // get the remote Disqus script and insert it into the DOM
                    var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true;
                    dsq.src = '//' + scope.disqus_shortname + '.disqus.com/embed.js';
                    (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq);
                }
            });
        }
    };
});

优点

我认为,这种方法的主要优点是它可以保持简单。在您的应用程序中注册该指令后,您无需在JavaScript中编写任何JavaScript或设置任何配置值。所有配置都通过在指令标记中传递属性来处理,如下所示:

<dir-disqus disqus-shortname="YOUR_DISQUS_SHORTNAME"
    disqus-identifier="{{ article.id }}"
    disqus-title="{{ article.title }}"
    ...>
</dir-disqus>

此外,您不需要更改index.html文件以包含Disqus .js文件 - 该指令将在准备好后动态加载它。这意味着所有额外的.js只会加载到那些实际使用Disqus指令的页面上。

您可以查看完整的来源和文档here on GitHub

买者

以上内容仅在您的网站使用HTML5Mode时才能正常工作,即您的网址中未使用“#”。我正在更新GitHub上的代码,因此该指令在不使用HTML5Mode时会起作用,但是要警告您必须将hashPrefix设置为“!”制作“hashbang”网址 - 例如www.mysite.com/#!/page/123。这是Disqus强加的限制 - 请参阅http://help.disqus.com/customer/portal/articles/472107-using-disqus-on-ajax-sites

答案 1 :(得分:4)

我对Disqus一无所知,但根据AngularJS文档source code

它们将加载函数绑定到afterPartialLoaded:

$scope.afterPartialLoaded = function() {
  var currentPageId = $location.path();
  $scope.partialTitle = $scope.currentPage.shortName;
  $window._gaq.push(['_trackPageview', currentPageId]);
  loadDisqus(currentPageId);
};

然后,他们只需将html添加到页面中:

function loadDisqus(currentPageId) {
  // http://docs.disqus.com/help/2/
  window.disqus_shortname = 'angularjs-next';
  window.disqus_identifier = currentPageId;
  window.disqus_url = 'http://docs.angularjs.org' + currentPageId;

  // http://docs.disqus.com/developers/universal/
  (function() {
    var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true;
    dsq.src = 'http://angularjs.disqus.com/embed.js';
    (document.getElementsByTagName('head')[0] ||
      document.getElementsByTagName('body')[0]).appendChild(dsq);
  })();

  angular.element(document.getElementById('disqus_thread')).html('');
}

答案 2 :(得分:4)

这就是我们解决它的方式。

我们在index.html的主体中加载DISQUS,并在有指令使用它时重置它。

指令:

'use strict';

angular.module('fooApp.directives')
    .directive('disqusComponent',['$window', '$log', function($window, $log) {

    var _initDisqus = function _initDisqus(scope)
    {
        if($window.DISQUS) {
                $window.DISQUS.reset({
                    reload: true,
                    config: function () {
                        this.page.identifier = scope.threadId;
                        this.disqus_container_id = 'disqus_thread';
                    }
                });
        }
        else
        {
            $log.error('window.DISQUS did not exist before directive was loaded.');
        }
    }

    var _linkFn = function link(scope, element, attrs) {
            element.html('<div id="disqus_thread"></div>');
            _initDisqus(scope);
        }


    return {
        replace: true,
        template: 'false',
        scope: {
            threadId: '@'
        },
        link: _linkFn
    };
}]);

这是如何测试的:

'use strict';

describe('Directive: Disqus', function() {

  var element, $window, $rootScope, $compile;

  beforeEach(function() {
    module('fooApp.directives', function($provide) {
        $provide.decorator('$window', function($delegate) {

            $delegate.DISQUS = {
                reset: jasmine.createSpy()
            };

            return $delegate;
        });
    });

    inject(function(_$rootScope_, _$compile_, _$window_) {
        $window = _$window_;
        $rootScope = _$rootScope_;
        $compile = _$compile_;
    });     

  });


  it('should place a div with id disqus_thread in DOM', function() {
    element = angular.element('<disqus-component></disqus-component>');
    element = $compile(element)($rootScope);
    expect(element.html()).toBe('<div id="disqus_thread"></div>');
  });

  it('should do a call to DISQUS.reset on load', function() {
    element = angular.element('<disqus-component thread-id="TESTTHREAD"></disqus-component>');
    element = $compile(element)($rootScope);

    var resetFn = $window.DISQUS.reset;

    expect(resetFn).toHaveBeenCalled();
  });

});