Verzögerung der AngularJS-Routenänderung bis zum Laden des Modells, um ein Flackern zu verhindern


321

Ich frage mich, ob es für AngularJS eine Möglichkeit gibt (ähnlich wie bei Google Mail), die Anzeige einer neuen Route zu verzögern, bis jedes Modell und seine Daten mit den entsprechenden Diensten abgerufen wurden.

Wenn beispielsweise eine vorhanden wäre ProjectsController, in der alle Projekte aufgelistet sind und project_index.htmlin der diese Projekte Project.query()angezeigt werden, wird sie vor dem Anzeigen der neuen Seite vollständig abgerufen.

Bis dahin wird die alte Seite weiterhin angezeigt (z. B. wenn ich eine andere Seite durchsucht und dann beschlossen habe, diesen Projektindex anzuzeigen).

Antworten:


374

Die Auflösungseigenschaft $ routeProvider ermöglicht die Verzögerung der Routenänderung , bis Daten geladen werden.

Definieren Sie zunächst eine Route mit einem solchen resolveAttribut.

angular.module('phonecat', ['phonecatFilters', 'phonecatServices', 'phonecatDirectives']).
  config(['$routeProvider', function($routeProvider) {
    $routeProvider.
      when('/phones', {
        templateUrl: 'partials/phone-list.html', 
        controller: PhoneListCtrl, 
        resolve: PhoneListCtrl.resolve}).
      when('/phones/:phoneId', {
        templateUrl: 'partials/phone-detail.html', 
        controller: PhoneDetailCtrl, 
        resolve: PhoneDetailCtrl.resolve}).
      otherwise({redirectTo: '/phones'});
}]);

Beachten Sie, dass die resolveEigenschaft auf der Route definiert ist.

function PhoneListCtrl($scope, phones) {
  $scope.phones = phones;
  $scope.orderProp = 'age';
}

PhoneListCtrl.resolve = {
  phones: function(Phone, $q) {
    // see: https://groups.google.com/forum/?fromgroups=#!topic/angular/DGf7yyD4Oc4
    var deferred = $q.defer();
    Phone.query(function(successData) {
            deferred.resolve(successData); 
    }, function(errorData) {
            deferred.reject(); // you could optionally pass error data here
    });
    return deferred.promise;
  },
  delay: function($q, $defer) {
    var delay = $q.defer();
    $defer(delay.resolve, 1000);
    return delay.promise;
  }
}

Beachten Sie, dass die Controller-Definition ein Auflösungsobjekt enthält, das Dinge deklariert, die dem Controller-Konstruktor zur Verfügung stehen sollen. Hier phoneswird das in den Controller injiziert und in der resolveEigenschaft definiert .

Die resolve.phonesFunktion ist für die Rückgabe eines Versprechens verantwortlich. Alle Versprechen werden gesammelt und die Routenänderung wird verzögert, bis alle Versprechen gelöst sind.

Arbeitsdemo: http://mhevery.github.com/angular-phonecat/app/#/phones Quelle: https://github.com/mhevery/angular-phonecat/commit/ba33d3ec2d01b70eb5d3d531619bf90153496831


10
@MiskoHevery - Was ist, wenn sich Ihre Controller in einem Modul befinden und eher als Zeichenfolge als als Funktion definiert sind? Wie können Sie das Auflösungsattribut wie Sie einrichten?
aar0n

53
Wie wird dies in angular.controller()Typcontroller-Definitionen verwendet? In dem $routeProviderZeug dachte ich, Sie müssten String-Namen von Controllern verwenden.
Ben Lesh

6
Gibt es ein Beispiel mit angular.controller () und der neuesten Version von AngularJS?
Laurent

22
@blesh, wenn Sie verwenden angular.controller(), können Sie das Ergebnis dieser Funktion einer Variablen ( var MyCtrl = angular.controller(...)) zuweisen und dann weiter damit arbeiten ( MyCtrl.loadData = function(){..}). Schauen Sie sich das Video von Egghead an
Petrkotek

17
Ich hätte immer noch gerne eine gute Möglichkeit, ohne Ihren Controller in einem globalen Bereich zu platzieren. Ich möchte nicht überall mit Globalen herumliegen. Sie können dies mit einer Konstanten tun, aber es wäre schön, die Auflösungsfunktion auf / in der Steuerung platzieren zu können, nicht irgendwo anders.
Erik Honn

