Reputation: 577
To demonstrate the problem, I'll explain what I'm trying to achieve. I'm creating a booking system using AngularUI Calendar, and the problem is that the calendar's performance starts to drop very quickly and noticeably when the number of the events increase (100+ events, and the calendar will take minutes to bind data). Due to that I'm trying to fetch data from back-end when view changes by sending AJAX request every time the user change the view (week, month, day, prev, next... etc.) This is when I start to have problems.
As it's been suggested on AngularUI Calendar website that I can use this function: $scope.eventsF = function (start, end, timezone, callback) {...}
and add it to the $scope.eventSources
and it will be called on every view switch. I added the $scope.eventsF
to my $scope.eventSources
but it's never updated.
Here is the sample (fetching all data, and it's working):
$scope.mainEvents = []; //To initiate the event source
Event.getEvents().then(function (events) { // Event is a factory
if (events) { //Checking if I have events
// If I try: $scope.mainEvents = events; the $scope.eventSources won't update
angular.forEach(events, function(value) {
this.push(value);
}, $scope.mainEvents);
} else {
// Tell the user no events to render...
}
}, function (error) {
console.log(error);
});
$scope.eventSources = [$scope.mainEvents, $scope.draftEvents];
Now I changed Event.getEvents()
to take two parameters as start date and end date of the view, send them to the back-end, and retrieve data as json again (worked fine, I consoled that out).
Here is the code (I tried two ways): Events are rendering, but $scope.eventSources
is not updating in both of them.
First:
$scope.mainEvents = function (start, end, timezone, callback) {
var s = start.format('YYYY-MM-DD'),
e = end.format('YYYY-MM-DD');
Event.getEvents(s, e).then(function (events) {
$scope.mainEvents = []; //even tried $scope.eventSources.mainEvents
if (events) {
angular.forEach(events, function (value) {
this.push(value);
}, $scope.mainEvents); //event tried $scope.eventSources.mainEvents
callback($scope.mainEvents);
}
}, function (error) {
console.log(error);
});
};
$scope.eventSources = [$scope.mainEvents, $scope.draftEvents];
Second:
$scope.eventsF = function (start, end, timezone, callback) {
var s = start.format('YYYY-MM-DD'),
e = end.format('YYYY-MM-DD');
Event.getEvents(s, e).then(function (events) {
$scope.mainEvents = [];
if (events) {
angular.forEach(events, function (value) {
this.push(value);
}, $scope.mainEvents);
callback($scope.mainEvents);
}
}, function (error) {
console.log(error);
});
};
$scope.eventSources = [$scope.mainEvents, $scope.draftEvents, $scope.eventsF];
I hope this is clear enough.
Upvotes: 8
Views: 1260
Reputation: 577
I found a solution to the problem. I didn't quite get why I have to do it this way, but I managed to make it work. I had remove the events from the $scope.mainEvents
array, one by one (I think in the AngularUI Calendar, they are using $watch
for every single event), then add the new events from promise to the $scope.mainEvents
also one by one. DO NOT use the callback()
function, as the $scope
will be updated ($watch
will take care of the rendering), so the callback()
will render the event again so you will end up with each event rendered twice.
Here is the final code:
$scope.mainEvents = [];
$scope.eventsF = function (start, end, timezone, callback) {
//moment.js objects
var s = start.format('YYYY-MM-DD'),
e = end.format('YYYY-MM-DD');
Event.getEvents(s, e).then(function (events) {
//emptying the array
$scope.mainEvents.splice(0, $scope.mainEvents.length);
//add the retrieved events
angular.forEach(events, function (value) {
this.push(value);
}, $scope.mainEvents);
}, function (error) {
//promise error logic
console.log(error);
});
};
Upvotes: 1
Reputation: 497
I'm guessing it is caused by Javascript's variable scope. You were referencing to $scope.eventSources
in different scope than $scope.eventSources
in you controller. Perhaps $scope.eventSources
get copied somewhere in AngularUI Calendar.
You could access $scope.eventSources
in your controller by simply create a variable that holds a reference to the variable.
// Create a variable that holds reference to controller's $scope
var ctrlScope = $scope;
$scope.mainEvents = [];
$scope.draftEvents = [];
$scope.eventsF = function (start, end, timezone, callback) {
var s = start.format('YYYY-MM-DD'),
e = end.format('YYYY-MM-DD');
Event.getEvents(s, e).then(function (events) {
if (events) {
angular.forEach(events, function (value) {
this.push(value);
}, $scope.mainEvents);
// Test
console.log(ctrlScope.eventSources);
callback($scope.mainEvents);
}
}, function (error) {
console.log(error);
});
};
$scope.eventSources = [$scope.mainEvents, $scope.draftEvents, $scope.eventsF];
Upvotes: 1
Reputation: 497
The simplest way I can think of is by re-assigning event sources to $scope.eventSources
at the end of $scope.eventsF
. Code below is based on your second option. Cheers.
$scope.mainEvents = [];
$scope.draftEvents = [];
$scope.eventsF = function (start, end, timezone, callback) {
var s = start.format('YYYY-MM-DD'),
e = end.format('YYYY-MM-DD');
Event.getEvents(s, e).then(function (events) {
$scope.mainEvents = [];
if (events) {
angular.forEach(events, function (value) {
this.push(value);
}, $scope.mainEvents);
// Re-assign event sources
$scope.eventSources = [$scope.mainEvents, $scope.draftEvents, $scope.eventsF];
// Assume that 'events' is an array with 5 elements.
// console.log($scope.eventSources) will produce :
// Array [ Array[5], Array[0], app</$scope.eventsF() ]
callback($scope.mainEvents);
}
}, function (error) {
console.log(error);
});
};
$scope.eventSources = [$scope.mainEvents, $scope.draftEvents, $scope.eventsF];
// console.log($scope.eventSources) will produce :
// Array [ Array[0], Array[0], app</$scope.eventsF() ]
I've updated the code above to show at which part I tested the $scope.eventSources
. Can you please show at which part of the sample code you would like the $scope.eventSources
get updated ?
Upvotes: 1