Retrasar el cambio de ruta AngularJS hasta que se cargue el modelo para evitar parpadeos

321

Me pregunto si hay una manera (similar a Gmail) para que AngularJS demore la presentación de una nueva ruta hasta después de que cada modelo y sus datos se hayan obtenido utilizando sus servicios respectivos.

Por ejemplo, si hubiera una ProjectsControllerlista de todos los Proyectos y project_index.htmlcuál fuera la plantilla que mostrara estos Proyectos, se Project.query()obtendría completamente antes de mostrar la nueva página.

Hasta entonces, la página anterior seguiría mostrándose (por ejemplo, si estuviera navegando en otra página y luego decidiera ver este índice del Proyecto).

Misko Hevery
fuente

Respuestas:

374

La propiedad $ routeProvider resolve permite retrasar el cambio de ruta hasta que se carguen los datos.

Primero defina una ruta con un 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 la resolvepropiedad se define en la ruta.

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 la definición del controlador contiene un objeto de resolución que declara cosas que deberían estar disponibles para el constructor del controlador. Aquí phonesse inyecta en el controlador y se define en la resolvepropiedad.

La resolve.phonesfunción es responsable de devolver una promesa. Se recopilan todas las promesas y el cambio de ruta se retrasa hasta que se resuelvan todas las promesas.

Demostración de trabajo: http://mhevery.github.com/angular-phonecat/app/#/phones Fuente: https://github.com/mhevery/angular-phonecat/commit/ba33d3ec2d01b70eb5d3d531619bf90153496831

Misko Hevery
fuente
10
@MiskoHevery: ¿qué sucede si sus controladores están dentro de un módulo y se definen como una cadena en lugar de una función? ¿Cómo podría configurar el atributo resolver como lo hace?
aar0n
53
¿Cómo se usa esto en angular.controller()las definiciones de controlador de tipo? En el $routeProviderasunto, pensé que tenías que usar los nombres de cadena de los controladores.
Ben Lesh
66
¿Algún ejemplo usando angular.controller () y con la última versión de AngularJS?
Laurent
22
@blesh, cuando lo usa angular.controller(), puede asignar el resultado de esta función a una variable ( var MyCtrl = angular.controller(...)) y luego trabajar con eso más ( MyCtrl.loadData = function(){..}). Mira el video de egghead, el código se muestra allí de inmediato: egghead.io/video/0uvAseNXDr0
petrkotek
17
Todavía me gustaría una buena manera de hacerlo sin tener que colocar su controlador en un global. No quiero tirar basura con globos por todo el lugar. Puede hacerlo con una constante, pero sería bueno poder poner la función de resolución en / en el controlador, no en otro lugar.
Erik Honn
51

Aquí hay un ejemplo de trabajo mínimo que funciona para 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/

Versión simplificada:

Como $ http () ya devuelve una promesa (también conocida como diferida), en realidad no necesitamos crear la nuestra. Entonces podemos simplificar MyCtrl. resolver:

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

El resultado de $ http () contiene datos , estado , encabezados y objetos de configuración , por lo que debemos cambiar el cuerpo de MyCtrl a:

$scope.datasets = datasets.data;

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

mb21
fuente
Estoy tratando de hacer algo como esto, pero tengo problemas para inyectar 'conjuntos de datos' ya que no está definido. ¿Alguna idea?
Rob Bygrave
Hola mb21, creo que podrías ayudarme con esta pregunta: stackoverflow.com/questions/14271713/…
winduptoy
¿Podría alguien ayudarme a convertir esta respuesta al formato app.controller ('MyCtrl')? jsfiddle.net/5usya/1 no funcionó para mí.
user1071182
me sale un error:Unknown provider: datasetsProvider <- datasets
chovy
Puede simplificar su respuesta reemplazando los conjuntos de datos con esto:function($http) { return $http({method: 'GET', url: '/someUrl'}) .then( function(data){ return data;}, function(reason){return 'error value';} ); }
Morteza Tourani
32

