2017-06-21 81 views
1

我注意到,有关这个问题的大多数问题是关于角度的替代jQuery $(document).ready函数,这是angular.element($document).ready然而我想要一个可测试/最佳实践的替代方案对此。替代angular.element(文档)。已经

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

目前我包裹控制器代码文件准备:

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

}); 

其中一期工程,但我无法测试,所以我认为这是不正确的使用。 我试着在指令中设置一个变量$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}); 
}); 

的“加载”腕表作品如预期,但自然变焦被击中负荷,多数民众赞成设置地图前。我觉得我可以在文件准备更改为$timeout功能,但是,这似乎是一种解决方法,而不是正确的解决方案,是有一个最佳实践的替代angular.element($document).ready,在相同的方式工作,但是让我来测试它成功的内容是什么?

+0

这段代码的上下文是什么?当文档准备就绪时,通常应该引导Angular应用程序,所以这个包装器在控制器内至少是无用的。 – estus

+0

@estus bing地图被调用如下:''which一旦加载,我们就可以访问上面的代码片段中的'Microsoft',这显然需要足够长的时间才能在doc中的控制器中存在 – gardni

+0

请提供一种将问题复制为小提琴/ plunk的方法,如果可能的话不暴露您的API钥匙什么的。一般来说,答案是'你应该只用angular.bootstrap准备好一次',所以这些都是关于与特定脚本交互的。如果此API上有事件可以连接以确保它已准备就绪,请考虑使用它们。 – estus

回答

4

通常,角度应用程序已在文件ready上引导。这是ng-app自动引导的默认行为,也应该在ready上执行手动引导。

这个问题是特定于当前案例(微软的Bing Maps API)。考虑到ready is suggested by Microsoft,开发人员正在自己选择更好的替代方案。

<script src="https://www.bing.com/api/maps/mapcontrol"></script> 

被synchonously加载,但它触发多个依赖性来加载这些初始文档ready被触发时尚未此刻加载。实际上,为了完成初始化,它需要ready在另一个ready之内,这正是原始代码和Microsoft示例显示的内容,并且看起来不太好。

为了避免竞争条件,应用程序引导可以推迟到所有先决条件将被加载的时刻,即window load event instead of document ready。它可以提供相当大的延迟,但它保证了应用程序依赖于脚本加载,不管如何执行其运输:

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

该API提供了控制初始化过程的另一种方法是global callback function

<script src="https://www.bing.com/api/maps/mapcontrol?callback=globalCallbackName"></script> 

回调可以被挤满了一个服务,而不是依靠<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); 
    }); 
}); 

bingMapsLoader诺言可以被链接以保证API被初始化,放入路由器解析器等。

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

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

    this.mapsInit =() => { 
    Microsoft.Maps.Map(...); 
    }; 
    ... 
+0

最后我采取了一种稍微不同的方法,但主要原则是基于您的回答,将回调打包到服务中并使用$ window.callback。完全可测试并正常工作,感谢您的努力! – gardni