Задержка изменения маршрута AngularJS до загрузки модели для предотвращения мерцания

321

Мне интересно, есть ли способ (аналогичный Gmail) для AngularJS отложить показ нового маршрута до тех пор, пока каждая модель и ее данные не будут получены с использованием соответствующих сервисов.

Например, если существует список, в ProjectsControllerкотором перечислены все проекты, и project_index.htmlкоторый является шаблоном, в котором отображаются эти проекты, Project.query()он будет полностью выбран перед отображением новой страницы.

До этого старая страница все еще продолжала бы отображаться (например, если бы я просматривал другую страницу, а затем решил посмотреть этот индекс проекта).

Миско Хевери
источник

Ответы:

374

Свойство разрешения $ routeProvider позволяет отложить изменение маршрута до загрузки данных.

Сначала определите маршрут с таким resolveатрибутом, как этот.

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

обратите внимание, что resolveсвойство определено на маршруте.

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

Обратите внимание, что определение контроллера содержит объект разрешения, который объявляет вещи, которые должны быть доступны для конструктора контроллера. Здесь phonesон вводится в контроллер и определяется в resolveсвойстве.

resolve.phonesФункция отвечает за возврат обещания. Все обещания собраны, и изменение маршрута откладывается до тех пор, пока все обещания не будут решены.

Рабочая демонстрация: http://mhevery.github.com/angular-phonecat/app/#/phones Источник: https://github.com/mhevery/angular-phonecat/commit/ba33d3ec2d01b70eb5d3d531619bf90153496831

Миско Хевери
источник
10
@MiskoHevery - что делать, если ваши контроллеры находятся внутри модуля и определены как строка, а не функция. Как вы можете настроить атрибут разрешения, как вы?
aar0n
53
Как это используется в angular.controller()определениях контроллеров типов? В $routeProviderвещах я думал, что вы должны использовать строковые имена контроллеров.
Бен Леш
6
Любой пример использования angular.controller () и с последней версией AngularJS?
Лоран
22
@blesh, когда вы используете angular.controller(), вы можете присвоить результат этой функции переменной ( var MyCtrl = angular.controller(...)), а затем работать с этим далее ( MyCtrl.loadData = function(){..}). Посмотрите видео на egghead
petrkotek
17
Я все еще хотел бы хороший способ обойтись без размещения вашего контроллера в глобальном. Я не хочу мусорить глобалами повсюду. Вы можете сделать это с константой, но было бы неплохо иметь возможность включить / разрешить функцию разрешения в контроллере, а не где-то еще.
Эрик Хонн
51

Вот минимальный рабочий пример, который работает для Angular 1.0.2

Шаблон:

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

Модернизированная версия:

Так как $ http () уже возвращает обещание (иначе отложенное), нам на самом деле не нужно создавать свое собственное. Таким образом, мы можем упростить MyCtrl. решить:

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

Результат $ http () содержит данные , статус , заголовки и объекты конфигурации , поэтому нам нужно изменить тело MyCtrl на:

$scope.datasets = datasets.data;

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

mb21
источник
Я пытаюсь сделать что-то подобное, но у меня возникают проблемы с введением «наборов данных», поскольку это не определено. Есть предположения?
Роб Бигрейв
Эй, mb21, я думаю, что ты мог бы помочь мне с этим вопросом: stackoverflow.com/questions/14271713/…
winduptoy
Может ли кто-нибудь помочь мне преобразовать этот ответ в формат app.controller ('MyCtrl')? jsfiddle.net/5usya/1 не работает для меня.
user1071182
я получаю ошибку:Unknown provider: datasetsProvider <- datasets
Чови
Вы можете упростить свой ответ, заменив наборы данных следующим:function($http) { return $http({method: 'GET', url: '/someUrl'}) .then( function(data){ return data;}, function(reason){return 'error value';} ); }
Мортеза Турани
32

