2017-06-21 2 views
1

Ich habe festgestellt, dass die meisten Fragen in Bezug auf dieses Thema in Bezug auf eine Alternative für die Funktion 0Query $(document).ready in angular sind, die angular.element($document).ready ist, aber ich möchte eine testbare/Best Practice-Alternative dazu.Alternative zu angular.element (Dokument) .ready

Ich spreche gerade Bing Maps, die geladen werden müssen, bevor der Code in meinem Controller ausgeführt wird.

derzeit wickle ich den Controller-Code im Dokument bereit:

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

}); 

Welche funktioniert, aber ich bin nicht in der Lage, es zu testen, so dass ich davon ausgehen, diese falsche Verwendung ist. Ich habe versucht, eine Variable in der Direktive von $scope.loaded = true; zu setzen, wie ich lese, dass, wenn die Direktive Link Funktion getroffen wird das DOM geladen werden muss. Ich habe dann versucht, das Dokument zu ersetzen bereit, mit:

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

die ‚geladen‘ Uhr wie erwartet funktioniert, aber natürlich die Zoom-Hit auf Last und das ist, bevor die Karte gesetzt. Ich habe das Gefühl, ich könnte das Dokument zu einer $timeout Funktion ändern, aber das scheint ein Workaround und nicht die richtige Lösung zu sein, gibt es eine Best-Practice-Alternative zu angular.element($document).ready, die auf die gleiche Weise funktioniert, aber erlaubt es mir, den Inhalt erfolgreich zu testen?

+0

Was ist der Kontext für diesen Code? Normalerweise sollte die Angular-App mit dem Bootstrapping gestartet werden, wenn das Dokument fertig ist. Daher ist dieser Wrapper innerhalb eines Controllers zumindest nutzlos. – estus

+0

@estus bing maps heißt so: '' was einmal geladen gibt uns Zugriff auf "Microsoft" im oben genannten Snippet, das anscheinend lange genug dauert, um nur in einem Controller in der doc bereit – gardni

+0

Bitte geben Sie eine Möglichkeit, das Problem als fiddle/plunk zu replizieren, wenn es möglich ist, ohne Ihre API offen zu legen Schlüssel oder etwas. Im Allgemeinen lautet die Antwort: "Sie sollten nur einmal mit angular.bootstrap fertig werden". Es geht also nur um die Interaktion mit einem bestimmten Skript. Wenn Ereignisse in dieser API vorhanden sind, die angeschlossen werden können, um sicherzustellen, dass sie bereit sind, sollten Sie sie stattdessen verwenden. – estus

Antwort

4

Im Allgemeinen ist die angulare Anwendung bereits im Dokument ready bootstrapped. Dies ist das Standardverhalten für das automatische Bootstrapping mit ng-app, und das manuelle Bootstrapping mitsollte auch unter ready durchgeführt werden.

Die Frage bezieht sich auf den aktuellen Fall (Microsoft Bing Maps API). Wenn man bedenkt, dass ready is suggested by Microsoft, ein Entwickler ist mit besseren Alternativen für sich allein.

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

wird synchonously geladen, aber es löst eine Reihe von Abhängigkeiten, die noch zur Zeit geladen werden, nicht geladen werden, wenn Ausgangsdokument ready ausgelöst wird. Eigentlich benötigt es ready innerhalb einer anderen ready, um die Initialisierung abzuschließen, das ist genau das, was der ursprüngliche Code und Microsoft Beispiel zeigen, und es sieht nicht sehr gut aus.

Um Race-Bedingungen zu vermeiden, kann der Anwendungs-Bootstrap auf den Moment verschoben werden, in dem alle Voraussetzungen geladen werden, d.h. window load event instead of document ready. Es kann erhebliche Verzögerung liefern, aber es garantiert, dass Skripte, die die Anwendung geladen beruht auf wurden, unabhängig davon, wie deren Transport durchgeführt wird:

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

Die Alternative, dass API Initialisierungsprozess zur Steuerung bereitstellt, ist global callback function:

<script src="https://www.bing.com/api/maps/mapcontrol?callback=globalCallbackName"></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); 
    }); 
}); 

:

ein Rückruf kann, anstatt sich auf <script> mit einem Service verpackt werdenVersprechen kann verkettet werden, um zu garantieren, dass API initialisiert wurde, in Router Resolver, etc.

Zusätzlich wird Controller-Konstruktor ausgeführt, bevor Anweisung kompiliert wird.Ob Fremd APIs verwendet werden oder nicht, ist es richtig, all DOM-spezifischer Code zu bewegen, um pre/post-Link-Funktion in Angular 1.4 und unteren und $onInit oder $postLink Haken an der Steuerung in dem Winkel 1,5 oder höher:

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

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

Ich nahm am Ende einen etwas anderen Ansatz, aber das Hauptprinzip beruhte auf Ihrer Antwort, packte den Callback in einen Service und benutzte $ window.callback. vollständig testbar und funktioniert einwandfrei, vielen Dank für Ihre Mühe! – gardni

Verwandte Themen