Reputation: 1155
wanna know the best way to improve performance in my scenario a list of 750 elements filtered in real time and with the requirement of no pagination or limit the list, the 750 elements should be on screen and need to be filtered as fast as posible in real time with a input text, obviously the performance using
ng-repeat="x in collection | filter: x"
is not quite good here, any ideas?
EDIT: Current Scenario
<section ng-repeat="(key, value) in content">
<md-subheader class="md-primary">{{ key == '1' ? '#' : key }}</md-subheader>
<md-list layout-padding>
<md-list-item class="md-3-line" ng-repeat="person in value" layout="row">
<div flex="15" flex-md="10" layout="column" layout-align="center center">
<span>{{ ::person.id_lista_nominal }}</span>
</div>
<div class="md-list-item-text" flex>
<h3>{{ ::person.nombre_completo }}</h3>
<h4>{{ ::person.ine }}</h4>
</div>
<div flex="15" flex-md="10" layout="column" layout-align="center center">
<md-button aria-label="Votó" class="md-primary md-raised text-white" ng-click="confirm_voting(person, $event)"><md-icon md-font-icon="mdi-navigation-check icon-md"></md-icon> </md-button>
</div>
</md-list-item>
</md-list>
</section>
Now all the filtering is in the controller for better performance, in this function
var update_list_content = function () {
//filter implementation in controller
var tmp = angular.copy(original);
tmp = $filter('filterBy')(tmp, ['nombre_completo', 'nombre_inverso'], $scope.param.search);
tmp = $filter('orderBy')(tmp, 'nombre_completo');
tmp = $filter('limitTo')(tmp, $scope.config.limit);
tmp = $filter('groupBy')(tmp, 'grupo');
$scope.content = tmp;
};
As it was correctly pointet out I havent been very clear with my goal here, so here it is:
Im developing a mobile app using cordova, all of this runs fine and fluid on pc but it feels laggy on mobile
in this I have a list like a contact list, the first ng-repeat reorder the list and split the list into groups (the sub-header isthe group name)
the nested ng-repeat is used to display the list of persons inside each group
the procees of this screen is quite simple and because of that a laggy interface is not acceptable
the user only searches in the list by scrolling it or by filtering by person name, the button on every person will ask if the user wats to "mark" the contact to proccess, if the user agrees the process will execute a simple websql command to set mark = 1 to the selected person by its id and it will remove that person from the list.
but the filtering with the onscreen 750 persons in the list feels buggy and every time the user marks a person, it takes like 3 or 4 seconds to update the list and show the removal of the marked
the desired experience is a fluid scroll, a fast search and an inmediate removal after mark, I know if I limit the number of persons onscreen (lets say to 50) I gain a lot of performance in the proccess but sadly for me thats not an option unless perhaps manage a way to simulate they can scroll up and down but limiting the amount of displayed data :S
Upvotes: 0
Views: 2136
Reputation: 1690
You don't have to limit the numbers of elements itself, but you can load them on scroll event if you detect it.
For exemple here I will admit that you use a common mysql database where all your data is stored, I use two variables to do this and get all my users commentary, it seems like that (read the comments):
//Function to get all comments progressively
$scope.getAllCommentsByUserId = function(){
//action name, here actionVar's value will be my php case, if
//$_POST['action'] equal this value then i will send my sql request
var actionVar = "getAllCommentsByUserId";
//Define callback function
var callback = function(resp){
if(resp.success == 1){
// $scope.userComments will be my array were the sql results
// from resp will be stored, also it's the main varaible of
// my ng-repeat loop, i push them because i don't want
// to erase the comments i already requested, so the new ones
// will be at the end of the array
angular.forEach(resp.comments, function(comment, key) {
$scope.userComments.push(comment);
});
// In case i got an error istead of a good result
}else{
$scope.$emit('showAlert', ["Une erreur est survenue","error"]);
}
};
// In case you want to know, userId is not needed, i use it just to
// request all the comments on the current user's page. that's just logic.
var params = {
'action' : actionVar,
'idUser' : $scope.bigouder.idUser,
'begin' : $scope.beginComments,
'length' : $scope.lengthComments
};
//HTTP REQUEST
$http.post(url, params)
.success(callback)
.error(function(){
$scope.$emit('showAlert', ["ERROR","error"]);
});
};
// I update the begining element index by adding the length to it
// YOU HAVE TO SET "beginComments" AND "lengthComments" AT THE BEGINING OF
// YOUR JS FILE, to give them a default value, otherwise that will never work.
$scope.changeMaxComments = function(){
$scope.beginComments += $scope.lengthComments;
$scope.lengthComments = 20;
$scope.getAllCommentsByUserId();
};
Basically what i do here is that i set 2 values:
beginComments will be the element from where my sql request will start saving the finded results, lengthComments will be the number of results i will save.
The sql request will be something like that: $query = "select ... from ... where ... order by ... limit". $_POST['begin'] . "," $_POST['length'];
so i use my two variable with the limit parameter in my sql request, i will take all the results from the "beginComments" element to the "beginComments + lengthComments" element.
You just have to call something like that when you detect a scroll event, angular will do the job and refresh your ng-repeat. :)
Upvotes: 1
Reputation: 1155
I think i found the solution to my own problem googling various types of workarounds i found this
An awesome directive that manage the concept of virtual scroll directly on ng-repeat loops of angular, that way the view only renders a few items while its scrolling it loads the rest but unlike lazy loading the user dont see the scroll bar incresing while scrolling down cause it alreade have the space calculated and it removes elements you pass scroll outside the view port so, you'll always have the 'illusion' of having the full set of items only rendering a few
Upvotes: 0
Reputation: 32635
(key, value) in filteredContent()
Otherwise I'd google lowering watch counts and writing effective filters for AngularJS.
Upvotes: 0
Reputation: 1885
You don't explain your requirements... AngularJS can be very heavy for large amounts of data, but 750 simple items shouldn't be a problem. You have several alternatives:
Use ReactJS (here a guide) for faster rendering, just delegate that view to reactJS.
Change your filter input to have a delay so it doesn't change the model
on every key pressed. First instead of listen for param.search
on your filter, listen to other variable searchText
.
For example:
$scope.$watch('param.search', function (newValue) {
if (searchTextTimeout) {
$timeout.cancel(searchTextTimeout);
}
searchTextTimeout= $timeout(function() {
$scope.searchText= newValue;
}, 300); // between 200-300 ms should be a good user experience
});
You can also apply React and delay the filter. If still your performance is poor, probably the issue is in your styles, a single style could causes a lot of repaints, for instance if you are animating the position
change when filter, that would be very slowly to animate.
I hope this works for you.
Update
Also i noticed that you are using this directive md-subheader which only seems to add a #1 or key what you need to show? if is the numeration you can use $index instead
Upvotes: 0
Reputation: 144
Limit the amount of elements that show initially then show more elements as the user scrolls. Like tweeter infinite scrolling (When a user gets to the bottom of the page it loads more tweets)
Upvotes: 0