[javascript] 깜박임을 방지하기 위해 모델이로드 될 때까지 AngularJS 경로 변경 지연

AngularJS가 각 모델과 해당 서비스를 사용하여 데이터를 가져 오기 전까지 새로운 경로 표시지연시킬 수 있는 방법이 있는지 궁금합니다 (Gmail과 유사) .

예를 들어, ProjectsController모든 프로젝트를 나열하고 project_index.html이러한 프로젝트를 표시 한 템플릿 인 파일 Project.query()이있는 경우 새 페이지를 표시하기 전에 완전히 가져옵니다.

그때까지 이전 페이지는 계속 표시됩니다 (예를 들어, 다른 페이지를 탐색하고이 프로젝트 색인을보기로 결정한 경우).



답변

$ routeProvider resolve 속성은 데이터가로드 될 때까지 경로 변경 지연을 허용합니다.

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

컨트롤러 정의에는 컨트롤러 생성자가 사용할 수있는 것을 선언하는 resolve 객체가 포함되어 있습니다. 여기서는 phones컨트롤러에 주입되며 resolve속성에 정의됩니다 .

resolve.phones기능은 약속을 반환합니다. 모든 약속이 수집되고 모든 약속이 해결 될 때까지 경로 변경이 지연됩니다.

실무 데모 : http://mhevery.github.com/angular-phonecat/app/#/phones
출처 : https://github.com/mhevery/angular-phonecat/commit/ba33d3ec2d01b70eb5d3d531619bf90153496831


답변

다음은 Angular 1.0.2에서 작동하는 최소 작업 예입니다.

주형:

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

<div ng-view>

</div>

자바 스크립트 :

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 ()의 결과에는 data , status , headersconfig 객체가 포함되므로 MyCtrl 본문을 다음과 같이 변경해야합니다.

$scope.datasets = datasets.data;

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


답변

최소화하기 쉬운 의존성 주입과 함께 angular.controller 메서드를 사용 하여이 작업을 수행하는 방법을 묻는 사람들이 있습니다. 방금이 일을 마치고 돌아와서 도와 줘야한다고 느꼈습니다. 내 해결책은 다음과 같습니다 (원래 질문과 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';
}]);

이 코드는 질문 / 가장 인기있는 답변에서 파생되었으므로 테스트되지 않았지만 최소화하기 쉬운 각도 코드를 만드는 방법을 이미 이해했다면 올바른 방향으로 보내야합니다. 내 코드에 필요하지 않은 부분은 ‘전화’의 해결 기능에 “전화”를 주입하거나 ‘지연’객체를 전혀 사용하지 않았습니다.

또한이 유튜브 비디오 ( http://www.youtube.com/watch?v=P6KITGRQujQ&list=UUKW92i7iQFuNILqQOUOCrFw&index=4&feature=plcp)를 추천합니다 )를

관심이 있으시면 내 코드 (커피 스크립트로 작성)를 붙여서 어떻게 작동하는지 확인할 수 있습니다.

참고로, 미리 여러 ​​모델에서 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 ....
    ]


답변

버전 1.1.5 이상의 일부인 이 commit$promise객체를 노출합니다.$resource . 이 커밋을 포함한 ngResource 버전은 다음과 같은 리소스를 확인할 수 있습니다.

$ routeProvider

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

제어 장치

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

    $scope.data = data;

}]);


답변

이 스 니펫은 의존성 주입 친화적이며 ( ngminuglify의 조합으로도 사용 가능 )보다 우아한 도메인 기반 솔루션입니다.

아래 예제는 Phone 자원상수 phoneRoutes를 등록합니다 . 여기에는 해당 (전화) 도메인에 대한 모든 라우팅 정보가 포함됩니다. 제공된 답변에서 마음에 들지 않는 부분은 리졸 브 로직 의 위치였습니다. 메인 모듈은 컨트롤러에 리소스 인수가 제공되는 방식에 대해 아무것도 알지 못하거나 방해 해서는 안됩니다 . 이런 식으로 논리는 동일한 도메인에 유지됩니다.

참고 : ngmin을 사용 하고 있고 사용 하지 않는 경우 DI 배열 규칙을 사용하여 resolve 함수 만 작성하면됩니다.

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

내 최신 (다가오는) 실험 에서 그것을 완전히 볼 수 있습니다 . 이 방법은 나를 위해 잘 작동하지만, 난 정말 $ 인젝터의 건설 지연되지 않는 이유 궁금 아무것도 가 주입 감지 할 때 어떤 A는 약속 개체; 그것은 soooOOOOOooOOOOOOO를 훨씬 쉽게 만들 것입니다.

편집 : 사용 Angular v1.2 (rc2)


답변

경로 표시를 지연 시키면 비동기 엉킴이 발생할 수 있습니다. 주요 엔티티의로드 상태를 추적하고 뷰에서이를 사용하는 것이 어떻습니까? 예를 들어 컨트롤러에서 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>


답변

위의 Misko 코드에서 근무했으며 이것이 내가 한 일입니다. 이 (가) $defer로 변경된 이후의 최신 솔루션 $timeout입니다. $timeout그러나 대체 하면 시간 초과 기간 (Misko의 코드에서 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;
  }
}