Atraso na alteração de rota do AngularJS até o modelo ser carregado para evitar oscilações

321

Gostaria de saber se existe uma maneira (semelhante ao Gmail) para o AngularJS atrasar a exibição de uma nova rota até depois que cada modelo e seus dados forem buscados usando seus respectivos serviços.

Por exemplo, se houvesse um ProjectsController que listasse todos os projetos e project_index.htmlque fosse o modelo que mostrava esses projetos, Project.query()seria buscado completamente antes de mostrar a nova página.

Até então, a página antiga continuaria sendo exibida (por exemplo, se eu estivesse navegando em outra página e decidisse ver este índice do projeto).

Misko Hevery
fonte

Respostas:

374

A propriedade $ routeProvider resolve permite atrasar a alteração de rota até que os dados sejam carregados.

Primeiro defina uma rota com resolveatributo como este.

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

observe que a resolvepropriedade está definida na rota.

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

Observe que a definição do controlador contém um objeto de resolução que declara itens que devem estar disponíveis para o construtor do controlador. Aqui o phonesé injetado no controlador e é definido na resolvepropriedade

A resolve.phonesfunção é responsável por retornar uma promessa. Todas as promessas são coletadas e a alteração da rota é adiada até que todas as promessas sejam resolvidas.

Demonstração de trabalho: http://mhevery.github.com/angular-phonecat/app/#/phones Fonte: https://github.com/mhevery/angular-phonecat/commit/ba33d3ec2d01b70eb5d3d531619bf90153496831

Misko Hevery
fonte
10
@MiskoHevery - e se seus controladores estiverem dentro de um módulo e forem definidos como uma string em vez de como uma função. Como você pode configurar o atributo de resolução como você faz?
aar0n
53
Como isso é usado nas angular.controller()definições de controlador de tipo? No $routeProvidermaterial, pensei que você tivesse que usar nomes de string de controladores.
Ben Lesh
6
Algum exemplo usando angular.controller () e com a versão mais recente do AngularJS?
Laurent
22
@blesh, quando você usa angular.controller(), pode atribuir o resultado desta função a uma variável ( var MyCtrl = angular.controller(...)) e depois trabalhar com isso ( MyCtrl.loadData = function(){..}). Veja o vídeo de egghead, o código é mostrado lá imediatamente: egghead.io/video/0uvAseNXDr0
petrkotek
17
Eu ainda gostaria de uma boa maneira de lidar com isso sem colocar seu controlador em um mundo global. Não quero jogar lixo em todos os lugares. Você pode fazer isso com uma constante, mas seria bom poder colocar a função de resolução no / no controlador, não em outro lugar.
precisa saber é o seguinte
51

Aqui está um exemplo de trabalho mínimo que funciona para o Angular 1.0.2

Modelo:

<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/

Versão simplificada:

Como $ http () já retorna uma promessa (também adiada), na verdade não precisamos criar nossa própria. Para que possamos simplificar o MyCtrl. resolver para:

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

O resultado de $ http () contém dados , status , cabeçalhos e objetos de configuração , portanto, precisamos alterar o corpo do MyCtrl para:

$scope.datasets = datasets.data;

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

mb21
fonte
Estou tentando fazer algo assim, mas estou tendo problemas para injetar 'conjuntos de dados', pois não está definido. Alguma ideia?
precisa saber é o seguinte
Hey MB21, eu acho que você pode ser capaz de me ajudar com esta pergunta: stackoverflow.com/questions/14271713/...
winduptoy
Alguém poderia me ajudar a converter esta resposta no formato app.controller ('MyCtrl')? jsfiddle.net/5usya/1 não funcionou para mim.
usar o seguinte comando
Eu recebo um erro:Unknown provider: datasetsProvider <- datasets
chovy
Você pode simplificar sua resposta substituindo os conjuntos de dados por:function($http) { return $http({method: 'GET', url: '/someUrl'}) .then( function(data){ return data;}, function(reason){return 'error value';} ); }
Morteza Tourani 8/16
32