51

Hier ist ein minimales Arbeitsbeispiel, das für Angular 1.0.2 funktioniert

Vorlage:

<script type="text/ng-template" id="/editor-tpl.html">
    Editor Template {{datasets}}
</script>

<div ng-view>

</div>

JavaScript:

function MyCtrl($scope, datasets) {    
    $scope.datasets = datasets;
}

MyCtrl.resolve = {
    datasets : function($q, $http) {
        var deferred = $q.defer();

        $http({method: 'GET', url: '/someUrl'})
            .success(function(data) {
                deferred.resolve(data)
            })
            .error(function(data){
                //actually you'd want deffered.reject(data) here
                //but to show what would happen on success..
                deferred.resolve("error value");
            });

        return deferred.promise;
    }
};

var myApp = angular.module('myApp', [], function($routeProvider) {
    $routeProvider.when('/', {
        templateUrl: '/editor-tpl.html',
        controller: MyCtrl,
        resolve: MyCtrl.resolve
    });
});​

http://jsfiddle.net/dTJ9N/3/

Optimierte Version:

Da $ http () bereits ein Versprechen zurückgibt (auch als verzögert bezeichnet), müssen wir eigentlich kein eigenes erstellen. So können wir MyCtrl vereinfachen. beschließen zu:

MyCtrl.resolve = {
    datasets : function($http) {
        return $http({
            method: 'GET', 
            url: 'http://fiddle.jshell.net/'
        });
    }
};

Das Ergebnis von $ http () enthält Daten , Status , Header und Konfigurationsobjekte. Daher müssen wir den Hauptteil von MyCtrl in Folgendes ändern:

$scope.datasets = datasets.data;

http://jsfiddle.net/dTJ9N/5/


Ich versuche so etwas zu tun, habe aber Probleme beim Einfügen von 'Datensätzen', da diese nicht definiert sind. Irgendwelche Gedanken?
Rob Bygrave

Hey mb21, ich denke, Sie können mir vielleicht bei dieser Frage helfen: stackoverflow.com/questions/14271713/…
winduptoy

Könnte mir jemand helfen, diese Antwort in das Format app.controller ('MyCtrl') zu konvertieren? jsfiddle.net/5usya/1 hat bei mir nicht funktioniert.
user1071182

Ich bekomme eine Fehlermeldung:Unknown provider: datasetsProvider <- datasets
Chovy

Sie können Ihre Antwort einfacher machen, indem Sie Datensätze durch folgende ersetzen:function($http) { return $http({method: 'GET', url: '/someUrl'}) .then( function(data){ return data;}, function(reason){return 'error value';} ); }
Morteza Tourani

32

Ich sehe einige Leute fragen, wie dies mit der Angular.Controller-Methode mit minifizierungsfreundlicher Abhängigkeitsinjektion zu tun ist. Da ich gerade damit angefangen habe, fühlte ich mich verpflichtet, zurückzukommen und zu helfen. Hier ist meine Lösung (übernommen aus der ursprünglichen Frage und Miskos Antwort):

angular.module('phonecat', ['phonecatFilters', 'phonecatServices', 'phonecatDirectives']).
  config(['$routeProvider', function($routeProvider) {
    $routeProvider.
      when('/phones', {
        templateUrl: 'partials/phone-list.html', 
        controller: PhoneListCtrl, 
        resolve: { 
            phones: ["Phone", "$q", function(Phone, $q) {
                var deferred = $q.defer();
                Phone.query(function(successData) {
                  deferred.resolve(successData); 
                }, function(errorData) {
                  deferred.reject(); // you could optionally pass error data here
                });
                return deferred.promise;
             ]
            },
            delay: ["$q","$defer", function($q, $defer) {
               var delay = $q.defer();
               $defer(delay.resolve, 1000);
               return delay.promise;
              }
            ]
        },

        }).
      when('/phones/:phoneId', {
        templateUrl: 'partials/phone-detail.html', 
        controller: PhoneDetailCtrl, 
        resolve: PhoneDetailCtrl.resolve}).
      otherwise({redirectTo: '/phones'});
}]);

angular.controller("PhoneListCtrl", [ "$scope", "phones", ($scope, phones) {
  $scope.phones = phones;
  $scope.orderProp = 'age';
}]);

