AngularJS $ location ne change pas le chemin

126

Je rencontre un problème avec la modification de l'URL de la page après l'envoi d'un formulaire.

Voici le flux de mon application:

  1. Les itinéraires sont définis, l'URL est reconnue vers une page de formulaire.
  2. Les pages chargent, le contrôleur définit les variables, les directives sont déclenchées.
  3. Une directive de formulaire spéciale est déclenchée qui effectue une soumission de formulaire spéciale à l'aide d'AJAX.
  4. Après l'exécution de l'AJAX (Angular ne s'occupe pas de l'AJAX), un rappel est déclenché et la directive appelle la $scope.onAfterSubmitfonction qui définit l'emplacement.

Le problème est qu'après avoir défini l'emplacement, rien ne se passe. J'ai également essayé de définir le paramètre d'emplacement sur /... Non. J'ai également essayé de ne pas soumettre le formulaire. Rien ne fonctionne.

J'ai testé pour voir si le code atteint la onAfterSubmitfonction (ce qu'il fait).

Ma seule pensée est que d'une manière ou d'une autre, la portée de la fonction est modifiée (depuis qu'elle est appelée à partir d'une directive), mais comment peut-elle être appelée onAfterSubmitsi la portée a changé?

Voici mon code

var Ctrl = function($scope, $location, $http) {
  $http.get('/resources/' + $params.id + '/edit.json').success(function(data) {
    $scope.resource = data;
  });

  $scope.onAfterSubmit = function() {
    $location.path('/').replace();
  };
}
Ctrl.$inject = ['$scope','$location','$http'];

Quelqu'un peut m'aider s'il vous plaît?

matsko
la source
1
duplication possible de Angular $ location.path ne fonctionne pas
Jim G.
Gardez à l'esprit que cela a été créé un an avant celui-là.
matsko
Juste et avec le bénéfice d'une année supplémentaire, l'autre a une réponse acceptée plus précisément correcte.
Jim G.
1
@JimG. ce n'est pas un doublon, cette question remonte à 4 ans, celle que vous liez, il y a 2 ans.
Castro Roy

Réponses:

298

J'ai eu un problème similaire il y a quelques jours. Dans mon cas, le problème était que j'ai changé les choses avec une bibliothèque tierce (jQuery pour être précis) et dans ce cas, même si l'appel de fonctions et la définition de variables fonctionnent, Angular ne reconnaît pas toujours qu'il y a des changements, il ne digère donc jamais.

$ apply () est utilisé pour exécuter une expression angulaire depuis l'extérieur du cadre angulaire. (Par exemple, à partir d'événements DOM du navigateur, setTimeout, XHR ou des bibliothèques tierces).

Essayez de l'utiliser $scope.$apply()juste après avoir changé l'emplacement et appelé replace()pour informer Angular que les choses ont changé.

F Lekschas
la source
1
Fonctionne comme un charme. J'ai ajouté le code que j'ai utilisé dans votre message pour le faire fonctionner :) Merci beaucoup !!!
matsko
4
Pour avoir une meilleure idée de la façon dont $ apply fonctionne avec angular et comment contourner ses problèmes, voici un lien pour cette yearofmoo.com/2012/10
...
2
@Skeater Vous pouvez simplement injecter le scope racine et opérer dessus comme ceci: factory ('xyz', ['$ rootScope', function ($ rootScope) {...}])
F Lekschas
4
peut encapsuler votre rappel avec $ timeout et la portée sera correctement appliquée
JasonS
7
Comme JasonS l'a dit, utilisez $ timeout (function () {// appliquer les modifications ici}); Cela présente deux avantages: 1) Vous n'avez pas besoin d'accéder à $ scope. 2) Vous n'avez pas à vous soucier de l'exécution de $ apply déjà. Pour la deuxième raison, l'utilisation de $ timeout est souvent l'approche préférée.
ArneHugo
56

Au lieu de $location.path(...)changer ou d'actualiser la page, j'ai utilisé le service $window. Dans Angular, ce service est utilisé comme interface avec l' windowobjet, et l' windowobjet contient une propriété locationqui vous permet de gérer les opérations liées à l'emplacement ou à l'URL.

Par exemple, avec window.locationvous pouvez attribuer une nouvelle page, comme ceci:

$window.location.assign('/');

Ou actualisez-le, comme ceci:

$window.location.reload();

Cela a fonctionné pour moi. C'est un peu différent de ce à quoi vous vous attendez mais fonctionne pour l'objectif donné.

Paulo Oliveira
la source
3
moi aussi, $ location.path () ne redirige pas lorsque l'url a des paramètres
Sudhakar
2
C'est la bonne réponse. Voir ici
Irving
28