Я вижу, как некоторые люди спрашивают, как это сделать, используя метод angular.controller с минимизацией дружественных инъекций зависимостей. Так как я только получил эту работу, я чувствовал себя обязанным вернуться и помочь. Вот мое решение (взято из исходного вопроса и ответа Миско):

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

Поскольку этот код получен из вопроса / самого популярного ответа, он не проверен, но он должен направить вас в правильном направлении, если вы уже понимаете, как сделать дружественный угловой код минификацией. Единственная часть, которая не требовала моего собственного кода, - это вставка «Phone» в функцию разрешения для «телефонов», при этом я вообще не использовал объект «задержки».

Я также рекомендую это видео на YouTube http://www.youtube.com/watch?v=P6KITGRQujQ&list=UUKW92i7iQFuNILqQOUOCrFw&index=4&feature=plcp , которое мне немного помогло

Если вас это заинтересует, я решил также вставить свой собственный код (написанный на coffeescript), чтобы вы могли увидеть, как я его заработал.

К вашему сведению, заранее я использую универсальный контроллер, который помогает мне делать CRUD на нескольких моделях:

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
источник
Правильно ли я понимаю из вашего примера и моих неудачных попыток, что теперь невозможно ссылаться на resolveфункцию в контроллере в последних версиях Angular? Так что должно быть объявлено прямо в конфиге, как здесь?
XML
@XMLilley Я уверен, что это так. Я считаю, что этот пример был из 1.1.2, когда я его написал. Я не видел никакой документации по размещению разрешения внутри контроллера
bitwit
2
Хорошо, спасибо. На SO есть много примеров (например, два верхних), но все они относятся к 2012 и началу 2013 года. Это элегантный подход, но, похоже, не рекомендуется. Кажется, что самой чистой альтернативой является написание отдельных сервисов, которые являются обещанными объектами.
XML
Спасибо, это сработало для меня. Для всех, кто получает ошибки о неопределенном $deferсервисе, обратите внимание, что в версии 1.5.7 AngularJS вы хотите использовать $timeoutвместо этого.
racl101
18

Этот коммит , который является частью версии 1.1.5 и выше, предоставляет $promiseобъект $resource. Версии ngResource, включая этот коммит, позволяют разрешать ресурсы следующим образом:

$ routeProvider

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

контроллер

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

    $scope.data = data;

}]);
Максимилиан Хоффманн
источник
Какие версии включают этот коммит, пожалуйста?
XML
Последняя нестабильная версия (1.1.5) включает этот коммит. ajax.googleapis.com/ajax/libs/angularjs/1.1.5/angular.min.js
Максимилиан Хоффманн
Мне нравится этот менее многословный подход. Было бы неплохо создать обещание из фактического объекта данных и передать его напрямую, но это настолько мало кода, что он работает хорошо.
Сэм Барнум
1
Как бы ресурс получил доступ к $ routeParams? Например: в GET '/api/1/apps/:appId'-> App.get({id: $routeParams.appId}).$promise();я не могу использовать, как это
zeronone
2
@zeronone, который вы вводите $routeдля своей решимости и использования $route.current.params. Будьте осторожны, $routeParamsвсе еще указывает на старый маршрут.
Брайс Стейси
16

Этот фрагмент кода удобен для внедрения зависимостей (я даже использую его в сочетании с ngmin и uglify ), и это более элегантное решение на основе домена .

В приведенном ниже примере регистрируется ресурс Phone и константа phoneRoutes , которая содержит всю информацию о маршрутизации для этого (телефонного) домена. Что-то, что мне не понравилось в предоставленном ответе, было расположение логики разрешения - главный модуль не должен ничего знать или беспокоиться о том, как аргументы ресурса передаются контроллеру. Таким образом, логика остается в той же области.

Примечание: если вы используете ngmin (а если нет: вам следует), вам нужно только написать функции разрешения с соглашением о массиве 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;
        }]
      }
    }
  });