Da dieser Code von der Frage / beliebtesten Antwort abgeleitet ist, ist er nicht getestet, sollte Sie jedoch in die richtige Richtung leiten, wenn Sie bereits verstehen, wie man einen minifizierungsfreundlichen Winkelcode erstellt. Der eine Teil, den mein eigener Code nicht benötigte, war das Einfügen von "Telefon" in die Auflösungsfunktion für "Telefone", und ich habe überhaupt kein "Verzögerungs" -Objekt verwendet.

Ich empfehle auch dieses Youtube-Video http://www.youtube.com/watch?v=P6KITGRQujQ&list=UUKW92i7iQFuNILqQOUOCrFw&index=4&feature=plcp , das mir sehr geholfen hat

Sollte es Sie interessieren, habe ich beschlossen, auch meinen eigenen Code (geschrieben in Coffeescript) einzufügen, damit Sie sehen können, wie ich ihn zum Laufen gebracht habe.

Zu Ihrer Information, im Voraus verwende ich einen generischen Controller, der mir hilft, CRUD bei mehreren Modellen durchzuführen:

appModule.config ['$routeProvider', ($routeProvider) ->
  genericControllers = ["boards","teachers","classrooms","students"]
  for controllerName in genericControllers
    $routeProvider
      .when "/#{controllerName}/",
        action: 'confirmLogin'
        controller: 'GenericController'
        controllerName: controllerName
        templateUrl: "/static/templates/#{controllerName}.html"
        resolve:
          items : ["$q", "$route", "$http", ($q, $route, $http) ->
             deferred = $q.defer()
             controllerName = $route.current.controllerName
             $http(
               method: "GET"
               url: "/api/#{controllerName}/"
             )
             .success (response) ->
               deferred.resolve(response.payload)
             .error (response) ->
               deferred.reject(response.message)

             return deferred.promise
          ]

  $routeProvider
    .otherwise
      redirectTo: '/'
      action: 'checkStatus'
]

appModule.controller "GenericController", ["$scope", "$route", "$http", "$cookies", "items", ($scope, $route, $http, $cookies, items) ->

  $scope.items = items
      #etc ....
    ]

Kann ich aus Ihrem Beispiel und meinen fehlgeschlagenen Versuchen richtig schließen, dass es resolvein neueren Versionen von Angular jetzt unmöglich ist, auf eine Funktion im Controller zu verweisen ? Also muss es direkt in der Konfiguration deklariert werden, wie es hier ist?
XML

@ XMLMLley Ich bin mir ziemlich sicher, dass das der Fall ist. Ich glaube, dieses Beispiel stammt aus 1.1.2, als ich es schrieb. Ich habe keine Dokumentation über das
Einfügen von

2
Cool, danke. Es gibt viele Beispiele dafür auf SO (wie die beiden besten hier), aber sie stammen alle aus den Jahren 2012 und Anfang 2013. Es ist ein eleganter Ansatz, der jedoch veraltet zu sein scheint. Die sauberste Alternative scheint nun darin zu bestehen, einzelne Dienste zu schreiben, die vielversprechende Objekte sind.
XML

Danke, das hat bei mir funktioniert. $deferBeachten Sie für alle anderen Benutzer, die Fehler bezüglich eines nicht definierten Dienstes erhalten, dass Sie in Version 1.5.7 von AngularJS $timeoutstattdessen verwenden möchten .
racl101

18

Dieses Commit , das Teil von Version 1.1.5 und höher ist, macht das $promiseObjekt von verfügbar $resource. Versionen von ngResource, einschließlich dieses Commits, ermöglichen das Auflösen von Ressourcen wie folgt:

$ routeProvider

resolve: {
    data: function(Resource) {
        return Resource.get().$promise;
    }
}

Regler

app.controller('ResourceCtrl', ['$scope', 'data', function($scope, data) {

    $scope.data = data;

}]);

Welche Versionen enthalten dieses Commit bitte?
XML

Die neueste instabile Version (1.1.5) enthält dieses Commit. ajax.googleapis.com/ajax/libs/angularjs/1.1.5/angular.min.js
Maximilian Hoffmann

Ich mag diesen weniger ausführlichen Ansatz. Es wäre schön, ein Versprechen aus dem eigentlichen Datenobjekt zu erstellen und dieses direkt weiterzugeben, aber dies ist so wenig Code, dass es gut funktioniert.
Sam Barnum

