javascript 在页面滚动上延迟加载 Angular 视图和控制器
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/20410447/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Lazy loading Angular views and controllers on page scroll
提问by TikaL13
I have a microsite that is utilizing Laravel and Angular. It's a one page microsite that is responsive and is broken into 5 sections. I would like to lazy load them to cut down on loading all at once.
我有一个使用 Laravel 和 Angular 的微型网站。这是一个响应式的一页微型网站,分为 5 个部分。我想延迟加载它们以减少一次加载。
<body ng-app>
<div id="wrapper">
<section id="intro">1</section>
<section id="Second">2</section>
<section id="Third">3</section>
<section id="Fourth">4</section>
<section id="Fifth">5</section>
</div>
</body>
I'm looking to load 1 & 2 on page load then as you scroll down the page load the other view with a nice fade in and then load its interactive items.
我希望在页面加载时加载 1 和 2,然后当您向下滚动页面时加载另一个具有漂亮淡入的视图,然后加载其交互式项目。
回答by m59
In this case it is probably not necessary (or efficient) to lazy load your controllers, but it can be done.
在这种情况下,延迟加载控制器可能没有必要(或有效),但可以做到。
There are many things to tackle here, so I'm going to handle it in sections.
这里有很多事情要处理,所以我将分部分处理。
Lazy-loading views on scroll (animated).
滚动上的延迟加载视图(动画)。
Markup:
标记:
<div class="container">
<section
ng-repeat="section in loadedSections"
ng-include="section+'.html'"
scroll-load
scroll-load-from="sections"
scroll-load-to="loadedSections"
ng-animate="{enter:'section-animate-enter'}"
></section>
</div>
Animation CSS:
动画 CSS:
.section-animate-enter {
-webkit-transition: 1.5s linear all;
transition: 1.5s linear all;
opacity: 0;
left: 100%;
}
.section-animate-enter.section-animate-enter-active {
opacity: 1;
left: 0;
}
Angular logic:
角度逻辑:
app.controller('myCtrl', function($scope) {
$scope.sections = ['top','mid','bottom']; //html files to load (top.html, etc)
$scope.loadedSections = [$scope.sections[0]]; //loaded html files
});
app.directive('scrollLoad', function($compile) {
return {
restrict: 'A',
link: function(scope, element, attrs) {
var to = scope[attrs.scrollLoadTo]; //$scope.loadedSections
var from = scope[attrs.scrollLoadFrom]; //$scope.sections
$window = angular.element(window);
$window.bind('scroll', function(event) {
var scrollTop = document.documentElement.scrollTop || document.body.scrollTop || 0;
var scrollPos = scrollTop + document.documentElement.clientHeight;
var elemBottom = element[0].offsetTop + element.height();
if (scrollPos >= elemBottom) { //scrolled to bottom of scrollLoad element
$window.unbind(event); //this listener is no longer needed.
if (to.length < from.length) { //if there are still elements to load
//use $apply because we're in the window event context
scope.$apply(to.push(from[to.length])); //add next section
}
}
});
}
};
});
Lazy-loading CONTROLLERS and views on scroll (animated).
延迟加载控制器和滚动视图(动画)。
Markup:
标记:
<div class="container">
<!-- the "lazy" directive will get the controller first, then add ng-include -->
<section
ng-repeat="section in loadedSections"
lazy="section"
scroll-load
scroll-load-from="sections"
scroll-load-to="loadedSections"
ng-animate="{enter:'section-animate-enter'}"
></section>
</div>
Angular Logic:
角度逻辑:
var $appControllerProvider; //see below
var app = angular.module('myApp', []);
app.config(function($controllerProvider) {
$appControllerProvider = $controllerProvider; //cache this so that we can lazy load controllers
});
app.controller('myCtrl', function($scope) {
$scope.sections = ['top','mid','bottom']; //html files to load (top.html, etc)
$scope.loadedSections = [$scope.sections[0]]; //loaded html files
});
app.directive('scrollLoad', function($compile) {
return {
restrict: 'A',
link: function(scope, element, attrs) {
var to = scope[attrs.scrollLoadTo]; //$scope.loadedSections
var from = scope[attrs.scrollLoadFrom]; //$scope.sections
$window = angular.element(window);
$window.bind('scroll', function(event) {
var scrollTop = document.documentElement.scrollTop || document.body.scrollTop || 0;
var scrollPos = scrollTop + document.documentElement.clientHeight;
var elemBottom = element[0].offsetTop + element.height();
if (scrollPos >= elemBottom) { //scrolled to bottom of scrollLoad element
$window.unbind(event); //this listener is no longer needed.
if (to.length < from.length) { //if there are still elements to load
//use $apply because we're in the window event context
scope.$apply(to.push(from[to.length])); //add next section
}
}
});
}
};
});
app.factory('myService', function($http, $q) {
return {
getController: function(fileName) {
return $http.get(fileName+'.js').then(function(response) {
return response.data;
});
}
}
});
app.directive('lazy', function(myService, $compile, $q) {
/* I store the directive in a variable then return it later
* so that I can abstract directive logic into other functions below */
var directiveReturn = {
restrict: 'A',
link: function(scope, element, attrs) {
var loadName = scope.$eval(attrs.lazy);
//this is straightforward - see the "addScript" function for explanation
myService.getController(loadName).then(function(js) {
return addScript(loadName, js, scope);
}).then(function() {
//the controller has been lazy loaded into angular
//now use "ng-include" to lazy load the view.
var ngInc = angular.element('<span></span>')
.attr('ng-include', "'"+loadName+".html'")
.attr('ng-controller', loadName+'Ctrl');
element.append(ngInc);
$compile(ngInc)(scope);
});
} //link
}; //directive return
/*
* This is the magic.
*/
var scriptPromises = {};
function addScript(loadName, js, scope) {
if (!scriptPromises[loadName]) { //if this controller hasn't already been loaded
var deferred = $q.defer();
//cache promise (which caches the controller when resolved)
scriptPromises[loadName] = deferred.promise;
//inject controller into a script tag
var script = document.createElement('script');
script.src = 'data:text/javascript,' + encodeURI(js);
script.onload = function() {
//this is how you lazy load a controller
$appControllerProvider.register(loadName, window[loadName+'Ctrl']);
//now that the controller is registered with angular, resolve the promise
//then, it is safe to add markup that uses this controller with ng-controller
scope.$apply(deferred.resolve());
};
//when this script loads, the controller will be registered and promise is resolved
document.body.appendChild(script);
return deferred.promise;
}
else { //controller already loaded
return scriptPromises[loadName]; //use cached controller
}
}
return directiveReturn;
});