Следующим этапом является внедрение данных маршрутизации, когда модуль находится в состоянии конфигурации, и его применение к $ 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: '/' });

});

Тестирование конфигурации маршрута с этой настройкой также довольно просто:

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

Вы можете увидеть это в полной славе в моем последнем (предстоящем) эксперименте . Хотя этот метод отлично работает для меня, мне действительно интересно, почему $ инжектор не задерживает создание чего-либо, когда он обнаруживает внедрение чего-либо, что является объектом обещания ; это сделало бы вещи оооооооооооооо намного проще.

Редактировать: используется Angular v1.2 (RC2)

ноль
источник
2
Этот превосходный ответ кажется гораздо более подходящим для философии «Angular» (инкапсуляция и т. Д.). Мы все должны прилагать сознательные усилия, чтобы не допустить, чтобы логика распространялась по всей базе кода, как кудзу.
zakdances
I really wonder why the $injector isn't delaying construction of anything when it detects injection of anything that is a promise objectЯ предполагаю, что они пропустили эту функцию, потому что она может поощрять шаблоны проектирования, которые негативно влияют на отзывчивость приложений. По их мнению, идеальное приложение - это действительно асинхронное приложение, поэтому его решение должно быть на грани.
zakdances
11

Задержка показа маршрута обязательно приведет к асинхронному путанице ... почему бы просто не отследить состояние загрузки вашего основного объекта и использовать его в представлении. Например, в вашем контроллере вы можете использовать как коллбэки успеха, так и ошибки в ngResource:

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

Тогда в представлении вы можете сделать все что угодно:

<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
источник
6
Возможно, предоставление статуса HTTP представлению не правильно, больше, чем работа с классами CSS и элементами DOM, принадлежащими контроллеру. Я бы, вероятно, использовал ту же идею, но не использовал абстрактный статус в isValid () и isLoaded ().
jpsimons
1
Это действительно не лучшее разделение проблем, плюс то, что он потерпит крах, если у вас есть вложенные контроллеры, которые зависят от конкретного объекта.
ноль
Это довольно умно ... что касается представления кодов состояния представлению, вы можете просто вставить логику http в свойства области в контроллере, а затем привязать их. Также, если вы делаете несколько вызовов ajax, которые происходят в фоновом режиме, вы все равно захотите это сделать.
KingOfHypocrites
Это было бы хорошо, если бы проблема заключалась в простой задержке просмотра. Но разрешение лучше всего использовать, если вам нужно отложить создание экземпляра контроллера, а не только представление. (Например: если вам нужно убедиться, что ваш JSON загружен, потому что ваш контроллер передает его в директиву, прежде чем он будет подключен.) Из документов: «маршрутизатор будет ждать, пока все они будут разрешены или один будет отклонен, прежде чем контроллер будет экземпляр ".
Дан
8

Я работал с кодом Миско выше, и это то, что я сделал с ним. Это более актуальное решение, поскольку $deferоно было изменено на $timeout. Подстановка, $timeoutоднако, будет ожидать период ожидания (в коде Миско, 1 секунда), а затем вернет данные, надеясь, что они разрешены вовремя. Таким образом, он возвращается как можно скорее.

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
источник
7

Использование AngularJS 1.1.5

Обновление функции «телефоны» в ответе Джустена с использованием синтаксиса AngularJS 1.1.5 .

Оригинал:

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

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

    return deferred.promise;
}

Обновлено:

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

Гораздо короче благодаря команде Angular и авторам. :)

Это также ответ Максимилиана Гофмана. Видимо, этот коммит сделал это в 1.1.5.

OJ Ракеньо
источник
1
Я не могу найти что-нибудь о $promiseв документах . Это могло быть удалено с v2.0 +.
zakdances
Это доступно только в 1.2
Томас
5

Можно использовать свойство разрешения $ routeProvider, чтобы отложить изменение маршрута до загрузки данных.

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