1
Wie würde die Ressource auf $ routeParams zugreifen? Zum Beispiel: in GET '/api/1/apps/:appId'-> App.get({id: $routeParams.appId}).$promise();kann ich nicht so verwenden
Zeronone

2
@zeronone spritzen Sie $routezu Ihrer Entschlossenheit und Verwendung $route.current.params. Seien Sie vorsichtig, $routeParamszeigt immer noch auf die alte Route.
Brice Stacey

16

Dieser Ausschnitt ist Dependency Injection freundlich (ich es auch verwende in Kombination von ngmin und uglify ) und es ist eine elegante Domain angetrieben basierte Lösung.

Das unten stehende Beispiel Register eine Telefonressource und eine Konstante phoneRoutes , die alle Ihre Routing - Informationen für diese (Telefon) Domäne enthält. Etwas , was ich tat es nicht wie in der mitgelieferten Antwort war die Lage der Entschlossenheit Logik - das Hauptmodul soll nicht wissen nichts oder über die Art und Weise gestört werden , die Ressourcen Argumente an die Steuerung zur Verfügung gestellt werden. Auf diese Weise bleibt die Logik in derselben Domäne.

Hinweis: Wenn Sie ngmin verwenden (und wenn nicht: sollten Sie), müssen Sie die Auflösungsfunktionen nur mit der DI-Array-Konvention schreiben.

angular.module('myApp').factory('Phone',function ($resource) {
  return $resource('/api/phone/:id', {id: '@id'});
}).constant('phoneRoutes', {
    '/phone': {
      templateUrl: 'app/phone/index.tmpl.html',
      controller: 'PhoneIndexController'
    },
    '/phone/create': {
      templateUrl: 'app/phone/edit.tmpl.html',
      controller: 'PhoneEditController',
      resolve: {
        phone: ['$route', 'Phone', function ($route, Phone) {
          return new Phone();
        }]
      }
    },
    '/phone/edit/:id': {
      templateUrl: 'app/phone/edit.tmpl.html',
      controller: 'PhoneEditController',
      resolve: {
        form: ['$route', 'Phone', function ($route, Phone) {
          return Phone.get({ id: $route.current.params.id }).$promise;
        }]
      }
    }
  });

Das nächste Stück ist das Injizieren der Routing-Daten, wenn sich das Modul im Konfigurationsstatus befindet, und das Anwenden auf den $ routeProvider .

angular.module('myApp').config(function ($routeProvider, 
                                         phoneRoutes, 
                                         /* ... otherRoutes ... */) {

  $routeProvider.when('/', { templateUrl: 'app/main/index.tmpl.html' });

  // Loop through all paths provided by the injected route data.

  angular.forEach(phoneRoutes, function(routeData, path) {
    $routeProvider.when(path, routeData);
  });

  $routeProvider.otherwise({ redirectTo: '/' });

});

Das Testen der Routenkonfiguration mit diesem Setup ist ebenfalls recht einfach:

describe('phoneRoutes', function() {

  it('should match route configuration', function() {

    module('myApp');

    // Mock the Phone resource
    function PhoneMock() {}
    PhoneMock.get = function() { return {}; };

    module(function($provide) {
      $provide.value('Phone', FormMock);
    });

    inject(function($route, $location, $rootScope, phoneRoutes) {
      angular.forEach(phoneRoutes, function (routeData, path) {

        $location.path(path);
        $rootScope.$digest();

        expect($route.current.templateUrl).toBe(routeData.templateUrl);
        expect($route.current.controller).toBe(routeData.controller);
      });
    });
  });
});

Sie können es in meinem letzten (bevorstehenden) Experiment in voller Pracht sehen . Obwohl diese Methode für mich gut funktioniert, frage ich mich wirklich, warum der $ -Injektor die Konstruktion von nichts verzögert , wenn er die Injektion von etwas erkennt , das ein vielversprechendes Objekt ist. es würde die Dinge sooooooooooooooo viel einfacher machen.

Bearbeiten: verwendet Angular v1.2 (rc2)


2
Diese ausgezeichnete Antwort scheint viel mehr im Einklang mit der "Angular" -Philosophie (Kapselung usw.) zu stehen. Wir sollten uns alle bewusst bemühen, zu verhindern, dass sich die Logik wie Kudzu über die gesamte Codebasis schleicht.
Zakdances