Veo a algunas personas preguntando cómo hacer esto usando el método angular.controller con inyección de dependencia amigable con la minificación. Desde que empecé a trabajar, me sentí obligado a volver y ayudar. Aquí está mi solución (adoptada de la pregunta original y la respuesta 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';
}]);

Dado que este código se deriva de la pregunta / respuesta más popular, no se ha probado, pero debería enviarlo en la dirección correcta si ya comprende cómo hacer un código angular amigable para la minificación. La única parte que mi propio código no requería era una inyección de "Teléfono" en la función de resolución para "teléfonos", ni utilicé ningún objeto de "retraso".

También recomiendo este video de YouTube http://www.youtube.com/watch?v=P6KITGRQujQ&list=UUKW92i7iQFuNILqQOUOCrFw&index=4&feature=plcp , que me ayudó bastante

Si le interesa, he decidido pegar también mi propio código (escrito en coffeescript) para que pueda ver cómo lo hice funcionar.

Para su información, de antemano uso un controlador genérico que me ayuda a hacer CRUD en varios 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
fuente
¿Debo deducir correctamente de su ejemplo y mis intentos fallidos que ahora es imposible hacer referencia a una resolvefunción en el controlador, en versiones recientes de Angular? Entonces, ¿tiene que declararse justo en la configuración como está aquí?
XML
@XMLilley Estoy bastante seguro de que ese es el caso. Este ejemplo fue de 1.1.2 cuando lo escribí, creo. No vi ninguna documentación sobre cómo poner resolución dentro de un controlador
bitwit
2
Genial, gracias. Hay muchos ejemplos de cómo hacerlo en SO (como los dos primeros aquí), pero todos son de 2012 y principios de 2013. Es un enfoque elegante, pero parece estar en desuso. La alternativa más limpia ahora parece estar escribiendo servicios individuales que son objetos de promesa.
XML
Gracias, esto funcionó para mí. Para cualquier otra persona que reciba errores sobre el $deferservicio indefinido , tenga en cuenta que en la versión 1.5.7 de AngularJS, desea utilizar $timeouten su lugar.
racl101
18

Este commit , que es parte de la versión 1.1.5 y superior, expone el $promiseobjeto de $resource. Las versiones de ngResource que incluyen este commit permiten 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
fuente
¿Qué versiones incluyen esa confirmación, por favor?
XML
La última versión inestable (1.1.5) incluye esta confirmación. ajax.googleapis.com/ajax/libs/angularjs/1.1.5/angular.min.js
Maximilian Hoffmann
Me gusta este enfoque menos detallado. Sería bueno crear una promesa a partir del objeto de datos real y pasarla directamente, pero este es un código tan pequeño que funciona bien.
Sam Barnum el
1
¿Cómo accedería el recurso a $ routeParams? Por ejemplo: en GET '/api/1/apps/:appId'-> App.get({id: $routeParams.appId}).$promise();No puedo usar así
zeronone
2
@zeronone inyecta $routea su resolución y uso $route.current.params. Tenga cuidado, $routeParamstodavía está apuntando a la ruta anterior.
Brice Stacey
16

Este fragmento es apto para inyección de dependencias (incluso lo uso en combinación de ngmin y uglify ) y es una solución basada en dominio más elegante .

El siguiente ejemplo registra un recurso de teléfono y una ruta de teléfono constante , que contiene toda su información de enrutamiento para ese dominio (de teléfono). Algo que no me gustó en la respuesta proporcionada fue la ubicación de la lógica de resolución : el módulo principal no debe saber nada ni preocuparse por la forma en que los argumentos de los recursos se proporcionan al controlador. De esta manera, la lógica permanece en el mismo dominio.

Nota: si está usando ngmin (y si no lo está: debería) solo tiene que escribir las funciones de resolución con la convención 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;
        }]
      }
    }
  });

