angular.element(document).ready 的替代品

gar*_*dni 1 javascript timeout watch document-ready angularjs

我注意到与这个主题相关的大多数问题都是关于 jQuery$(document).ready函数在 angular 中的替代方案,angular.element($document).ready但是我想要一个可测试的/最佳实践替代方案。

我目前正在注入 Bing Maps,它需要在我的控制器中的代码执行之前加载。

目前我将控制器代码包装在准备好的文档中:

angular.element($document).ready(function() {
    self.map = new Microsoft.Maps.Map(document.getElementById('map'), {
        credentials: $scope.credentials,
        enableClickableLogo: false,
        enableSearchLogo: false,
        showDashboard: false,
        disableBirdseye: true,
        allowInfoboxOverflow: true,
        liteMode: true,
        minZoom: 2
    });

    $scope.$watch('zoom', function (zoom) {
        self.map.setView({animate: true, zoom: zoom});
    });

    if ($scope.onMapReady) {
        $scope.onMapReady({ map: self.map });
    }

});
Run Code Online (Sandbox Code Playgroud)

哪个有效,但我无法测试它,所以我认为这是不正确的用法。我尝试在指令中设置一个变量,$scope.loaded = true;正如我读到的那样,如果指令链接函数被命中,则必须加载 DOM。然后我尝试用以下内容替换准备好的文档:

$scope.$watch('loaded', function () {
    self.map = new Microsoft.Maps.Map(document.getElementById('map'), {
       credentials: $scope.credentials,
       enableClickableLogo: false,
       enableSearchLogo: false,
       showDashboard: false,
       disableBirdseye: true,
       allowInfoboxOverflow: true,
       liteMode: true,
       minZoom: 2
    });

    if ($scope.onMapReady) {
        $scope.onMapReady({ map: self.map });
    }
});

$scope.$watch('zoom', function (zoom) {
    self.map.setView({animate: true, zoom: zoom});
});
Run Code Online (Sandbox Code Playgroud)

“加载”手表按预期工作,但很自然地缩放会在加载时命中,那是在设置地图之前。我觉得我可以将准备好的文档更改为$timeout函数,但这似乎是一种解决方法而不是正确的解决方案,是否有最佳实践替代方法以angular.element($document).ready相同的方式工作但允许我成功测试其内容?

Est*_*ask 5

通常 Angular 应用程序已经在 document 上引导ready。这是使用 自动引导的默认行为ng-app,也angular.bootstrap应该执行手动引导ready

该问题特定于当前案例(Microsoft 的 Bing Maps API)。考虑到ready微软的建议,开发人员有更好的选择。

<script src="https://www.bing.com/api/maps/mapcontrol"></script>
Run Code Online (Sandbox Code Playgroud)

是同步加载的,但它会触发许多依赖项加载,这些依赖项在ready触发初始文档时尚未加载。实际上,它需要ready在另一个内部ready才能完成初始化,这正是原始代码和Microsoft示例显示的,看起来不太好。

为了避免竞争条件,应用程序引导程序可以推迟到加载所有先决条件的那一刻,即windowload事件而不是 documentready。它可能会提供相当大的延迟,但它保证应用程序依赖的脚本被加载,无论它们的传输是如何执行的:

angular.element(window).on('load', () => {
  angular.bootstrap(document.body, ['app']
});
Run Code Online (Sandbox Code Playgroud)

API 提供的用于控制初始化过程的替代方法是全局回调函数

<script src="https://www.bing.com/api/maps/mapcontrol?callback=globalCallbackName"></script>
Run Code Online (Sandbox Code Playgroud)

回调可以与服务打包在一起,而不是依赖于<script>

angular.module('bingMaps', [])
.factory('bingMapsLoader', ($q, $window, $document, $timeout) => {
  var script = document.createElement('script');
  script.src = 'https://www.bing.com/api/maps/mapcontrol?callback=bingMapsCallback';
  script.async = true;

  $document.find('body').append(script);

  return $q((resolve, reject) => {
    $window.bingMapsCallback = resolve;
    $timeout(reject, 30000);
  });
});
Run Code Online (Sandbox Code Playgroud)

bingMapsLoader 可以链接 promise 以保证 API 已初始化、放入路由器解析器等。

此外,在编译指令之前执行控制器构造函数。无论是否使用第三方 API,将所有特定于 DOM 的代码移动到 Angular 1.4 及更低版本中的前/后链接功能以及Angular 1.5 或更高版本中的控制器$onInit$postLink钩子是正确的:

app.controller('FooController', function (bingMapsLoader) {
  this.$postLink = () => {
    bingMapsLoader.then(() => this.mapsInit());
  };

  this.mapsInit = () => {
    Microsoft.Maps.Map(...);
  };
  ...
Run Code Online (Sandbox Code Playgroud)