I really wonder why the $injector isn't delaying construction of anything when it detects injection of anything that is a promise objectIch vermute, sie haben diese Funktionalität weggelassen, weil sie möglicherweise Entwurfsmuster fördert, die sich negativ auf die Reaktionsfähigkeit von Apps auswirken. Die ideale App in ihrem Kopf ist eine wirklich asynchrone, daher sollte das Auflösen ein Randfall sein.
Zakdances

11

Wenn Sie die Route verzögern, führt dies mit Sicherheit zu einem asynchronen Gewirr. Verfolgen Sie einfach den Ladestatus Ihrer Hauptentität und verwenden Sie diesen in der Ansicht. In Ihrem Controller können Sie beispielsweise sowohl die Erfolgs- als auch die Fehlerrückrufe für ngResource verwenden:

$scope.httpStatus = 0; // in progress
$scope.projects = $resource.query('/projects', function() {
    $scope.httpStatus = 200;
  }, function(response) {
    $scope.httpStatus = response.status;
  });

Dann könnten Sie in der Ansicht alles tun:

<div ng-show="httpStatus == 0">
    Loading
</div>
<div ng-show="httpStatus == 200">
    Real stuff
    <div ng-repeat="project in projects">
         ...
    </div>
</div>
<div ng-show="httpStatus >= 400">
    Error, not found, etc. Could distinguish 4xx not found from 
    5xx server error even.
</div>

6
Möglicherweise ist es nicht richtig, den HTTP-Status der Ansicht zugänglich zu machen, genauso wenig wie der Umgang mit CSS-Klassen und DOM-Elementen, die in den Controller gehören. Ich würde wahrscheinlich die gleiche Idee verwenden, aber den abstrakten Status in isValid () und isLoaded () entfernen.
Jpsimons

1
Dies ist in der Tat nicht die beste Trennung von Bedenken, und es stürzt ab, wenn Sie verschachtelte Controller haben, die vom jeweiligen Objekt abhängen.
null

Dies ist ziemlich clever. Um die Statuscodes der Ansicht zugänglich zu machen, können Sie die http-Logik einfach in die Bereichseigenschaften des Controllers einfügen und dann an sie binden. Auch wenn Sie mehrere Ajax-Aufrufe im Hintergrund tätigen, möchten Sie dies trotzdem tun.
KingOfHypocrites

Dies wäre in Ordnung, wenn es nur darum ginge, eine Ansicht zu verzögern. Die Auflösung wird jedoch am besten verwendet, wenn Sie die Instanziierung eines Controllers verzögern müssen - nicht nur die Ansicht. (Beispiel: Wenn Sie sicherstellen müssen, dass Ihr JSON geladen ist, weil Ihr Controller ihn vor der Verkabelung an eine Direktive übergibt.) Aus den Dokumenten: "Der Router wartet darauf, dass alle aufgelöst oder einer abgelehnt wird, bevor der Controller ausgeführt wird instanziiert ".
Dan

8

Ich habe nach Miskos Code oben gearbeitet und das habe ich damit gemacht. Dies ist eine aktuellere Lösung, da $deferauf geändert wurde $timeout. Das Ersetzen $timeoutwartet jedoch auf die Zeitüberschreitung (in Miskos Code 1 Sekunde) und gibt dann die Daten zurück, in der Hoffnung, dass sie rechtzeitig behoben werden. Auf diese Weise wird so schnell wie möglich zurückgegeben.

function PhoneListCtrl($scope, phones) {
  $scope.phones = phones;
  $scope.orderProp = 'age';
}

PhoneListCtrl.resolve = {

  phones: function($q, Phone) {
    var deferred = $q.defer();

    Phone.query(function(phones) {
        deferred.resolve(phones);
    });

    return deferred.promise;
  }
}

7

Verwenden von AngularJS 1.1.5

Aktualisieren der 'Telefone'-Funktion in Justens Antwort mithilfe der AngularJS 1.1.5- Syntax.

Original:

phones: function($q, Phone) {
    var deferred = $q.defer();

    Phone.query(function(phones) {
        deferred.resolve(phones);
    });

    return deferred.promise;
}

Aktualisiert:

phones: function(Phone) {
    return Phone.query().$promise;
}

Viel kürzer dank des Angular-Teams und der Mitwirkenden. :) :)