J'ai eu le même problème, mais mon appel à $ location était DÉJÀ dans un résumé. L'appel de $ apply () vient de donner une erreur $ digest déjà en cours de traitement.

Cette astuce a fonctionné (et assurez-vous d'injecter $locationdans votre contrôleur):

$timeout(function(){ 
   $location...
},1);

Bien que je ne sache pas pourquoi c'était nécessaire ...

Ross R
la source
5
Non, c'est une mauvaise idée. Vérifiez simplement la phase au cas où elle se trouve dans un résumé, puis vous pouvez ignorer l'application. Angular le rattrapera et changera l'URL de lui-même: coderwall.com/p/ngisma
matsko
3
timeout me semble être un sale hack. Si votre href contient "#", le $ location.path () ne fonctionne pas comme prévu. Supprimez simplement href = "#" sur votre balise a ou définissez-le simplement sur href = "" et vous n'aurez pas besoin d'utiliser $ timeout
Shankar ARUL - jupyterdata.com
7
$$ phase est une variable privée à laquelle vous ne devez pas essayer d'accéder car elle pourrait changer dans une nouvelle version angularjs.
Blaise le
7
Utiliser $ timeout peut être un hack, mais utiliser $$ phase est un hack encore plus grand. En règle générale, ne visualisez ni ne touchez aucun élément précédé de $$.
nilskp
3
Après environ 10 heures de bris de choses aléatoires ... cette solution a fonctionné pour moi!
Shakeel
6

J'ai dû intégrer ma $location.path()déclaration comme ceci car mon résumé était toujours en cours d'exécution:

       function routeMe(data) {                
            var waitForRender = function () {
                if ($http.pendingRequests.length > 0) {
                    $timeout(waitForRender);
                } else {
                    $location.path(data);
                }
            };
            $timeout(waitForRender);
        }
Helzgate
la source
1
Merci pour la fonction, très utile!
Bill Effin Murray
pour moi, le problème était que nous utilisions angular-block-ui et cela empêchait la mise à jour de l'emplacement de la barre d'adresse. nous avons décoré la requête $ http avec un booléen que notre requestFilter a récupéré afin que block-ui ne se déclenche pas sur cet appel particulier, puis tout s'est bien passé.
matao
2

Dans mon cas, le problème était l'indicateur de paramètre facultatif ('?') Manquant dans la configuration de mon modèle.

Par exemple:

.when('/abc/:id?', {
    templateUrl: 'views/abc.html',
    controller: 'abcControl'
})


$location.path('/abc');

Sans le caractère d'interrogation, l'itinéraire ne changerait évidemment pas en supprimant le paramètre d'itinéraire.

Victor Hugo
la source
Ce n'est pas une configuration de modèle mais une configuration de route.
Piotr Dobrogost
1

Si l'un d'entre vous utilise le routeur Angular-ui / ui-router, utilisez: $state.go('yourstate')au lieu de $location. Cela a fait l'affaire pour moi.

Elferone
la source
0

Cela fonctionne pour moi (dans CoffeeScript)

 $location.path '/url/path'
 $scope.$apply() if (!$scope.$$phase)
crocs
la source
0

À mon avis, la plupart des réponses ici semblent un peu haceuses (par exemple $ apply () ou $ timeout), car jouer avec $ apply () peut conduire à des erreurs indésirables.

Habituellement, lorsque $ location ne fonctionne pas, cela signifie que quelque chose n'a pas été implémenté de manière angulaire.

Dans cette question particulière, le problème semble être dans la partie AJAX non angulaire. J'ai eu un problème similaire, où la redirection à l'aide de $ location devrait avoir lieu après une promesse résolue. Je voudrais illustrer le problème sur cet exemple.

L'ancien code:

taskService.createTask(data).then(function(code){
            $location.path("task/" + code);
        }, function(error){});

Remarque: taskService.createTask renvoie une promesse.

$ q - la manière angulaire d'utiliser les promesses:

let dataPromises = [taskService.createTask(data)];
        $q.all(dataPromises).then(function(response) {
                let code = response[0];
                $location.path("task/" + code);
            }, 
            function(error){});

L'utilisation de $ q pour résoudre la promesse a résolu le problème de redirection.

En savoir plus sur le service $ q: https://docs.angularjs.org/api/ng/service/ $ q

iwan_gu
la source
-8
setTimeout(function() { $location.path("/abc"); },0);

Cela devrait résoudre votre problème.

Akshay
la source
1
Exécuter un non-setTimeout est une très mauvaise idée. Et, comme @matsko l'a dit ci-dessus, $ timeout n'est pas la meilleure idée.
gonzofish