Vejo algumas pessoas perguntando como fazer isso usando o método angular.controller com injeção de dependência amigável à minificação. Desde que acabei de trabalhar, senti-me obrigado a voltar e ajudar. Aqui está minha solução (adotada da pergunta original e da resposta de Misko):

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

Como esse código é derivado da pergunta / resposta mais popular, ele não foi testado, mas deve ser enviado na direção certa, se você já entender como tornar um código angular amigável à minificação. A parte que o meu próprio código não exige foi a injeção de "Phone" na função de resolução para 'phones', nem usei nenhum objeto 'delay'.

Eu também recomendo este vídeo do YouTube http://www.youtube.com/watch?v=P6KITGRQujQ&list=UUKW92i7iQFuNILqQOUOCrFw&index=4&feature=plcp , o que me ajudou um pouco

Se lhe interessar, decidi também colar meu próprio código (escrito em coffeescript) para que você possa ver como eu o fiz funcionar.

Para sua informação, com antecedência eu uso um controlador genérico que me ajuda a fazer CRUD em vários modelos:

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 ....
    ]
bitwit
fonte
Eu deduzo corretamente do seu exemplo e das minhas tentativas falhas que agora é impossível fazer referência a uma resolvefunção no controlador, nas versões recentes do Angular? Portanto, ele deve ser declarado na configuração, como está aqui?
XML
@XMLilley Tenho certeza de que é esse o caso. Este exemplo foi da 1.1.2 quando eu escrevi, acredito. Eu não vi qualquer documentação sobre colocando resolve dentro de um controlador
bitwit
2
Legal, obrigado. Existem muitos exemplos de como fazê-lo no SO (como os dois principais aqui), mas todos são de 2012 e início de 2013. É uma abordagem elegante, mas parece estar obsoleta. A alternativa mais limpa agora parece estar escrevendo serviços individuais que são objetos de promessa.
XML
Graças isso funcionou para mim. Para qualquer outra pessoa que obtiver erros sobre $deferserviço indefinido , observe que na versão 1.5.7 do AngularJS, você deseja usar $timeout.
racl101
18

Esse commit , que faz parte da versão 1.1.5 e posterior, expõe o $promiseobjeto de $resource. Versões do ngResource, incluindo esta confirmação, permitem resolver recursos como este:

$ routeProvider

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

controlador

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

    $scope.data = data;

}]);
Maximilian Hoffmann
fonte
Quais versões incluem esse commit, por favor?
XML
A versão instável mais recente (1.1.5) inclui esse commit. Ajax.googleapis.com/ajax/libs/angularjs/1.1.5/angular.min.js
Maximilian Hoffmann
Eu gosto dessa abordagem menos detalhada. Seria bom criar uma promessa a partir do objeto de dados real e passá-la diretamente, mas esse código é tão pouco que funciona muito bem.
Sam Barnum
1
Como o recurso acessaria $ routeParams? Por exemplo: em GET '/api/1/apps/:appId'-> App.get({id: $routeParams.appId}).$promise();eu não posso usar assim #
zeronone 13/02/14
2
@zeronone você injeta $routesua resolução e uso $route.current.params. Cuidado, $routeParamsainda está apontando para a rota antiga.
Brice Stacey
16

Esse snippet é compatível com injeção de dependência (eu até o uso em combinação de ngmin e uglify ) e é mais elegante solução baseada em domínio .

O exemplo abaixo registra um recurso Phone e um phoneRoutes constante , que contém todas as suas informações de roteamento para esse domínio (telefone). Algo que não gostei na resposta fornecida foi a localização da lógica de resolução - o módulo principal não deve saber nada ou se preocupar com a maneira como os argumentos dos recursos são fornecidos ao controlador. Dessa forma, a lógica permanece no mesmo domínio.

Nota: se você estiver usando ngmin (e se não estiver: deve) deve escrever apenas as funções de resolução com a convenção de matriz DI.

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

