I developed a specialized directive that swaps out the content within an element with a loading icon until the value of a specific attribute is no longer undefined. Here is the code for the directive:
.directive('ngLoading', function (Session, $compile) {
var loadingSpinner = '<div class="spinner">' +
'<div class="rect1"></div>' +
'<div class="rect2"></div>' +
'<div class="rect3"></div>' +
'<div class="rect4"></div>' +
'<div class="rect5"></div></div>';
return {
restrict: 'A',
link: function (scope, element, attrs) {
var originalContent = element.html();
element.html(loadingSpinner);
scope.$watch(attrs.ngLoading, function (val) {
if(val) {
element.html(originalContent);
$compile(element.contents())(scope);
} else {
element.html(loadingSpinner);
}
});
}
};
});
I utilize this directive in my view like this:
<div ng-loading="user">
{{user.name}}
</div>
The content inside this div is substituted by a loading icon until the user variable in the scope holds some data. At that point, the initial content is restored and compiled using $compile.
Although this setup works well in most instances, it fails when there is an ng-repeat directive present in the original content. For example, the following scenario does not work:
<div ng-loading="users">
<table>
<thead>
<tr>
<th>Name</th>
<th>Age</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="user in users">
<td>{{user.name}}</td>
<td>{{user.age}}</td>
</tr>
</tbody>
</table>
</div>
In this case, the table is rendered but none of the rows inside tbody are displayed as if the users variable was null. Even though I can see that the users variable indeed contains an array of users right before the $compile call. I attempted wrapping the $watch code within my directive with an $apply call, leading to an error stating "$apply already in progress."
It's important to note that the controller overseeing the div has a property named users.
What could be causing this issue?
UPDATE
I made changes to my HTML like so:
<div ng-loading="users">
{{users[0]}}
<table>
<thead>
<tr>
<th>Name</th>
<th>Age</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="user in users">
<td>{{user.name}}</td>
<td>{{user.age}}</td>
</tr>
</tbody>
</table>
</div>
Upon completion of the loading process, the div's content is replaced by the toString representation of the first user in the array, containing all the correct details, followed by an empty table. This appears to be a problem specifically related to ng-repeat...