Reputation: 115
I'm writing an event calendar in AngularUI with some filtering.
<div
id="calendar"
ui-calendar="{ height: 450, editable: false, defaultView: 'month' }"
class="span9 pull-right calendar"
ng-model="events"
</div>
When the filter dropdowns are changed, a function is fired, which should do some voodoo and update $scope.events.
(Shortened)
// Clear $scope.events
$scope.events = [];
// Initialise new events
var new_events = all_events;
if($scope.events_filter.type != 'all') {
// Do filter
new_events = json_service.filter(
new_events,
'type',
$scope.events_filter.type
);
}
// Update $scope.events
$scope.events = new_events;
Let's start with 6 events. If there are two 'types,' with - say - 3 events each, and lets call one type A and one type B, you can change from All to A, and three events will be correctly shown. If you change back to 'all,' there will be 6 visible.
If you change from A to B, the calendar does not update, however, examining $scope.events DOES show the data has changed.
The filtering works, and $scope.events is being updated.
The problem seems to be that if $scope.events length doesn't change - it doesn't see fit to redraw the calendar.
Upvotes: 0
Views: 1794
Reputation: 1033
You are correct. If the length of the events array does not change the calendar will not update. This is because angular will not detect changes in the whole array object,(without throwing a digest error) rather it needs to watch a more focused variable set or watch a function that returns one event source to work properly.
Two edits solved this specific problem. Aadding getOptions() to the watch method value, and either watching the first event source or watching all event sources and adding an equalsTracker to the directive's attributes.
Here is a link to a calendar that switches out 2 arrays of events from a filter service. (this does not solve your issue however) http://plnkr.co/edit/VbYDNK?p=preview
The calendar is now watching the first event array in eventSources. So now any events that you want to control with angular on the scope can be added to the first array, and then if you need to pull in any sources you can call fullcalendar's addSource method at any given time or any other method of your choice.
This is a much more open ended calendar that creates many angles of attack.
Here is a link to a calendar that is watching the first array itself and not its length. This works as well, so maybe we should go with this one. (this solves your problem) http://plnkr.co/edit/AU6KNZ?p=preview
I have read that it can cause performance issues to watch large arrays.
Edit: I have went back to the drawing board and found a way to solve all of these problems in one. Now we no longer need to watch the first array in event sources, rather we can just watch a tracker variable. The tracker variable will watch the length of all events in eventSources plus the length of eventSources itself. This will allow for any array to be watched inside of eventSources and still let angular to do its magic.
Because of this specific use case an equalsTracker attribute has been added to the calendar. This equalsTracker attr must be a number and should be updated when a filter service is ran and the resulting array of events has the same length as the current scope.events array being filtered.
Upvotes: 3
Reputation: 115
I found a solution! With joshkurz help, I realised that it was only checking for the length changing.
Simple solution:-
Before filtering check length of $scope.events.
After filtering check length of $scope.events.
var length = $scope.events.length;
$scope.events = [];
var new_events = all_events;
... filter new_events a little bit ...
$scope.events = new_events;
if($scope.events.length == length) {
$scope.events.push({});
}
The length isn't different any more ;)
Thanks josh :)
Upvotes: 0