Dies ist auch die Antwort von Maximilian Hoffmann. Anscheinend hat dieses Commit es in 1.1.5 geschafft.


1
Ich kann anscheinend nichts $promisein den Dokumenten finden . Es wurde möglicherweise ab Version 2.0 entfernt.
Zakdances

Es ist nur in 1.2
Thomas

5

Sie können die Eigenschaft $ routeProvider auflösen verwenden, um die Routenänderung zu verzögern, bis Daten geladen werden.

angular.module('app', ['ngRoute']).
  config(['$routeProvider', function($routeProvider, EntitiesCtrlResolve, EntityCtrlResolve) {
    $routeProvider.
      when('/entities', {
        templateUrl: 'entities.html', 
        controller: 'EntitiesCtrl', 
        resolve: EntitiesCtrlResolve
      }).
      when('/entity/:entityId', {
        templateUrl: 'entity.html', 
        controller: 'EntityCtrl', 
        resolve: EntityCtrlResolve
      }).
      otherwise({redirectTo: '/entities'});
}]);

Beachten Sie, dass die resolveEigenschaft auf der Route definiert ist.

EntitiesCtrlResolveund EntityCtrlResolvesind konstante Objekte, die in derselben Datei wie EntitiesCtrlund EntityCtrlController definiert sind.

// EntitiesCtrl.js

angular.module('app').constant('EntitiesCtrlResolve', {
  Entities: function(EntitiesService) {
    return EntitiesService.getAll();
  }
});

angular.module('app').controller('EntitiesCtrl', function(Entities) {
  $scope.entities = Entities;

  // some code..
});

// EntityCtrl.js

angular.module('app').constant('EntityCtrlResolve', {
  Entity: function($route, EntitiesService) {
    return EntitiesService.getById($route.current.params.projectId);
  }
});

angular.module('app').controller('EntityCtrl', function(Entity) {
  $scope.entity = Entity;

  // some code..
});

3

Ich mag die Idee von darkporter, weil es für ein Entwicklerteam, das neu bei AngularJS ist, leicht sein wird, sie sofort zu verstehen und zu arbeiten.

Ich habe diese Anpassung erstellt, die 2 Divs verwendet, einen für die Laderleiste und einen für den tatsächlichen Inhalt, der nach dem Laden der Daten angezeigt wird. Die Fehlerbehandlung würde an anderer Stelle erfolgen.

Fügen Sie $ scope ein 'ready'-Flag hinzu:

$http({method: 'GET', url: '...'}).
    success(function(data, status, headers, config) {
        $scope.dataForView = data;      
        $scope.ready = true;  // <-- set true after loaded
    })
});

In der HTML-Ansicht:

<div ng-show="!ready">

    <!-- Show loading graphic, e.g. Twitter Boostrap progress bar -->
    <div class="progress progress-striped active">
        <div class="bar" style="width: 100%;"></div>
    </div>

</div>

<div ng-show="ready">

    <!-- Real content goes here and will appear after loading -->

</div>

Siehe auch: Boostrap-Fortschrittsbalken-Dokumente


Fällt etwas auseinander, wenn Sie mehrere Daten laden. Woher weißt du, ob alles geladen ist?
Toxaq

Die Dinge haben sich weiterentwickelt, seit ich diese Antwort im Februar hinzugefügt habe, mit viel mehr Aktivität auf dieser Seite. Offenbar gibt es in Angular eine bessere Unterstützung für die Lösung dieses Problems als hier vorgeschlagen. Prost,
reggoodwin

Ich komme etwas spät an, aber der Umgang mit mehreren Daten ist kein großes Problem. Sie müssen nur separate Variablen (Boolesche Werte: isReadyData1, isReadyData2 usw.) für jede Anforderung verwenden und $ scope.ready = isReadyData1 && isReadyData2 ...; funktioniert gut für mich.
GuillaumeA

1

Ich mochte die obigen Antworten und habe viel von ihnen gelernt, aber in den meisten der obigen Antworten fehlt etwas.

Ich steckte in einem ähnlichen Szenario fest, in dem ich die URL mit einigen Daten auflöste, die in der ersten Anforderung vom Server abgerufen wurden. Das Problem, mit dem ich konfrontiert war, war, was ist, wenn das Versprechen ist rejected.