A próxima parte é injetar os dados de roteamento quando o módulo estiver no estado de configuração e aplicá-los ao $ 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: '/' });

});

Testar a configuração da rota com esta configuração também é bastante fácil:

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

Você pode vê-lo com toda a glória no meu mais recente experimento . Embora esse método funcione bem para mim, eu realmente me pergunto por que o injetor $ não está atrasando a construção de nada quando detecta a injeção de qualquer coisa que seja um objeto de promessa ; isso tornaria as coisas muuuuito mais fáceis.

Edit: usado Angular v1.2 (rc2)

nulo
fonte
2
Essa excelente resposta parece muito mais alinhada com a filosofia "Angular" (encapsulamento etc.). Todos devemos estar fazendo um esforço consciente para impedir que a lógica se arraste por toda a base de código como o kudzu.
Zakdances 22/10
I really wonder why the $injector isn't delaying construction of anything when it detects injection of anything that is a promise objectSuponho que eles tenham omitido essa funcionalidade porque isso pode incentivar padrões de design que afetam negativamente a capacidade de resposta dos aplicativos. O aplicativo ideal em sua mente é verdadeiramente assíncrono, portanto, a resolução deve ser um caso de extrema importância.
Zakdances 22/10
11

O atraso na exibição da rota certamente levará a um emaranhado assíncrono ... por que não simplesmente rastrear o status de carregamento da sua entidade principal e usá-lo na exibição. Por exemplo, no seu controlador, você pode usar os retornos de sucesso e erro no ngResource:

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

Então, na visualização, você pode fazer o que for:

<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>
jpsimons
fonte
6
Talvez expor o status HTTP à visualização não esteja correto, mais do que lidar com classes CSS e elementos DOM pertencentes ao controlador. Eu provavelmente usaria a mesma ideia, mas o status abstrato fora em isValid () e isLoaded ().
Jpsimons 08/09/12
1
De fato, essa não é a melhor separação de preocupações, além disso, ela falhará se você tiver controladores aninhados que dependem do objeto específico.
Nulo
Isso é bastante inteligente. Para expor os códigos de status à exibição, você pode simplesmente colocar a lógica http nas propriedades do escopo no controlador e vincular a elas. Além disso, se você fizer várias chamadas ajax que acontecem em segundo plano, você fará isso de qualquer maneira.
KingOfHypocrites
Isso seria bom se o problema fosse simplesmente adiar uma exibição. Mas a resolução é melhor usada se você precisar atrasar a instanciação de um controlador - não apenas a visualização. (Por exemplo: se você precisa ter certeza de que seu JSON está carregado, porque seu controlador o passa para uma diretiva antes de ser conectado.) Na documentação: "o roteador aguardará que todos sejam resolvidos ou que um seja rejeitado antes que o controlador seja instanciado ".
Dan
8

Eu trabalhei com o código de Misko acima e foi isso que fiz com ele. Esta é uma solução mais atual, pois $deferfoi alterada para $timeout. Substituir, $timeoutno entanto, aguardará o período de tempo limite (no código de Misko, 1 segundo) e retornará os dados esperando que sejam resolvidos a tempo. Dessa forma, ele retorna o mais rápido possível.

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;
  }
}
Justen
fonte
7

Usando AngularJS 1.1.5

Atualizando a função 'phones' na resposta de Justen usando a sintaxe do AngularJS 1.1.5 .

Original:

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

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

    return deferred.promise;
}

Atualizada:

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

Muito mais curto, graças à equipe e colaboradores da Angular. :)

Esta é também a resposta de Maximilian Hoffmann. Aparentemente, esse commit entrou no 1.1.5.

OJ Raqueño
fonte
1
Não consigo encontrar nada $promisenos documentos . Pode ter sido retirado a partir da v2.0 +.
Zakdances 22/10
Ele está disponível apenas na versão 1.2
Thomas
5

Você pode usar a propriedade $ routeProvider resolve para atrasar a alteração de rota até que os dados sejam carregados.

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

Observe que a resolvepropriedade está definida na rota.