La siguiente pieza es inyectar los datos de enrutamiento cuando el módulo está en el estado de configuración y aplicarlo al $ 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: '/' });

});

Probar la configuración de ruta con esta configuración también es 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);
      });
    });
  });
});

Puedes verlo en toda su gloria en mi último (próximo) experimento . Aunque este método funciona bien para mí, realmente me pregunto por qué el inyector $ no está retrasando la construcción de nada cuando detecta la inyección de algo que es un objeto prometedor ; haría las cosas muuuucho más fáciles.

Editar: usado Angular v1.2 (rc2)

nulo
fuente
2
Esta excelente respuesta parece estar mucho más en línea con la filosofía "angular" (encapsulación, etc.). Todos deberíamos estar haciendo un esfuerzo consciente para evitar que la lógica se arrastre por toda la base de código como kudzu.
zakdances
I really wonder why the $injector isn't delaying construction of anything when it detects injection of anything that is a promise objectSupongo que omitieron esta funcionalidad porque podría fomentar patrones de diseño que afecten negativamente la capacidad de respuesta de las aplicaciones. La aplicación ideal en su mente es una realmente asíncrona, por lo que la resolución debería ser un caso extremo.
zakdances
11

Retrasar la visualización de la ruta seguramente conducirá a un enredo asíncrono ... ¿por qué no simplemente rastrear el estado de carga de su entidad principal y usarlo en la vista? Por ejemplo, en su controlador, puede usar las devoluciones de llamada de éxito y error en ngResource:

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

Luego, en la vista, podrías hacer lo que sea:

<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
fuente
66
Quizás exponer el estado HTTP a la vista no es correcto, ya que tratar con clases CSS y elementos DOM pertenecen al controlador. Probablemente usaría la misma idea pero el estado abstracto fuera en isValid () y isLoaded ().
jpsimons
1
De hecho, esta no es la mejor separación de preocupaciones, además de que se bloqueará si tiene controladores anidados que dependen del objeto específico.
nulo
Esto es bastante inteligente ... en cuanto a exponer los códigos de estado a la vista, puede pegar la lógica http en las propiedades del alcance dentro del controlador y luego vincularlas. Además, si realiza múltiples llamadas ajax que suceden en segundo plano, querrá hacerlo de todos modos.
KingOfHypocrites
Esto estaría bien si el problema fuera simplemente retrasar una vista. Pero la resolución se utiliza mejor si necesita retrasar la creación de instancias de un controlador, no solo la vista. (Por ejemplo: si necesita asegurarse de que su JSON esté cargado porque su controlador lo pasa a una directiva antes de que esté conectado). De los documentos: "el enrutador esperará a que se resuelvan todos o se rechace uno antes de que el controlador esté instanciado ".
Dan
8

Trabajé desde el código de Misko anterior y esto es lo que he hecho con él. Esta es una solución más actual ya que $deferse ha cambiado a $timeout. $timeoutSin embargo, la sustitución esperará el período de tiempo de espera (en el código de Misko, 1 segundo), luego devolverá los datos con la esperanza de que se resuelva a tiempo. De esta manera, vuelve lo antes posible.

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

Usando AngularJS 1.1.5

Actualización de la función 'teléfonos' en la respuesta de Justen usando la sintaxis AngularJS 1.1.5 .

Original:

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

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

    return deferred.promise;
}

Actualizado:

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

Mucho más corto gracias al equipo de Angular y sus colaboradores. :)

Esta es también la respuesta de Maximilian Hoffmann. Al parecer, ese compromiso lo hizo en 1.1.5.

OJ Raqueño
fuente
1
Parece que no puedo encontrar nada $promiseen los documentos . Podría haber sido sacado a partir de v2.0 +.
zakdances
Solo está disponible en 1.2
Thomas
5

Puede usar la propiedad de resolución $ routeProvider para retrasar el cambio de ruta hasta que se carguen los datos.

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

Tenga en cuenta que la resolvepropiedad se define en la ruta.