Ich war mit einem benutzerdefinierten Anbieter , die ein benutzten zurückzukehren , Promisedie durch die aufgelöste wurden resolvevon $routeProviderzum Zeitpunkt der Konfigurationsphase.

Was ich hier hervorheben möchte, ist das Konzept, dass whenes so etwas tut.

Es sieht die URL in der URL-Leiste und dann den entsprechenden whenBlock im aufgerufenen Controller und die Ansicht wird bisher so gut verwiesen.

Nehmen wir an, ich habe folgenden Konfigurationsphasencode.

App.when('/', {
   templateUrl: '/assets/campaigns/index.html',
   controller: 'CampaignListCtr',
   resolve : {
      Auth : function(){
         return AuthServiceProvider.auth('campaign');
      }
   }
})
// Default route
.otherwise({
   redirectTo: '/segments'
});

Bei der Root-URL im Browser wird der erste Block des Laufs aufgerufen, andernfalls otherwisewird er aufgerufen.

Stellen wir uns ein Szenario vor, in dem ich rootUrl in der Adressleistenfunktion drücke AuthServicePrivider.auth().

Sagen wir Versprechen zurück in wird ablehnen Zustand , was dann ???

Es wird überhaupt nichts gerendert.

Otherwise Der Block wird nicht ausgeführt, wie dies für eine URL der Fall ist, die nicht im Konfigurationsblock definiert ist und der Konfigurationsphase von angleJs unbekannt ist.

Wir müssen das Ereignis behandeln, das ausgelöst wird, wenn dieses Versprechen nicht gelöst wird. Bei einem Fehler $routeChangeErorrwird ausgelöst $rootScope.

Es kann wie im folgenden Code gezeigt erfasst werden.

$rootScope.$on('$routeChangeError', function(event, current, previous, rejection){
    // Use params in redirection logic.
    // event is the routeChangeEvent
    // current is the current url
    // previous is the previous url
    $location.path($rootScope.rootPath);
});

IMO Es ist im Allgemeinen eine gute Idee, den Ereignisverfolgungscode in den Ausführungsblock der Anwendung einzufügen. Dieser Code wird unmittelbar nach der Konfigurationsphase der Anwendung ausgeführt.

App.run(['$routeParams', '$rootScope', '$location', function($routeParams, $rootScope, $location){
   $rootScope.rootPath = "my custom path";
   // Event to listen to all the routeChangeErrors raised
   // by the resolve in config part of application
   $rootScope.$on('$routeChangeError', function(event, current, previous, rejection){
       // I am redirecting to rootPath I have set above.
       $location.path($rootScope.rootPath);
   });
}]);

Auf diese Weise können wir Versprechensfehler zum Zeitpunkt der Konfigurationsphase behandeln.


0

Ich hatte eine komplexe mehrstufige Gleitfeldoberfläche mit deaktivierter Bildschirmebene. Erstellen einer Direktive zum Deaktivieren der Bildschirmebene, die ein Klickereignis zum Ausführen des Status "Gefällt mir" erstellt

$state.go('account.stream.social.view');

erzeugten einen Flicking-Effekt. history.back () stattdessen hat es gut funktioniert, aber in meinem Fall ist es nicht immer wieder in der Geschichte. Was ich also herausfinde, ist, dass, wenn ich einfach ein Attribut href auf meinem Deaktivierungsbildschirm anstelle von state.go erstelle, es wie ein Zauber funktioniert hat.

<a class="disable-screen" back></a>

Richtlinie "zurück"

app.directive('back', [ '$rootScope', function($rootScope) {

    return {
        restrict : 'A',
        link : function(scope, element, attrs) {
            element.attr('href', $rootScope.previousState.replace(/\./gi, '/'));
        }
    };

} ]);

app.js Ich speichere nur den vorherigen Status

app.run(function($rootScope, $state) {      

    $rootScope.$on("$stateChangeStart", function(event, toState, toParams, fromState, fromParams) {         

        $rootScope.previousState = fromState.name;
        $rootScope.currentState = toState.name;


    });
});

-2

Eine mögliche Lösung könnte darin bestehen, die ng-cloak-Direktive mit dem Element zu verwenden, in dem wir die Modelle verwenden, z

<div ng-cloak="">
  Value in  myModel is: {{myModel}}
</div>

Ich denke, das kostet am wenigsten Mühe.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.