Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/javascript/425.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Javascript 延迟AngularJS路线更改,直到加载模型,以防止闪烁_Javascript_Angularjs_Angularjs Routing - Fatal编程技术网

Javascript 延迟AngularJS路线更改,直到加载模型,以防止闪烁

Javascript 延迟AngularJS路线更改,直到加载模型,以防止闪烁,javascript,angularjs,angularjs-routing,Javascript,Angularjs,Angularjs Routing,我想知道是否有一种方法(类似于Gmail)让AngularJS延迟显示新路线,直到使用各自的服务获取每个模型及其数据之后 例如,如果有一个列出所有项目的ProjectsController,以及显示这些项目的模板project\u index.html,project.query() 在此之前,旧页面仍将继续显示(例如,如果我正在浏览另一个页面,然后决定查看此项目索引)。属性允许延迟路由更改,直到加载数据 首先定义一个带有resolve属性的路由,如下所示 angular.module('pho

我想知道是否有一种方法(类似于Gmail)让AngularJS延迟显示新路线,直到使用各自的服务获取每个模型及其数据之后

例如,如果有一个列出所有项目的
ProjectsController
,以及显示这些项目的模板
project\u index.html
project.query()

在此之前,旧页面仍将继续显示(例如,如果我正在浏览另一个页面,然后决定查看此项目索引)。

属性允许延迟路由更改,直到加载数据

首先定义一个带有
resolve
属性的路由,如下所示

angular.module('phonecat',['phonecatFilters','phonecatServices','phonecatDirectives'])。
配置(['$routeProvider',函数($routeProvider){
$routeProvider。
当(“/phones”{
templateUrl:'partials/phone list.html',
控制器:PhoneListCtrl,
解析:PhoneListCtrl.resolve})。
当('/phones/:phoneId'{
templateUrl:'partials/phone detail.html',
控制器:PhoneDetailCtrl,
resolve:PhoneDetailCtrl.resolve})。
否则({重定向到:'/phones'});
}]);
请注意,
resolve
属性是在route上定义的

函数PhoneListCtrl($scope,phones){
$scope.phones=电话;
$scope.orderProp='age';
}
PhoneListCtrl.resolve={
电话:功能(电话,$q){
//见:https://groups.google.com/forum/?fromgroups=#!topic/angular/DGf7yyD4Oc4
var deferred=$q.deferred();
电话查询(功能(successData){
延迟解析(successData);
},函数(错误数据){
deferred.reject();//您可以选择在此处传递错误数据
});
回报。承诺;
},
延迟:函数($q,$defer){
变量延迟=$q.defer();
$defer(delay.resolve,1000);
返回延迟。承诺;
}
}
请注意,控制器定义包含一个resolve对象,该对象声明了控制器构造函数应该可以使用的内容。这里,
phones
被注入控制器,并在
resolve
属性中定义

resolve.phones
功能负责返回承诺。收集所有承诺,并延迟路由更改,直到所有承诺得到解决

工作演示:
来源:

延迟显示路由肯定会导致异步纠结。。。为什么不简单地跟踪主实体的加载状态并在视图中使用它呢。例如,在控制器中,您可以同时使用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>

加载
真正的东西
...
错误、未找到等可以区分4xx未找到和
5xx服务器错误。

我是根据上面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;
  }
}

下面是一个适用于Angular 1.0.2的最小工作示例

模板:

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

<div ng-view>

</div>

精简版:

由于$http()已经返回了一个承诺(也称为延迟),我们实际上不需要创建自己的承诺。所以我们可以简化MyCtrl。决心:

MyCtrl.resolve = {
    datasets : function($http) {
        return $http({
            method: 'GET', 
            url: 'http://fiddle.jshell.net/'
        });
    }
};
$http()的结果包含数据、状态、标题和配置对象,因此我们需要将MyCtrl的主体更改为:

$scope.datasets = datasets.data;

我喜欢darkporter的想法,因为对于刚加入AngularJS的开发团队来说,理解和工作都很容易

我创建了这个自适应,它使用2个div,一个用于加载条,另一个用于加载数据后显示的实际内容。错误处理将在其他地方进行

将“就绪”标志添加到$scope:

$http({method: 'GET', url: '...'}).
    success(function(data, status, headers, config) {
        $scope.dataForView = data;      
        $scope.ready = true;  // <-- set true after loaded
    })
});
$http({method:'GET',url:'…'})。
成功(函数(数据、状态、标题、配置){
$scope.dataForView=数据;
$scope.ready=true;//

另请参见:

我看到一些人问如何使用angular.controller方法和小型化友好依赖项注入来实现这一点。由于我刚刚完成了这项工作,我觉得有必要回来帮助。以下是我的解决方案(采用原始问题和Misko的答案):

由于此代码源于问题/最流行的答案,因此未经测试,但如果您已经了解如何制作小型化友好的角度代码,则应将其发送到正确的方向。我自己的代码不需要的一部分是“电话”的注入进入“phones”的解析函数,我也没有使用任何“delay”对象

我还推荐这段youtube视频,它对我帮助很大

如果您感兴趣,我决定也粘贴我自己的代码(用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 ....
    ]
,它是版本1.1.5及更高版本的一部分,公开了
$resource
$promise
对象。包含此提交的ngResource版本允许解析如下资源:

$routeProvider

resolve: {
    data: function(Resource) {
        return Resource.get().$promise;
    }
}
app.controller('ResourceCtrl', ['$scope', 'data', function($scope, data) {

    $scope.data = data;

}]);
控制器

resolve: {
    data: function(Resource) {
        return Resource.get().$promise;
    }
}
app.controller('ResourceCtrl', ['$scope', 'data', function($scope, data) {

    $scope.data = data;

}]);
使用AngularJS 1.1.5 使用AngularJS 1.1.5语法更新Justen答案中的“电话”功能

原件:

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团队和贡献者,篇幅更短。:)

这也是Maximilian Hoffmann的答案。很明显,commit将它变成了1.1.5。

这个片段是依赖注入友好的(我甚至将它与结合使用)
phones: function(Phone) {
    return Phone.query().$promise;
}
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;
        }]
      }
    }
  });
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);
      });
    });
  });
});
<div ng-cloak="">
  Value in  myModel is: {{myModel}}
</div>
App.when('/', {
   templateUrl: '/assets/campaigns/index.html',
   controller: 'CampaignListCtr',
   resolve : {
      Auth : function(){
         return AuthServiceProvider.auth('campaign');
      }
   }
})
// Default route
.otherwise({
   redirectTo: '/segments'
});
$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);
});
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);
   });
}]);
$state.go('account.stream.social.view');
<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.run(function($rootScope, $state) {      

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

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


    });
});