EntitiesCtrlResolvey EntityCtrlResolvees objetos constantes definidos en el mismo archivo que EntitiesCtrly 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
fuente
3

Me gusta la idea de darkporter porque será fácil para un equipo de desarrollo nuevo en AngularJS entender y trabajar de inmediato.

Creé esta adaptación que usa 2 divs, uno para la barra de carga y otro para el contenido real que se muestra después de cargar los datos. El manejo de errores se haría en otro lugar.

Agregue un indicador 'listo' a $ scope:

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

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

Ver también: Documentos de la barra de progreso de Boostrap

reggoodwin
fuente
Se desmorona un poco si está cargando múltiples datos. ¿Cómo sabes si todo está cargado?
toxaq
Las cosas han avanzado desde que agregué esta respuesta en febrero, con mucha más actividad en esta página. Parece que hay un mejor soporte en Angular para resolver este problema ahora que lo que se sugiere aquí. Saludos,
reggoodwin
Llego un poco tarde, pero tratar con múltiples datos no es una gran preocupación. Solo tiene que usar variables separadas (booleanos: isReadyData1, isReadyData2, etc.) para cada solicitud y establecer $ scope.ready = isReadyData1 && isReadyData2 ...; funciona bien para mi
GuillaumeA
1

Me gustaron las respuestas anteriores y aprendí mucho de ellas, pero hay algo que falta en la mayoría de las respuestas anteriores.

Estaba atrapado en un escenario similar donde estaba resolviendo la URL con algunos datos que se obtienen en la primera solicitud del servidor. El problema que enfrenté fue qué pasa si la promesa es rejected.

Estaba usando un proveedor personalizado que solía devolver uno Promiseque fue resuelto por el resolvede$routeProvider en el momento de la fase de configuración.

Lo que quiero enfatizar aquí es el concepto de when que hace algo como esto.

Ve la url en la barra de url y luego el whenbloque respectivo en el controlador llamado y la vista se refiere hasta ahora muy bien.

Digamos que tengo el siguiente código de fase de configuración.

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

En la URL raíz del navegador, se llama al primer bloque de ejecución; de lo contrario, otherwisese llama.

Imaginemos que se llama a un escenario en el que golpeé rootUrl en la AuthServicePrivider.auth()función de barra de direcciones .

Digamos que la promesa devuelta está en estado de rechazo , ¿entonces qué?

Nada se representa en absoluto.

Otherwise el bloque no se ejecutará como lo es para cualquier url que no esté definida en el bloque de configuración y que sea desconocida para la fase de configuración de angularJs.

Tendremos que manejar el evento que se dispara cuando esta promesa no se resuelve. En caso de fallo $routeChangeErorrse dispara $rootScope.

Se puede capturar como se muestra en el código a continuación.

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

En general, es una buena idea poner el código de seguimiento de eventos en el bloque de ejecución de la aplicación. Este código se ejecuta justo después de la fase de configuración de la aplicación.

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

De esta manera podemos manejar el fracaso de la promesa en el momento de la fase de configuración.

Ashish Singh
fuente
0

He tenido una interfaz de panel deslizante multinivel compleja, con capa de pantalla deshabilitada. Crear una directiva sobre la capa de pantalla de desactivación que crearía un evento de clic para ejecutar el estado como

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

estaban produciendo un efecto de parpadeo. history.back () en lugar de que funcionó bien, sin embargo, no siempre está de vuelta en la historia en mi caso. Entonces, lo que descubrí es que si simplemente creo el atributo href en mi pantalla de desactivación en lugar de state.go, funcionaría de maravilla.

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

Directiva 'atrás'

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

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

} ]);

app.js acabo de guardar el 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
fuente
-2

Una posible solución podría ser usar la directiva ng-cloak con el elemento donde estamos usando los modelos, por ejemplo

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

Creo que este requiere menos esfuerzo.

Devendra Singh
fuente