Обратите внимание, что resolveсвойство определено на маршруте.

EntitiesCtrlResolveи EntityCtrlResolveэто постоянные объекты , определенные в том же файле , как EntitiesCtrlи EntityCtrlконтроллеры.

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

Мне нравится идея darkporter, потому что команде разработчиков, новичкам в AngularJS, будет легко понять и сразу начать работать.

Я создал эту адаптацию, которая использует 2 div, один для панели загрузчика, а другой для фактического контента, отображаемого после загрузки данных. Обработка ошибок будет сделана в другом месте.

Добавьте флаг 'ready' в $ scope:

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

В виде 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>

Смотрите также: документы с индикатором выполнения Boostrap

reggoodwin
источник
Немного развалится, если вы загружаете несколько частей данных. Как узнать, все ли загружено?
Toxaq
С тех пор, как я добавил этот ответ в феврале, дела пошли еще дальше, и на этой странице было гораздо больше активности. Похоже, что в Angular лучшая поддержка для решения этой проблемы сейчас, чем то, что предлагается здесь. Приветствия,
Редгудвин
Я прибыл немного поздно, но работа с несколькими частями данных не представляет большой проблемы. Вам просто нужно использовать отдельные переменные (booleans: isReadyData1, isReadyData2 и т. Д.) Для каждого запроса и установить $ scope.ready = isReadyData1 && isReadyData2 ...; хорошо работает для меня
GuillaumeA
1

Мне понравились приведенные выше ответы и я многому научился у них, но в большинстве приведенных выше ответов чего-то не хватает.

Я застрял в похожем сценарии, где я решал URL с некоторыми данными, которые выбираются в первом запросе с сервера. Проблема, с которой я столкнулся, заключалась в том, что если обещание будет rejected.

Я использую пользовательский поставщик , который используется для возврата , Promiseкоторый разрешился resolveв $routeProviderто время фазы конфигурации.

Здесь я хочу подчеркнуть, что концепция whenделает что-то вроде этого.

Он видит URL-адрес в строке URL-адреса, а затем соответствующий whenблок в вызываемом контроллере, и просмотр до сих пор считается хорошим.

Допустим, у меня есть следующий код фазы конфигурации.

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

По корневому URL в браузере вызывается первый блок запуска, иначе otherwiseвызывается.

Давайте представим сценарий, который я нажал на rootUrl в AuthServicePrivider.auth()функции адресной строки .

Допустим, обещание вернулось в состоянии отказа, что тогда ???

Ничего не делается вообще.

Otherwise блок не будет выполняться так, как это делается для любого URL, который не определен в блоке конфигурации и неизвестен на этапе конфигурации angularJs.

Нам придется обработать событие, которое запускается, когда это обещание не выполнено. При неудаче $routeChangeErorrувольняют $rootScope.

Это может быть зафиксировано, как показано в коде ниже.

$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 Как правило, рекомендуется помещать код отслеживания событий в блок запуска приложения. Этот код запускается сразу после этапа настройки приложения.

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

Таким образом, мы можем справиться с ошибкой обещания во время фазы конфигурации.

Ашиш Сингх
источник
0

У меня был сложный многоуровневый интерфейс скользящей панели с отключенным слоем экрана. Создание директивы по отключению слоя экрана, которая создала бы событие щелчка, чтобы выполнить состояние как

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

производили потрясающий эффект. history.back () вместо этого работала нормально, однако в моем случае это не всегда возвращается в историю. Итак, я обнаружил, что если я просто создаю атрибут href на своем экране отключения вместо state.go, это работает как чудо.

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

Директива «назад»

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

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

} ]);

app.js я просто сохраняю предыдущее состояние

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

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

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


    });
});
Дима
источник
-2

Одним из возможных решений может быть использование директивы ng-cloak с элементом, в котором мы используем модели, например

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

Я думаю, что это требует минимум усилий.

Девендра Сингх
источник