I have a horizontal month calendar that shows the activities for employees per day.
Currently it's using a table with the following hierarchy/flow
1) Controller: Generate an array containing every day for that month based on the select month / year. A $scope.$watchCollection updates the array whenever year or month changes.
As the code explains what I do better:
<table class="table table-bordered table-condensed" ng-controller="PlanningOverviewController">
<tr>
<th><input type="text" ng-model="employeefilter"/></th>
<th colspan="7" ng-repeat="week in weeks">
{{week | weekheader}}
</th>
</tr>
<tr>
<th> </th>
<th ng-repeat="day in days">
{{day.format("ddd")}}
</th>
</tr>
<tr>
<th> </th>
<th ng-repeat="day in days">
{{day.date()}}
</th>
</tr>
<tr ng-repeat="employee in planning | filter:employeefilter">
<td> {{employee.firstName}} {{employee.lastName}}</td>
<td class="calendar-unit" ng-repeat="day in days" ng-init="assignments = getAssignments(day, employee)">
<div class="assignment"
assignment ng-repeat="assignment in assignments"
assignment-description = "{{assignment.description}}"
assignment-period="{{assignment.period}}"
assignment-type="{{assignment.type}}">
</div>
</td>
</tr>
</table>
The problem is that the getAssignments() is "expensive" as it needs to check:
Whenever the list of employee is changed (due to a filter), or the month changes, the generating of the table takes up to 6 seconds when having ~40 employees.
Is there another way to approach this, or to improve the nesting of ng-repeats?
Thanks, can clarify if more info is needed.
EDIT:
I've tried removing some elements from the code to see what causes improvements. When I let assignments return a dummy value, the rendering improves by 2-3 seconds, so some caching there might help it. However, it still takes 3-4 seconds to render the "empty" table that way.
I've then removed logic from the directive itself, effectively making it empty, this still caused the same delay.
However, when removing the directive itself from the html, the table renders almost instantly when changing dates. Do I have to assume that this is a (normal?) limitation to angular? I don't want static data, as the goal is to drag assignments around.
Update 2: I realised that I've been approaching this pretty inefficiently... After looking at the scopes & bindings with batarang + reviewing the code itself, I've noticed that I'm doing way too much "getAssignments()" checks, and that my entire approach is based on "getting assignments for an employee", while it'll probably be better / faster to focus on getting assignments for a day. I'll update the code over the weekend & post an answer if it improves/resolves the issue.
Update 3: After rewriting the way the planning get generated, it now takes 2 seconds to display the table for 50 resources. There are about 1750 directives, (at least 1 event for an employee per day). I assume this is a normal delay seeing the size?
update 4: Code for directive:
angular.module('planning.directives', ["templates.app", "ui.bootstrap", "planning.controllers"])
.directive("assignment", function () {
return {
restrict: "EA",
replace: true,
scope: {
assignmentData: "=",
assignmentCreateMethod: "&"
},
template: '<div class="assignment">{{assignmentData.description}} </div>',
link: function (scope, element, attrs) {
element.addClass(scope.assignmentData.type);
if (scope.assignmentData.period == "am") {
element.css("width", "50%");
element.css("float", "left");
}
if (scope.assignmentData.period == "pm") {
element.css("width", "50%");
element.css("float", "right");
}
element.on("mouseenter",function () {
element.addClass("hover");
}).on("mouseleave", function () {
element.removeClass("hover");
});
element.on("click", function() {
scope.assignmentCreateMethod();
});
}
};
});
Each iteration of ng-repeat creates a new child scope, and that new child scope always gets a new property.
You can add condition using ng-if also and this is effective too. You can apply any condition to your list using ng-if attribute. In below example, I have put condition where Age > 20 and IsActive is true. ng-repeat will fetch all records which full fill this scenario.
$first and $last It's common when using ng-repeat to add specific behavior to the first or last element of the loop, e.g. special styling around the edges. Instead, ng-repeat already supplies you with two ready boolean properties. $first is true for the first element, and $last is true for the last element.
In this case it sounds like you're rendering a lot more information that would be able to be visible at any one time. So you could:
Rethink the interface with some sort of pagination, so you only render a screen of information at any one time. Or maybe a limit on the number of weeks you can show at any one time.
Create some sort of directive that removes its contents (i.e. via an ngIf
) if its not visible on the screen. Perhaps listening to (a debounced) scroll event on the window, and calling functions from http://verge.airve.com/ to test if any part of it is visible. It might be tricky to sort out the vertical sizes of the elements in this case, so it slightly depends on your use-case.
To clarify, this would have to remove the contents before it gets rendered by Angular, and not just hide the elements.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With