EntitiesCtrlResolvee EntityCtrlResolvesão objetos constantes definidos no mesmo arquivo EntitiesCtrle nos EntityCtrlcontroladores.

// 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..
});
Bohdan Lyzanets
fonte
3

Gosto da ideia do darkporter porque será fácil para uma equipe de desenvolvimento nova do AngularJS entender e trabalhar imediatamente.

Eu criei essa adaptação que usa 2 divs, uma para a barra do carregador e outra para o conteúdo real exibido após o carregamento dos dados. O tratamento de erros seria feito em outro lugar.

Adicione um sinalizador 'pronto' ao $ scope:

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

Na visualização html:

<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>

Consulte também: Documentos da barra de progresso do Boostrap

reggoodwin
fonte
Desmorona um pouco se você estiver carregando vários dados. Como você sabe se tudo está carregado?
toxaq
As coisas mudaram desde que adicionei esta resposta em fevereiro, com muito mais atividade nesta página. Parece que existe um suporte melhor no Angular para resolver esse problema agora do que o sugerido aqui. Cheers,
reggoodwin
Chego um pouco atrasado, mas lidar com vários dados não é uma grande preocupação. Você só precisa usar variáveis ​​separadas (booleans: isReadyData1, isReadyData2 etc.) para cada solicitação e definir o $ scope.ready = isReadyData1 && isReadyData2 ...; funciona bem para mim.
GuillaumeA
1

Gostei das respostas acima e aprendi muito com elas, mas há algo que está faltando na maioria das respostas acima.

Eu estava preso em um cenário semelhante em que estava resolvendo o URL com alguns dados que foram buscados na primeira solicitação do servidor. O problema que enfrentei foi o que aconteceria se a promessa fosse rejected.

Eu estava usando um provedor personalizado que usou para retornar um Promiseque foi resolvido pelo resolvede $routeProvider, no momento da fase de configuração.

O que quero enfatizar aqui é o conceito de when que faz algo assim.

Ele vê o URL na barra de URL e, em seguida, o respectivo whenbloco no chamado controlador e a exibição é referida até agora como boa.

Digamos que eu tenho o seguinte código de fase de configuração.

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

No URL raiz do navegador, o primeiro bloco de execução é chamado, caso contrário, otherwiseé chamado.

Vamos imaginar que um cenário em que eu carregue no rootUrl na AuthServicePrivider.auth()função da barra de endereço seja chamado.

Vamos dizer que a promessa retornada está em estado de rejeição o que então ???

Nada é renderizado.

Otherwise O bloco não será executado como é para qualquer URL que não esteja definido no bloco de configuração e seja desconhecido na fase de configuração do angularJs.

Teremos que lidar com o evento que é acionado quando essa promessa não for resolvida. Em caso de falha $routeChangeErorré acionado $rootScope.

Pode ser capturado como mostrado no código abaixo.

$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 Geralmente, é uma boa ideia colocar o código de rastreamento de eventos no bloco de aplicativos em execução. Este código é executado logo após a fase de configuração do aplicativo.

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

Dessa forma, podemos lidar com falhas prometidas no momento da fase de configuração.

Ashish Singh
fonte
0

Eu tive uma interface complexa de painel deslizante de vários níveis, com a camada de tela desativada. Criando diretiva para desativar a camada de tela que criaria um evento click para executar o estado como

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

estavam produzindo um efeito sacudindo. history.back () em vez de funcionar ok, no entanto, nem sempre está de volta à história no meu caso. Então, o que eu descobri é que, se eu simplesmente criar o atributo href na minha tela de desativação em vez de state.go, funcionaria como um encanto.

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

Diretiva 'voltar'

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

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

} ]);

app.js Acabei de salvar o estado anterior

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

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

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


    });
});
Dima
fonte
-2

Uma solução possível pode ser usar a diretiva ng-cloak com o elemento em que estamos usando os modelos, por exemplo

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

Eu acho que este exige menos esforço.

Devendra Singh
fonte